• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

UI Utils Manual

Status
Not open for further replies.

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Introduction
UI Utils is a library written in vJass which aims to assist user in using the new UI natives introduced since patch 1.31.

Without this library, user is required to learn and understand a lot things before being able to actually deal with '"the interface" itself, things like toc & fdf file, frame definition, the uncanny coordinate system, limitations, and so much more. Which can be a lot of hassle to get through. That's where this library comes in. It aims to facilitate user to immediately work with UI without needing to know any of those stuff. All you need is vJass programming knowledge. It also provides nice wrapper to the natives which can be lifesaver even for advanced users.

Resource thread: (Link)

Key Knowledge
Several important things to point out from the whole tutorial thread:
• UI Utils uses pixel as measurement unit
• It uses bottom-left as default anchor point
• Anchor point is where the frame is docked on it's parent area, on screen if it's a root frame (read more...)
• Pivot point is where the frame pivots within it's own area (read more...)
• Child frame will inherit it's parent's properties (position, opacity, size, etc.)
• TOC file contains the list of fdf files (read more...)
• FDF file contains frame definitions (read more...)
• Simple frame does not have events (except for SIMPLE_BUTTON)
• Non-simple frame can't go beyond the "4:3 bounds"(read more...)
Table of Content

• Getting Started •

x

open

(hidden)

Installation
• Import [Snippet] LinkedListModule
• Copy UIUtils trigger to your map
• Go to Import Manager and import following files to your map:
- UIUtils.toc
- UIUtils.fdf​
• Configure the system
• For true fullscreen you might need to manually remove the inventory cover texture​

Configuration

This section explains a bit more detail about some stuffs inside the system configuration:

private constant boolean PERSISTENT_CHILD_PROPERTIES = true
This setting determines how the system updates frame's properties (position, scale, etc.) when it changes to other parent.
If set to false, the frame's apparent properties will be retained as shown below:

anim1.gif
If set to true, the frame's actual properties will be retained as shown below:

anim2.gif
private constant boolean AUTO_SIZE_FRAMES = true
If set to true, it will adjust frame sizes relative to the reference resolution. If set to false, all frame size will remain the same on all resolutions.
private constant integer RESOLUTION_WIDTH = 1360
private constant integer RESOLUTION_HEIGHT = 768
This is the resolution you use for designing your UI layout. This will be used as reference in resizing frame on other resolutions.​


• Primitive Frame Types •

x

open

(hidden)

Before diving right in, you have to know the basic difference between simple and non-simple frame. Simple frame commonly doesn't fire UI event, except for SIMPLE_BUTTON (click event only), but they can go where ever they want on the screen. While non-simple frame is much more interactive (fires events) yet they don't go beyond a certain bounds, which later I would personally call the "4:3 bounds". If a non-simple frame tries to go beyond it, it will become stretched, or even not rendered at all. UI Utils can automatically prevent non-simple frames from going out of these bounds.

• TEXTURE & SIMPLE_TEXTURE
For displaying 2D image/texture.
pic1.jpg
JASS:
        set frame = UIFrame.create(false, UIFrame.TYPE_TEXTURE, UIFrame.Null, -35, 0, 0)
        set frame.texture = "ReplaceableTextures\\CommandButtons\\BTNFootman.tga"
        call frame.setAnchorPoint(0.5, 0.5)
        call frame.setPivotPoint(0.5, 0.5)
        call frame.setSize(64, 64)

        set frame = UIFrame.create(false, UIFrame.TYPE_SIMPLE_TEXTURE, UIFrame.Null, 35, 0, 0)
        set frame.texture = "ReplaceableTextures\\CommandButtons\\BTNPeasant.tga"
        call frame.setAnchorPoint(0.5, 0.5)
        call frame.setPivotPoint(0.5, 0.5)
        call frame.setSize(64, 64)

• TEXT & SIMPLE_TEXT
• BUTTON & SIMPLE_BUTTON
• BAR
• H_SLIDER & V_SLIDER



• Creating a Frame •

x

open

(hidden)

First, let's check out the frame creator API:
JASS:
static method create takes boolean isSimple, string frameType, UIFrame parent, real x, real y, integer level returns UIFrame

First parameter is "isSimple" which is a boolean. Pass true if the frame type we are about to create is a simple frame, else pass false. UI Utils automatically determines it if you are using the primitive frame types. But you must pass the correct value if you are about to create your own custom frame definition.

Next, we have to determine what type of frame we want to create, based on our needs. Let's say we are creating an inventory system and we are adding image as the background of the inventory. Thus we will be using either SIMPLE_TEXTURE or TEXTURE. Be aware of simple and non-simple frame limitation and chose one of those based on what features you want to add to the system. For instance, you can't put the inventory at the screen edge if you use TEXTURE. And even if you use SIMPLE_TEXTURE and put it at screen left/right edge, you can't use non-simple frame like TEXTURE and BUTTON for the item icons because they won't be able to follow the background. You will be forced to use SIMPLE_BUTTON, which can only detect left-click, thus it will limit item interaction you can have inside the inventory. So if you want to have many interactions, you can't use simple frames, and can't put it at screen edge. It's just one example scenario. The main point is, determine the frame type carefully, think it long term.

Okay now let's start writing the code. Say we decided to use non-simple frame:

JASS:
globals
    UIFrame background
endglobals

function createInventory takes nothing returns nothing
    set background = UIFrame.create(false, UIFrame.TYPE_TEXTURE, ...
endfunction
Next, we determine the parent frame. We want this background to be the root of all frames inside it. So let's set the parent to null, or in this case, UIFrame.Null.

JASS:
globals
    UIFrame background
endglobals

function createInventory takes nothing returns nothing
    set background = UIFrame.create(false, UIFrame.TYPE_TEXTURE, UIFrame.Null, ...
endfunction
Next two parameters are the position, x and y, of our frame. Before proceeding, let me briefly explain what the term "position" is in this UI environment. Here, position is essentially the offset from the frame's anchor point. By default, the frame's anchor point is bottom-left or in number: (0, 0). If you set the frame's position to [x: 100, y: 100], this is how it turns out, say the whole black area is our screen:

pic2.jpg


That's because the anchor point is bottom-left. Say we change the anchor point to top-right, or in number (1, 1):

pic3.jpg


It will go out of screen. I hope you get the basic picture at this point. You can read more about anchor and pivot point here: Link.

Okay, back to our inventory. Say we want to put it at the right edge of the "screen", I mean we can't, since we decided to use non-simple frame. But UI Utils provides a feature to prevent non-simple frame from going off the bounds. If you turned the feature on, you won't have to worry about it's position anymore. Say we have it turned on, then we will just anchors the background at the screen's middle right edge. Remember that position is just offset and we will change it's anchor to middle left later, so we will just pass zeros for the position. And we will set the pivot point to middle-right as well for neat reason.

JASS:
globals
    UIFrame background
endglobals

function createInventory takes nothing returns nothing
    set background = UIFrame.create(false, UIFrame.TYPE_TEXTURE, UIFrame.Null, 0, 0, ...)
    call background.setAnchorPoint(1, 0.5) // Set anchor point to right-middle of the screen
    call background.setPivotPoint(1, 0.5) // Set pivot point to right-middle
endfunction
Then one last parameter is the "level" of the frame. The higher the level is, the higher it's rendering priority will be. Because it's a background we want it to be rendered below everything else (item icons for example) so we simply set it at zero.

JASS:
globals
    UIFrame background
endglobals

function createInventory takes nothing returns nothing
    set background = UIFrame.create(false, UIFrame.TYPE_TEXTURE, UIFrame.Null, 0, 0, 0)
    call background.setAnchorPoint(1, 0.5) // Set anchor point to right-middle of the screen
    call background.setPivotPoint(1, 0.5) // Set pivot point to right-middle
endfunction
Now you will have a plain white frame displayed at the right part of the screen. Let's continue in the next section!
ss1.jpg



• Frame Modification •

x

open

(hidden)

You can manipulate the frame however you want using a bunch of wrappers and some methods UI Utils provides. The whole list of APIs is available in the UI Utils script itself including each's usage.

Again, at this point we have a plain white frame on our screen. Now let's apply a fancy background texture on it. Let's import the desired texture image to our map, as always, it must be in either tga or blp format. Now we can use it as background's texture using the texture wrapper:
JASS:
method operator texture= takes string filePath returns nothing
method operator texture  takes nothing returns string
JASS:
globals
    UIFrame background
endglobals

function createInventory takes nothing returns nothing
    set background = UIFrame.create(false, UIFrame.TYPE_TEXTURE, UIFrame.Null, 0, 0, 0)
    set background.texture = "war3mapimported\\background.tga"
    call background.setAnchorPoint(1, 0.5) // Set anchor point to right-middle of the screen
    call background.setPivotPoint(1, 0.5) // Set pivot point to right-middle
endfunction
Now it has a texture but appears too small. We need to resize it. In my case, the texture dimension is:
ss2.jpg


I checked the texture's size in photoshop in pixel unit. Quite conveniently, UIUtils also uses pixel unit measurement. So I will simply set the size accordingly using this method:
JASS:
method setSize takes real width, real height returns nothing
JASS:
globals
    UIFrame background
endglobals

function createInventory takes nothing returns nothing
    set background = UIFrame.create(false, UIFrame.TYPE_TEXTURE, UIFrame.Null, 0, 0, 0)
    set background.texture = "war3mapimported\\background.tga"
    call background.setAnchorPoint(1, 0.5) // Set anchor point to right-middle of the screen
    call background.setPivotPoint(1, 0.5) // Set pivot point to right-middle
    call background.setSize(293, 438)
endfunction
Keep in mind that modifying frame doesn't require any particular execution order. It's just my weird personal preference to put set's before call's. Anyway let's see how our code turns out:
ss3.jpg


Now we are getting somewhere! That's just simple frame modification example, but you are free to manipulate the frame however you want in runtime using the other wrappers and methods available (read documentation). We will continue with our inventory in the next section.

About changing texture, for some frame types like BUTTON, you can change the other sub-textures as well, as mentioned in the documentation:
JASS:
> Texture displayed when frame is disabled
    | method operator disabledTexture= takes string filePath returns nothing
    | method operator disabledTexture  takes nothing returns string
> Highlighter texture
    | method operator highlightTexture= takes string filePath returns nothing
    | method operator highlightTexture  takes nothing returns string
> Texture displayed when frame is pressed
    | method operator pushedTexture= takes string filePath returns nothing
    | method operator pushedTexture  takes nothing returns string
> Background texture for the frame
    | method operator backgroundTexture= takes string filePath returns nothing
    | method operator backgroundTexture  takes nothing returns string
> Border texture for the frame
    | method operator borderTexture= takes string filePath returns nothing
    | method operator borderTexture  takes nothing returns string
According to my latest test, not all textures can be modified, including border and highlight textures (blizzard's limitation) but I will just provide the wrapper anyway, just in case they change their mind one day.

For advanced user: in order for custom frame definition to work with these wrappers, your frame definition needs to follow specific format. You can check out UI Utils fdf file format or message me if you need help with this matter. Or alternatively, you can use the "subFrame getter" (read more).


• Compositing Frames •

x

open

(hidden)
Say we are creating item icons for the inventory above. We want those icons to always follow the background. Thus we set their parent to that background frame.


• Frame Property Inheritance •

x

open

(hidden)



• Anchor & Pivot •

x

open

(hidden)

Basically, anchor point is where the frame is docked on it's parent area (on screen if it's a root frame, or has no parent). While pivot point is where the frame pivots within it's own area. Confused? Let's break it down to technical level. In placing the frame, the system initially calculate the frame's anchor point, which ranges from 0 to 1, 1 means full width/height of the screen/parent frame. Then it will take the frame's position or offset into account. And finally the system will calculate the final position by offsetting the previous position by the frame's pivot point, which also ranges from 0 to 1, and 1 also means full width/height of the frame.

Maybe the easiest way to understand the concept of position, anchor, and pivot points altogether is by looking at the following tables (again, say the black area is our screen):


Position
Anchor
Pivot
Result (no parent)
Result (has parent)
(0, 0)(0, 0)(0, 0)p1.pngp4.png
(0.5, 0.5)p2.pngp5.png
(1, 1)p3.pngp6.png
Position
Anchor
Pivot
Result (no parent)
Result (has parent)
(0, 0)(0.5, 0.5)(0, 0)p7.pngp10.png
(0.5, 0.5)p8.pngp11.png
(1, 1)p9.pngp12.png
Position
Anchor
Pivot
Result (no parent)
Result (has parent)
(0, 0)(1, 1)(0, 0)p13.pngp16.png
(0.5, 0.5)p14.pngp17.png
(1, 1)p15.pngp18.png


• Frame Events •

x

open

(hidden)



• Origin Frames Manipulation •

x

open

(hidden)



• Sub-Frames •

x

open

(hidden)



• Other Features •

x

open

(hidden)



• F.A.Q •

x

open

(hidden)

 
Last edited:
Level 2
Joined
Nov 6, 2019
Messages
5
This is incredible!
Is there a way to make something display for a specific player only? I want to make a unit selector button but only show the appropriate unit for player.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
This is incredible!
Is there a way to make something display for a specific player only? I want to make a unit selector button but only show the appropriate unit for player.
Of course. You can create frame and hide/display/manipulate it inside if GetLocalPlayer() == specific_player then block.
 
Status
Not open for further replies.
Top