- Joined
- Mar 19, 2008
- Messages
- 3,141
Advanced dialog & button wrapper. Let "advanced" worry you not. Instead of providing simple wrapper around functions we already got for dialogs (few) and buttons (none), this library adds additional features which hasn't been included in any previous snippet of such subject before.
Not only this does include commonly needed "page" feature (connecting dialogs), it also provides insert and remove methods on the top of well used append.
Note: dialogs & buttons are pesky handles. The re-set message bug is well known. Due nature of such handles, I don't recommend destroying Dialog instance. Rather hide it.
Another worth noting bug, is fact that after using TriggerRegisterDialogButtonEvent, the GetClickedButton() native, will return null. Mentioned function requires specific button as argument and, blizz programmers assumed that retrival of actuall handle won't be needed in such case. Here, you have that opportunity, handle will be retrieved via trigger id.
Library is designed mainly for multiplayer games. Of course, each of its features will work correctly in SP, however, the idea was to provide not just features, but also let those features be dynamic. In sp game is paused each time dialog is shown.
Dynamic - append/insert/remove methods will update dialog in real time, no matter its visibility status. Clear() won't re-open dialog since showing object without data is rather useless.
Theory behind:
Let's assume you play RTS game (example: Azeroth Wars) and your Kingdom (example: IronForge) falls. After that you get a choice via dialog: get aerial peak or go northrend. Unfortunately you need to take a piss. Doom! You come back and aerie is drowned with undeads. This option is rather pointless now, since our "new kingdom" got destroyed before we actually got to play it. We should be left with one option less.
Hope, you guys get the point
Notice how easy it is to use and modify dialogs in demo code below. That example also shows how to connect dialogs so creation of several pages is piece of cake.
Script:
Demo code:
Not only this does include commonly needed "page" feature (connecting dialogs), it also provides insert and remove methods on the top of well used append.
Note: dialogs & buttons are pesky handles. The re-set message bug is well known. Due nature of such handles, I don't recommend destroying Dialog instance. Rather hide it.
Another worth noting bug, is fact that after using TriggerRegisterDialogButtonEvent, the GetClickedButton() native, will return null. Mentioned function requires specific button as argument and, blizz programmers assumed that retrival of actuall handle won't be needed in such case. Here, you have that opportunity, handle will be retrieved via trigger id.
Library is designed mainly for multiplayer games. Of course, each of its features will work correctly in SP, however, the idea was to provide not just features, but also let those features be dynamic. In sp game is paused each time dialog is shown.
Dynamic - append/insert/remove methods will update dialog in real time, no matter its visibility status. Clear() won't re-open dialog since showing object without data is rather useless.
Theory behind:
Let's assume you play RTS game (example: Azeroth Wars) and your Kingdom (example: IronForge) falls. After that you get a choice via dialog: get aerial peak or go northrend. Unfortunately you need to take a piss. Doom! You come back and aerie is drowned with undeads. This option is rather pointless now, since our "new kingdom" got destroyed before we actually got to play it. We should be left with one option less.
Hope, you guys get the point
Notice how easy it is to use and modify dialogs in demo code below. That example also shows how to connect dialogs so creation of several pages is piece of cake.
Script:
JASS:
/*****************************************************************************
*
* DialogButton v1.1.0.3
* by Bannar aka Spinnaker
*
* Allows one to handle dialog operations with ease.
* Defines struct type wrappers for both, dialog and button handles.
*
******************************************************************************
*
* Requirements:
*
* VectorT by Bannar
* hiveworkshop.com/forums/submissions-414/containers-vector-t-248942/
*
* Alloc - choose whatever you like
*
******************************************************************************
*
* Functions:
*
* function GetEventDialog takes nothing returns Dialog
* returns Dialog instance which has been clicked
*
* function GetEventButton takes nothing returns Button
* returns Button instance which has been clicked
*
* function GetDialogEventPlayer takes nothing returns player
* returns event player who actually clicked dialog or button
*
* function RegisterDialogButtonEvent takes code c, Button b returns nothing
* register function c as a click-event callback for Button b
*
* function RegisterDialogEvent takes code c, Dialog dlg returns nothing
* register function c as a click-event callback for Dialog dlg
*
******************************************************************************
*
* struct Button:
*
* Field Access:
*
* | method operator parent takes nothing returns Dialog
* | parent Dialog instance for this Button
* |
* | method operator button takes nothing returns button
* | actuall button handle
* |
* | method operator value takes nothing returns string
* | message displayed on button
* |
* | method operator hotkey takes nothing returns integer
* | hotkey for this button
* |
* | method operator index takes nothing returns integer
* | position of button in parents dialog
* |
* | method operator reference takes nothing returns Dialog
* | returns Dialog instance connected with button if any
*
*
* Methods:
*
* | static method create takes GenericDialog dlg, string s, integer hk returns thistype
* | default ctor of Button struct; note - actuall button is not drawn yet
* |
* | method destroy takes nothing returns nothing
* | default dctor; call only if you really need to
* |
* | method register takes code c returns nothing
* | registers function c as a click-event callback
* |
* | method disconnect takes nothing returns nothing
* | unbinds current dialog ref from this button
* |
* | method connect takes GenericDialog dlg returns nothing
* | binds dialog dlg with this button; dlg will be displayed on this click-event
*
*
******************************************************************************
*
* struct Dialog:
*
* Field Access:
*
* | method operator dialog takes nothing returns dialog
* | actuall dialog handle
* |
* | method operator message takes nothing returns string
* | retrives message string assigned to this dialog
* |
* | method operator message= takes string s returns nothing
* | assigns message of dialog to value of s
* |
* | method button takes integer i returns Button
* | returns struct instance of Button at position i
*
*
* General:
*
* | static method create takes nothing returns thistype
* | default ctor
* |
* | method destroy takes nothing returns nothing
* | default dctor
* |
* | method size takes nothing returns integer
* | returns button count for dialog
* |
* | method empty takes nothing returns boolean
* | whether there are any buttons added or not
*
*
* Display:
*
* | method visible takes nothing returns boolean
* | whether this dialog is visible to any player or not
* |
* | method visibleFor takes player p returns boolean
* | whether this dialog is visible to player p or not
* |
* | method show takes nothing returns nothing
* | displays dialog to every player
* |
* | method showFor takes player p returns nothing
* | displays dialog to player p
* |
* | method hide takes nothing returns nothing
* | hides dialog for every player
* |
* | method hideFor takes player p returns nothing
* | hides dialog for player p
*
*
* Refreshing:
*
* | method forceRefresh takes nothing returns nothing
* | causes immediate repaint of dialog, ignoring the batch count
* |
* | method refresh takes nothing returns nothing
* | repaints dialog if not supressed by batch count
* |
* | method lock takes nothing returns nothing
* | increases batch count prevent dialog from refreshing, follow each call by unlock()
* |
* | method unlock takes nothing returns nothing
* | decreases batch count, causing refresh if reduced to 0, use after each lock()
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | clears all data attached to this dialog
* |
* | method appendEx takes GenericButton b returns nothing
* | appends Button b to the end of dialog; parent of b must equal to this
* |
* | method append takes string s, integer hotkey returns nothing
* | generic append
* |
* | method insertEx takes integer pos, GenericButton b returns nothing
* | inserts Button b to before Button at position pos; parent of b must equal to this
* |
* | method insert takes integer pos, string s, integer hotkey returns nothing
* | generic insert
* |
* | method removeEx takes GenericButton b returns boolean
* | removes Button b from this dialog if found
* |
* | method remove takes integer pos returns boolean
* | removes Button at position pos from this dialog
* |
* | method register takes code c returns nothing
* | registers function c as a click-event callback
*
*
*****************************************************************************/
library DialogButton requires VectorT, Alloc
globals
private Dialog array displayed
private Table table
endglobals
function GetEventDialog takes nothing returns Dialog
return table[GetHandleId(GetClickedDialog())]
endfunction
function GetEventButton takes nothing returns Button
local Button b = table[GetHandleId(GetClickedButton())] // via TriggerRegisterDialogEvent
if ( b == 0 ) then
return table[GetHandleId(GetTriggeringTrigger())] // via TriggerRegisterDialogButtonEvent
endif
return b
endfunction
function GetDialogEventPlayer takes nothing returns player
return GetTriggerPlayer()
endfunction
private struct GenericDialog extends array
dialog dialog
string message
integer batch
trigger cb
implement Alloc
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.dialog = DialogCreate()
set this.message = ""
set this.batch = 0
set this.cb = CreateTrigger()
set table[GetHandleId(this.dialog)] = this
return this
endmethod
method destroy takes nothing returns nothing
call table.remove(GetHandleId(dialog))
call DialogDestroy(dialog)
set dialog = null
set message = null
set batch = 0
call DestroyTrigger(cb)
set cb = null
call deallocate()
endmethod
endstruct
private struct GenericButton extends array
GenericDialog parent
button button
string value
integer hotkey
integer index
GenericDialog ref
boolexpr bexpr
trigger cb
implement Alloc
static method create takes Dialog dlg, string s, integer hk returns thistype
local thistype this = thistype.allocate()
set this.parent = dlg
set this.value = s
set this.hotkey = hk
set this.index = 0
return this
endmethod
method destroy takes nothing returns nothing
call table.remove(GetHandleId(button))
set parent = 0
set button = null
set value = null
set hotkey = 0
set index = 0
set ref = 0
if ( bexpr != null ) then
call DestroyBoolExpr(bexpr)
set bexpr = null
endif
if ( cb != null ) then
call table.remove(GetHandleId(cb))
call DestroyTrigger(cb)
set cb = null
endif
call deallocate()
endmethod
method paint takes integer pos returns nothing
if ( parent != 0 ) then
if ( button != null ) then
call table.remove(GetHandleId(button))
endif
if ( cb != null ) then
call DestroyTrigger(cb)
endif
set index = pos
set button = DialogAddButton(parent.dialog, value, hotkey)
set table[GetHandleId(button)] = this
if ( bexpr != null ) then
set cb = CreateTrigger()
call TriggerRegisterDialogButtonEvent(cb, button)
call TriggerAddCondition(cb, bexpr)
set table[GetHandleId(cb)] = this
endif
endif
endmethod
endstruct
static if not ButtonVector.create.exists then
//! runtextmacro DEFINE_VECTOR("ButtonVector", "GenericButton", "true")
endif
private module DialogInit
private static method onInit takes nothing returns nothing
set table = Table.create()
endmethod
endmodule
struct Dialog extends array
private ButtonVector buttons
private method operator base takes nothing returns GenericDialog
return this
endmethod
method operator dialog takes nothing returns dialog
return base.dialog
endmethod
method operator message takes nothing returns string
return base.message
endmethod
method operator message= takes string s returns nothing
set base.message = s
call DialogSetMessage(base.dialog, base.message)
endmethod
method size takes nothing returns integer
return buttons.size()
endmethod
method empty takes nothing returns boolean
return buttons.empty()
endmethod
method button takes integer i returns Button
if ( i < 0 and i >= size() ) then
return 0
endif
return buttons[i]
endmethod
method visible takes nothing returns boolean
local integer i = 0
loop
exitwhen i >= 16
if ( displayed[i] == this ) then
return true
endif
set i = i + 1
endloop
return false
endmethod
method visibleFor takes player p returns boolean
return ( displayed[GetPlayerId(p)] == this )
endmethod
method show takes nothing returns nothing
if ( not empty() ) then
set displayed[GetPlayerId(GetLocalPlayer())] = this
call DialogSetMessage(base.dialog, base.message)
call DialogDisplay(GetLocalPlayer(), base.dialog, true)
endif
endmethod
method showFor takes player p returns nothing
if ( not empty() ) then
set displayed[GetPlayerId(p)] = this
call DialogSetMessage(base.dialog, base.message)
call DialogDisplay(p, base.dialog, true)
endif
endmethod
method hide takes nothing returns nothing
if ( visibleFor(GetLocalPlayer()) ) then
set displayed[GetPlayerId(GetLocalPlayer())] = 0
call DialogDisplay(GetLocalPlayer(), base.dialog, false)
endif
endmethod
method hideFor takes player p returns nothing
if ( visibleFor(p) ) then
set displayed[GetPlayerId(p)] = 0
call DialogDisplay(p, base.dialog, false)
endif
endmethod
private static method onClick takes nothing returns boolean
local GenericButton b = GetEventButton()
local Dialog dlg = GetEventDialog()
if ( b.ref == 0 ) then
if ( dlg.visibleFor(GetLocalPlayer()) ) then
set displayed[GetPlayerId(GetLocalPlayer())] = 0
endif
endif
return false
endmethod
static method create takes nothing returns thistype
local thistype this = GenericDialog.create()
set this.buttons = ButtonVector.create()
call TriggerRegisterDialogEvent(base.cb, base.dialog)
call TriggerAddCondition(base.cb, function thistype.onClick)
return this
endmethod
method destroy takes nothing returns nothing
call hide()
call buttons.destroy()
set buttons = 0
call base.destroy()
endmethod
method forceRefresh takes nothing returns nothing
local integer i = 0
call DialogDisplay(GetLocalPlayer(), base.dialog, false)
call DialogClear(base.dialog)
call DialogSetMessage(base.dialog, base.message)
loop
exitwhen i >= size()
call buttons[i].paint(i)
set i = i + 1
endloop
if ( visibleFor(GetLocalPlayer()) ) then
call DialogDisplay(GetLocalPlayer(), base.dialog, true)
endif
endmethod
method refresh takes nothing returns nothing
if ( base.batch == 0 ) then
call forceRefresh()
endif
endmethod
method lock takes nothing returns nothing
set base.batch = base.batch + 1
endmethod
method unlock takes nothing returns nothing
if ( base.batch > 0 ) then
set base.batch = base.batch - 1
if ( base.batch == 0 ) then
call forceRefresh()
endif
endif
endmethod
method clear takes nothing returns nothing
call hide()
call DialogClear(base.dialog)
set base.message = null
call buttons.clear()
endmethod
method appendEx takes GenericButton b returns nothing
if ( b != 0 ) then
if ( b.parent == this ) then
call b.paint(size())
call buttons.push(b)
if ( visibleFor(GetLocalPlayer()) ) then
call DialogDisplay(GetLocalPlayer(), base.dialog, true)
endif
endif
endif
endmethod
method append takes string s, integer hotkey returns nothing
call appendEx(GenericButton.create(this, s, hotkey))
endmethod
method insertEx takes integer pos, GenericButton b returns nothing
if ( pos >= 0 and pos <= size() ) then
if ( b != 0 ) then
if ( b.parent == this ) then
call buttons.insert(pos, 1, b)
call refresh()
endif
endif
endif
endmethod
method insert takes integer pos, string s, integer hotkey returns nothing
call insertEx(pos, GenericButton.create(this, s, hotkey))
endmethod
method removeEx takes GenericButton b returns boolean
local integer i = 0
loop
exitwhen i >= size()
if ( buttons[i] == b ) then
call buttons.erase(i, 1)
call refresh()
return true
endif
set i = i + 1
endloop
return false
endmethod
method remove takes integer pos returns boolean
if ( buttons[pos] == 0 ) then
return false
else
call buttons.erase(pos, 1)
call refresh()
endif
return true
endmethod
method register takes code c returns nothing
if ( base.dialog != null ) then
call TriggerClearConditions(base.cb)
call TriggerAddCondition(base.cb, function thistype.onClick)
call TriggerAddCondition(base.cb, Condition(c))
endif
endmethod
implement DialogInit
endstruct
struct Button extends array
private method operator base takes nothing returns GenericButton
return this
endmethod
method operator parent takes nothing returns Dialog
return base.parent
endmethod
method operator button takes nothing returns button
return base.button
endmethod
method operator value takes nothing returns string
return base.value
endmethod
method operator hotkey takes nothing returns integer
return base.hotkey
endmethod
method operator index takes nothing returns integer
return base.index
endmethod
method operator reference takes nothing returns Dialog
return base.ref
endmethod
static method create takes GenericDialog dlg, string s, integer hk returns thistype
return GenericButton.create(dlg, s, hk)
endmethod
method destroy takes nothing returns nothing
call base.destroy()
endmethod
private static method onConnect takes nothing returns nothing
local thistype this = table[GetHandleId(GetTriggeringTrigger())]
if ( this != 0 ) then
if ( not Dialog(base.ref).empty() ) then
call Dialog(base.ref).showFor(GetTriggerPlayer())
else
call Dialog(base.parent).showFor(GetTriggerPlayer())
endif
endif
endmethod
method register takes code c returns nothing
if ( c == null ) then
return
endif
if ( base.bexpr != null ) then
call DestroyBoolExpr(base.bexpr)
endif
set base.bexpr = Condition(c)
if ( base.button != null ) then
if ( base.cb == null ) then
set base.cb = CreateTrigger()
call TriggerRegisterDialogButtonEvent(base.cb, base.button)
set table[GetHandleId(base.cb)] = this
else
call TriggerClearConditions(base.cb)
endif
call TriggerAddCondition(base.cb, base.bexpr)
endif
endmethod
method disconnect takes nothing returns nothing
set base.ref = 0
if ( base.cb != null ) then
call TriggerClearConditions(base.cb)
endif
endmethod
method connect takes GenericDialog dlg returns nothing
if ( dlg != 0 ) then
set base.ref = dlg
call register(function thistype.onConnect)
endif
endmethod
endstruct
function RegisterDialogButtonEvent takes code c, Button b returns nothing
call b.register(c)
endfunction
function RegisterDialogEvent takes code c, Dialog dlg returns nothing
call dlg.register(c)
endfunction
endlibrary
Demo code:
JASS:
scope Testing initializer Init
globals
private integer array humans
private integer array undeads
endglobals
private function onHumans takes nothing returns nothing
local Button b = GetEventButton()
local Dialog d = GetEventDialog()
if ( b.reference == 0 ) then
call CreateUnit(GetDialogEventPlayer(), humans[b.index], 0, 0, 0)
endif
endfunction
private function onUndeads takes nothing returns nothing
local Button b = GetEventButton()
local Dialog d = GetEventDialog()
if ( b != d.button(4) ) then
call CreateUnit(GetDialogEventPlayer(), undeads[b.index], 0, 0, 0)
endif
endfunction
private function Init takes nothing returns nothing
set humans[0] = 'Hamg'
set humans[1] = 'Hpal'
set humans[2] = 'Hmkg'
set humans[3] = 'Hblm'
set humans[5] = 'necr'
set undeads[0] = 'Udea'
set undeads[1] = 'Ulic'
set undeads[2] = 'Udre'
set undeads[3] = 'Ucrl'
endfunction
struct DialogButton_test extends array
private static Dialog dlg
private static method reshow takes nothing returns boolean
call dlg.show()
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local Dialog log = Dialog.create()
local Dialog win = Dialog.create()
local Button but = Button.create(log, "|cffffffffGo to page 2|r", 0)
set dlg = log
call PolledWait(2.0)
set log.message = "Humans"
call log.append("|cffff0000A|rrchmage", 'A')
call log.append("|cffff0000P|raladin", 'P')
call log.append("Mountain |cffff0000K|ring", 'K')
call log.append("|cffff0000B|rlood Mage", 'B')
call log.appendEx(but)
call but.connect(win)
call log.register(function onHumans)
call log.show()
call PolledWait(5.)
// Blood Mage will disappear! Not so "human" anymore
call log.lock()
set log.message = log.message + " and friends"
call log.remove(3)
call log.insert(3, "|cffff0000B|ranana", 'B')
call log.append("A |cffff0000R|rabbit", 'R')
call log.unlock()
set humans[3] = 'nlur'
set win.message = "Undeads"
call win.append("|cffff0000D|reath Knight", 'D')
call win.append("Li|cffff0000c|rh", 'C')
call win.append("D|cffff0000r|read Lord", 'R')
call win.append("Cryp|cffff0000t|r Lord", 'T')
set but = Button.create(win, "|cffffffffBack to page 1|r", 0)
call but.connect(log)
call win.appendEx(but)
call win.register(function onUndeads)
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t, Condition(function thistype.reshow))
set t = null
endmethod
endstruct
endscope
Last edited: