- Joined
- Jul 18, 2010
- Messages
- 2,377
Button
Buttons are powerful and useful UI-Frames. A Button in Warcraft 3 is only a space on the screen that can be pressed. The pressing can send an audio Feedback (GLUE created with BlzCreateFrame) and one can catch the pressing with a TriggerEvent which allows executing Code. But if the button is only the clickable space where does the displayed Image, the Text and the Highlight come from?
Jump to without fdf
Hotkeys
Each is an own strong connected childFrame being used as some function. Such strong conneted childs mimic size and Position of the parent Frame. There is a BACKDROP that is shown when the BUTTON is enabled, another is shown when the BUTTON is disabled and so on, each such is an own ChildFrame....
Jump to without fdf
Hotkeys
Each is an own strong connected childFrame being used as some function. Such strong conneted childs mimic size and Position of the parent Frame. There is a BACKDROP that is shown when the BUTTON is enabled, another is shown when the BUTTON is disabled and so on, each such is an own ChildFrame....
Example
Here is an Image of the text inside a fdf of a GLUETEXTBUTTON containg most, when not all strong connected childFrames. In the Image: text that is connected to Childframes is in a colored box. Also there are some comments right to the Frames in different color.
ControlStyle "AUTOTRACK", Mouse Clicks onto the button won't affect/reach the world below the button, can't order units or select Units.
The TEXT-ChildFrame is not needed to set the text of the TEXTBUTTON by Code, but it is needed to style it in the fdf.
The highlighter does only work when ControlStyle mentions that behaviour.
A (GLUE)BUTTON is basicly the same without the ButtonText feature/child-Frame.
ControlStyle "AUTOTRACK", Mouse Clicks onto the button won't affect/reach the world below the button, can't order units or select Units.
The TEXT-ChildFrame is not needed to set the text of the TEXTBUTTON by Code, but it is needed to style it in the fdf.
The highlighter does only work when ControlStyle mentions that behaviour.
A (GLUE)BUTTON is basicly the same without the ButtonText feature/child-Frame.
Unneeded ChildFrames
Child-Frames that you do not need can often be skiped.
Therefore this is still a valid GLUEBUTTON: even with only mentioning ControlBackdrop, ControlDisabledBackdrop and ControlMouseOverHighlight.
Means a texture in enabled state, a texture for disabled state and a glowing for mouse hovering.
An example how one would create this "HeroSelectorButton". One can't cause it is not loaded.
Therefore this is still a valid GLUEBUTTON: even with only mentioning ControlBackdrop, ControlDisabledBackdrop and ControlMouseOverHighlight.
Means a texture in enabled state, a texture for disabled state and a glowing for mouse hovering.
Code:
Frame "GLUEBUTTON" "HeroSelectorButton" {
Width 0.035,
Height 0.035,
ControlStyle "AUTOTRACK|HIGHLIGHTONMOUSEOVER",
ControlBackdrop "HeroSelectorButtonIcon",
Frame "BACKDROP" "HeroSelectorButtonIcon" {
}
ControlDisabledBackdrop "HeroSelectorButtonIconDisabled",
Frame "BACKDROP" "HeroSelectorButtonIconDisabled" {
}
ControlMouseOverHighlight "HeroSelectorButtonHighLight",
Frame "HIGHLIGHT" "HeroSelectorButtonHighLight" {
HighlightType "FILETEXTURE",
HighlightAlphaFile "UI\Glues\ScoreScreen\scorescreen-tab-hilight.blp",
HighlightAlphaMode "ADD",
}
}
JASS:
//Create "HeroSelectorButton", for game UI
local framehandle buttonFrame = BlzCreateFrame("HeroSelectorButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
//By Having created "HeroSelectorButton" also its children are created, this children are accessed using BlzGetFrameByName right after the creation or later one if the slots were not taken by other frames.
local framehandle buttonIconFrame = BlzGetFrameByName("HeroSelectorButtonIcon", 0)
//Set a Texture
call BlzFrameSetTexture(buttonIconFrame, "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn", 0, true)
// Place the buttonFrame to the center of the screen
call BlzFrameSetAbsPoint(buttonFrame, FRAMEPOINT_CENTER, 0.4, 0.3)
// Give that buttonFrame a size
call BlzFrameSetSize(buttonFrame, 0.05, 0.05)
Hotkeys in fdf
(GLUE)(TEXT)BUTTONs can have set Hotkeys right in the fdf. This is done by giving the Button a Parent Frame that has the fdfAction
For some wierd reason the Hotkeys don't work out of the Box (when created for GAMEUI), first one has to hide and show the Parent Frame. Then the hotkeys will listen and fire when clicked. The hotkeys will fire a control-click Event which does not keep the keyboard focus unlike when the event is started with a mouse click.
Buttons added by code (to a Parent with
An example fdf:
The result of the fdf. It creates 3 Strings (KeyMyButton1, KeyMyButton2, KeyMyButton3) and Defines FRAME "MyButtonF" as well as it's 3 Child ButtonFrames "MyButton1", "MyButton2" and "MyButton3".
The Lua code to Load the fdf/toc create the Frames, trigger and events to listen to the Button clicks.
One still would need to write the toc and fdf. And import them into the map.
TabFocusPush,
aswell as creating a StringList with the wanted Hotkeys. Then one creates Buttons as child of the Frame with TabFocusPush,
and setups ControlShortcutKey "StringName in StringList",
. This hotkeys will consider the current active main input Frame, when created for GAMEUI they will stop listen when the player is in a Menu or types in chat messages.For some wierd reason the Hotkeys don't work out of the Box (when created for GAMEUI), first one has to hide and show the Parent Frame. Then the hotkeys will listen and fire when clicked. The hotkeys will fire a control-click Event which does not keep the keyboard focus unlike when the event is started with a mouse click.
Buttons added by code (to a Parent with
TabFocusPush,
) during the runtime will not use ControlShortcutKey,
making this quite static (like most in fdf). One could use OsKeyEvents for something more dynamic.An example fdf:
Code:
IncludeFile "UI\FrameDef\UI\EscMenuTemplates.fdf",
// The Hotkeys for the Buttons
StringList {
KeyMyButton1 "1",
KeyMyButton2 "2",
KeyMyButton3 "3",
}
Frame "FRAME" "MyButtonF" {
Width 0.1,
Height 0.1,
TabFocusPush, // Enables ControlShortcutKey for children
LayerStyle "IGNORETRACKEVENTS", // this Frame itself will not control/Block the Mouse
Frame "BUTTON" "MyButton1" INHERITS WITHCHILDREN "EscMenuButtonTemplate" {
ControlShortcutKey "KeyMyButton1", // read "KeyMyButton1" and use it's value as Hotkey
SetPoint TOP, "MyButtonF", TOP, 0, 0,
}
Frame "BUTTON" "MyButton2" INHERITS WITHCHILDREN "EscMenuButtonTemplate" {
ControlShortcutKey "KeyMyButton2",
SetPoint TOP, "MyButton1", BOTTOM, 0, 0,
}
Frame "BUTTON" "MyButton3" INHERITS WITHCHILDREN "EscMenuButtonTemplate" {
ControlShortcutKey "KeyMyButton3",
SetPoint TOP, "MyButton2", BOTTOM, 0, 0,
}
}
The Lua code to Load the fdf/toc create the Frames, trigger and events to listen to the Button clicks.
Lua:
do
local real = MarkGameStarted
function MarkGameStarted()
real()
BlzLoadTOCFile("war3mapImported\\Test.toc")
local frame = BlzCreateFrame("MyButtonF", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.6, 0.3)
local trigger = CreateTrigger()
BlzTriggerRegisterFrameEvent(trigger, BlzGetFrameByName("MyButton1", 0), FRAMEEVENT_CONTROL_CLICK)
BlzTriggerRegisterFrameEvent(trigger, BlzGetFrameByName("MyButton2", 0), FRAMEEVENT_CONTROL_CLICK)
BlzTriggerRegisterFrameEvent(trigger, BlzGetFrameByName("MyButton3", 0), FRAMEEVENT_CONTROL_CLICK)
TriggerAddAction(trigger, function()
print("click", BlzFrameGetName(BlzGetTriggerFrame()))
end)
-- for some reason the buttons did not use hotkeys on default but after the were hidden and shown the hotkeys work
BlzFrameSetVisible(frame, false)
BlzFrameSetVisible(frame, true)
end
end
Buttons only with Code
Fdf has nice Features, but if I would only want a button showing an Icon could that be created with Code alone?
Yes, using BlzCreateFrameByType one can create new empty Frames based on FrameType (behavoiur) and give them new names and even inherit other Frames.
One would create a GLUEBUTTON with parent GameUI and a BACKDROP using the created Button as parent. Also it would be required to mimic postion and size which BlzFrameSetAllPoints handles. Each of that is only one line of Code.
And we have a Button with an Icon:
But what about the glowing, can that also be done only with code?
Yes, you see the empty string after the parentFrame in BlzCreateFrameByType, this is the Name of the mainFrame the new frame inherits from.
Yes, using BlzCreateFrameByType one can create new empty Frames based on FrameType (behavoiur) and give them new names and even inherit other Frames.
One would create a GLUEBUTTON with parent GameUI and a BACKDROP using the created Button as parent. Also it would be required to mimic postion and size which BlzFrameSetAllPoints handles. Each of that is only one line of Code.
JASS:
//Create a "GLUEBUTTON" named "Facebutton", the clickable Button, for game UI
local framehandle buttonFrame = BlzCreateFrameByType("GLUEBUTTON", "FaceButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0)
//Create a BACKDROP named "FaceButtonIcon", the visible image, for buttonFrame.
local framehandle buttonIconFrame = BlzCreateFrameByType("BACKDROP", "FaceButtonIcon", buttonFrame, "", 0)
//buttonIconFrame will mimic buttonFrame in size and position
call BlzFrameSetAllPoints(buttonIconFrame, buttonFrame)
//Set a Texture
call BlzFrameSetTexture(buttonIconFrame, "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn", 0, true)
// Place the buttonFrame to the center of the screen
call BlzFrameSetAbsPoint(buttonFrame, FRAMEPOINT_CENTER, 0.4, 0.3)
// Give that buttonFrame a size
call BlzFrameSetSize(buttonFrame, 0.05, 0.05)
But what about the glowing, can that also be done only with code?
Yes, you see the empty string after the parentFrame in BlzCreateFrameByType, this is the Name of the mainFrame the new frame inherits from.
Inherit
Inherit is a technique in which data of another Frame is copied, one should only copy familiar frametypes.
When using BlzCreateFrameByType one can inherit data/behaviour from a loaded mainframe, a Frame with its framehead outside of a framebody, by writing the Name of the Frame one wants to inherit. For the glowing we need a mainframe with a ControlMouseOverHighlight or ControlFocusHighlight depends on the wanted behaviour. Inherited childrenFrames are not accessable with BlzGetFrameByName and don't get a handleId. With the release of the native BlzFrameGetChild in V1.32.6 one is able to get the inherited childFrames during the runtime, in 1.32.6 this native ignores String&Texture childFrames.
There are 2 Default loaded BUTTONs which could be inherited for glowing:
"IconButtonTemplate" (blue)
"ScoreScreenTabButtonTemplate" (yellow)
The example from earlier now having a yellow glowing.
When using BlzCreateFrameByType one can inherit data/behaviour from a loaded mainframe, a Frame with its framehead outside of a framebody, by writing the Name of the Frame one wants to inherit. For the glowing we need a mainframe with a ControlMouseOverHighlight or ControlFocusHighlight depends on the wanted behaviour. Inherited childrenFrames are not accessable with BlzGetFrameByName and don't get a handleId. With the release of the native BlzFrameGetChild in V1.32.6 one is able to get the inherited childFrames during the runtime, in 1.32.6 this native ignores String&Texture childFrames.
There are 2 Default loaded BUTTONs which could be inherited for glowing:
"IconButtonTemplate" (blue)
"ScoreScreenTabButtonTemplate" (yellow)
JASS:
BlzCreateFrameByType("GLUEBUTTON", "MyFrameName", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "IconButtonTemplate", 0)
BlzCreateFrameByType("GLUEBUTTON", "MyFrameName", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScoreScreenTabButtonTemplate", 0)
JASS:
//Create a "GLUEBUTTON" named "Facebutton" inheriting "ScoreScreenTabButtonTemplate", the clickable Button, for game UI
local framehandle buttonFrame = BlzCreateFrameByType("GLUEBUTTON", "FaceButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScoreScreenTabButtonTemplate", 0)
//Create a BACKDROP named "FaceButtonIcon", the visible image, for buttonFrame.
local framehandle buttonIconFrame = BlzCreateFrameByType("BACKDROP", "FaceButtonIcon", buttonFrame, "", 0)
//buttonIconFrame will mimic buttonFrame in size and position
call BlzFrameSetAllPoints(buttonIconFrame, buttonFrame)
//Set a Texture
call BlzFrameSetTexture(buttonIconFrame, "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn", 0, true)
// Place the buttonFrame to the center of the screen
call BlzFrameSetAbsPoint(buttonFrame, FRAMEPOINT_CENTER, 0.4, 0.3)
// Give that buttonFrame a size
call BlzFrameSetSize(buttonFrame, 0.05, 0.05)
Button Action
Most times when one creates a Button, that Button should do something when the user clicks it. In Warcraft 3 this is done over Triggers and Events. The most straightforward FrameEvent, for Buttons, is FRAMEEVENT_CONTROL_CLICK it happens when the user releases the left MouseButton when the initial mouseClick was inside the Button or when the Button has focus and the user clicks return/space.
An example Code, that prints a Message to the screen when a button was clicked when it was registered using ButtonAddAction:
An example Code, that prints a Message to the screen when a button was clicked when it was registered using ButtonAddAction:
JASS:
function ButtonClickAction takes nothing returns nothing
BJDebugMsg("Button Clicked")
endfunction
function ButtonAddAction takes framehandle buttonFrame returns nothing
local trigger buttonEventTrigger = CreateTrigger()
call TriggerAddAction(buttonEventTrigger, function ButtonClickAction)
call BlzTriggerRegisterFrameEvent(buttonEventTrigger, buttonFrame, FRAMEEVENT_CONTROL_CLICK)
endfunction
Focus
After a (GLUE)(TEXT)BUTTON was clicked (with the mouse) it will keep the clicking player's input focus. Blocking most hotkeys and oskey events as long they have the focus, even without any attached Trigger or FrameEvent. You can remove that focus from a clicked Button by disabling and enabling the clicked Button inside the TriggerAction then it loses the focus with the clicking. The user can also remove the focus with a left mouse click onto playable ground
Sadly this Technique can produce a bug, when the Button is clicked and a pressed arrow key is released almost at the same time the camera will keep panning even without holding the arrow key (discovered by Uncle), another mouse click by the user somewhere stops the auto panning.
Uncle found later a way to stop the paning by code using
JASS:
function ButtonClickAction takes nothing returns nothing
BJDebugMsg("Button Clicked")
if GetLocalPlayer() == GetTriggerPlayer() then
call BlzFrameSetEnable(BlzGetTriggerFrame(), false)
call BlzFrameSetEnable(BlzGetTriggerFrame(), true)
call StopCamera()
endif
endfunction
Uncle found later a way to stop the paning by code using
StopCameraForPlayerBJ()
or StopCamera()
FrameEvent
Inside Frameevents one can get some informations about the current event.
The linked content is a list about frameevents and which frametypes can use them.
[JASS/AI] - UI: FrameEvents and FrameTypes
Edited: Replaced selfexecution with a less problematic approach
Edited: Added a demo map with a (re)selection example & a demo map for fdf hotkeys
UI: Change Lumber Text
[JASS/AI] - UI: Create a TextButton
[JASS/AI] - UI: Positionate Frames (important)
UI: toc-Files
UI: Reading a FDF
UI - The concept of Parent-Frames
[JASS/AI] - UI: FrameEvents and FrameTypes
UI: Frames and Tooltips
[JASS/AI] - UI: Creating a Bar
UI - Simpleframes
UI: What are BACKDROPs?
UI: GLUEBUTTON
UI: TEXTAREA the scrolling Text Frame
UI: EditBox - Text Input
[JASS/AI] - UI: Creating a Cam control
UI: Showing 3 Multiboards
UI: OriginFrames
Default Names for BlzGetFrameByName (Access to Existing game-Frames)
[JASS/AI] - UI: List - Default MainFrames (built in CreateAble)
Code:
BlzGetTriggerFrame()
BlzGetTriggerFrameEvent()
GetTriggerPlayer()
BlzGetTriggerFrameText()
BlzGetTriggerFrameValue()
[JASS/AI] - UI: FrameEvents and FrameTypes
Edited: Replaced selfexecution with a less problematic approach
Edited: Added a demo map with a (re)selection example & a demo map for fdf hotkeys
Other UI-Frame Tutorials
The following links might provide more insight into this subject.UI: Change Lumber Text
[JASS/AI] - UI: Create a TextButton
[JASS/AI] - UI: Positionate Frames (important)
UI: toc-Files
UI: Reading a FDF
UI - The concept of Parent-Frames
[JASS/AI] - UI: FrameEvents and FrameTypes
UI: Frames and Tooltips
[JASS/AI] - UI: Creating a Bar
UI - Simpleframes
UI: What are BACKDROPs?
UI: GLUEBUTTON
UI: TEXTAREA the scrolling Text Frame
UI: EditBox - Text Input
[JASS/AI] - UI: Creating a Cam control
UI: Showing 3 Multiboards
UI: OriginFrames
Default Names for BlzGetFrameByName (Access to Existing game-Frames)
[JASS/AI] - UI: List - Default MainFrames (built in CreateAble)
Attachments
Last edited: