• 🏆 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!

[vJASS] WidescreenUI

Level 12
Joined
Jun 12, 2010
Messages
413
A simple library that provides a couple of useful frames to create UI elements that move to the edges of the screen, even if the user has a widescreen.

JASS:
library WidescreenUI
/*
    Credits:
    - Kazeon: UIUtils
        The logic behind DPI calculations is based on UIUtils.
        https://www.hiveworkshop.com/threads/ui-utils-v1-05.320005/
   
    - Tasyen: for UI Tutorials and discovering that ConsoleUIBackdrop allows frames to go out of the 4:3 view
        This library would not be possible without Tasyen's efforts. Enough said.


    Problem:
        - Frames can normally only stay within the 4:3 bounds at the center of the screen.
     
    Solution:
        - Depending on the Frames parent, it can actually go outside those bounds (discovered by Tasyen).
        - One such parent is ConsoleUIBackdrop.

    How to use this library:
        - Any frames that go outside the 4:3 screen must be children/grandchildren of UIView.ConsoleUIBackdrop.
   
        - Attach your frames to UIView.LEFT_BORDER and UIView.RIGHT_BORDER and they will automatically reposition when resolution changes.
   
        - Do not use UIView.LEFT_BORDER, UIView.RIGHT_BORDER as parents unless you know what you're doing.
   
        - This library is initialized at map start (0 seconds). Any code that uses it to create frames should also do that.
   
        - Useful variables:
            - UIView.minX => minimum X coordinate for the local user's screen.
            - UIView.maxX => maximum X coordinate for the local user's screen.
            - UIView.minY => 0.0
            - UIView.maxY => 0.6
 
 
/////////////// Detailed documentation (mostly copied from UI Utils)///////////

struct UIView
    1. Static Members
    • Values might be async between clients
 
        - Maximum x coordinate for the user's screen.
            | readonly static real maxX
       
        - Minimum x coordinate for the user's screen.
            | readonly static real minX
       
        - Screen width in pixels.
            | readonly static integer ResolutionWidth
       
        - Screen height in pixels.
            | readonly static integer ResolutionHeight
       
        - Frames
            | readonly static framehandle ConsoleUIBackdrop
       
            - The first child of ConsoleUIBackdrop.
                | readonly static framehandle LEFT_BORDER
           
            - The second child of ConsoleUIBackdrop.
                | readonly static framehandle RIGHT_BORDER
     
    2. Static methods
    • Values might be async between clients
 
        - Convert pixel unit to DPI and vice versa
            • Usage: [value]*UIUtils.PXTODPI
                     [value]*UIUtils.DPITOPX
                | static method operator PXTODPI takes nothing returns real
                | static method operator DPITOPX takes nothing returns real
           
        - Width of the 4:3 bound
            | static method operator width4by3 takes nothing returns real

        - Convert from pixel to screen x/y coordinate (in DPI unit)
            | static method getScreenPosX takes real x returns real
            | static method getScreenPosY takes real y returns real
                   
*/
//////////////////////////////// Source Code /////////////////////////////////////////////

private keyword UIViewInit

struct UIView extends array
    // Credits to Kazeon for UIUtils library, where this code was taken from.
    // https://www.hiveworkshop.com/threads/ui-utils-v1-05.320005/

    readonly static integer ResolutionWidth = 1920
    readonly static integer ResolutionHeight = 1080
    readonly static real maxX = 0.933
    readonly static real minX = -0.133
 
    readonly static framehandle ConsoleUIBackdrop
    readonly static framehandle LEFT_BORDER = null
    readonly static framehandle RIGHT_BORDER
 
    static constant method operator minY takes nothing returns real
        return 0.0
    endmethod
 
    static constant method operator maxY takes nothing returns real
        return 0.6
    endmethod
 
    static method operator PXTODPI takes nothing returns real
        return maxY/ResolutionHeight
    endmethod
 
    static method operator DPITOPX takes nothing returns real
        return ResolutionHeight/maxY
    endmethod
 
    static method operator width4by3 takes nothing returns real
        return (ResolutionWidth-ResolutionHeight/600.*800.)/2.
    endmethod
 
    static method getScreenPosX takes integer x returns real
        return (-UIView.width4by3+x)*UIView.PXTODPI
    endmethod
 
    static method getScreenPosY takes integer y returns real
        return y*UIView.PXTODPI
    endmethod
 
    static method onTimer takes nothing returns nothing
        local integer w = BlzGetLocalClientWidth()
        local integer h = BlzGetLocalClientHeight()
   
        if LEFT_BORDER == null then
            //! runtextmacro UIViewInitMacro("GetExpiredTimer()")
       
            // This is here just for the author of the system, because he was too lazy to change how his map worked.
            //! runtextmacro optional LoPUILibLoadTOCFiles()
        endif
 
        if ResolutionWidth != w or ResolutionHeight != h then
            set ResolutionWidth = w
            set ResolutionHeight = h
       
            set minX = getScreenPosX(0)
            set maxX = getScreenPosX(ResolutionWidth)
            call BlzFrameSetAbsPoint(LEFT_BORDER, FRAMEPOINT_TOPLEFT, minX, 0.6)
            call BlzFrameSetAbsPoint(RIGHT_BORDER, FRAMEPOINT_TOPRIGHT, maxX, 0.6)
            call BlzFrameSetSize(LEFT_BORDER, 0.001, 0.6)
            call BlzFrameSetSize(RIGHT_BORDER, 0.001, 0.6)
        endif
    endmethod
 
    implement UIViewInit
   
endstruct

private module UIViewInit
    //! textmacro UIViewInitMacro takes timer
        set ConsoleUIBackdrop = BlzGetFrameByName("ConsoleUIBackdrop", 0)
        set LEFT_BORDER = BlzCreateFrameByType("FRAME", "LeftBorderFrame", ConsoleUIBackdrop, "", 0)
        set RIGHT_BORDER = BlzCreateFrameByType("FRAME", "RightBorderFrame", ConsoleUIBackdrop, "", 0)
   
        call TimerStart($timer$, 0.5, true, function thistype.onTimer)
        set ResolutionWidth = 0
        set ResolutionHeight = 0
    //! endtextmacro

    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 0., false, function thistype.onTimer)
    endmethod
endmodule





endlibrary


Originally, I had two libraries here. One was the library below, which I was pretty sure was working, but apparently I was wrong. It used a FRAME created with FRAMEPOINT_TOPLEFT on LEFT_BORDER and FRAMEPOINT_BOTTOMRIGHT on RIGHT_BORDER called WorldFrame. However, it appears such a frame was blocking user input, just like button frames.

You can have a look at the library below. If you think you can implement it in a working way, feel free to do so.

IsMouseOnWorld currently uses a timer to check if a tooltip is visibile. Ideally, it should instead use a FRAMEEVENT_MOUSE_ENTER. However, all frames that can use this event will make it so the player can't actually click anywhere in the game world. If anyone figures out a way to write this library using frame events instead of a timer, please let me know.
JASS:
library IsMouseOnWorld requires WidescreenUI
/*
Problem:
When using the mouse natives, a lot of the time we don't want to do anything if the player was actually
clicking a UI element, not the game world.

Solution:
This library provides functions that determine if a player's mouse is on the game world.

------------------------------------------------------------------
                                API
------------------------------------------------------------------

// This function returns an async value.
function IsMouseOnWorld takes nothing returns boolean

// This function returns a synced value.
function IsPlayerMouseOnWorld takes player whichPlayer returns boolean

// When mouse is on the top menu bar, BlzGetPlayerMouseX/Y will return 0 for both coords.
// It's basically impossible for a player to actually have their mouse on point (0, 0).
// That means, if both x and y are 0, the mouse is probably on the top bar and not the world.
// Therefore, the following functions are defined, for QoL. They automatically filter out (0,0):

// async
function IsMouseOnWorldCoords takes real x, real y returns boolean

// synced
function IsPlayerMouseOnWorldCoords takes player whichPlayer, real x, real y returns boolean


------------------------------------------------------------------
                           Configuration
------------------------------------------------------------------
*/

public struct Config extends array

    // This should be a power of 2. (1/32, 1/64, 1/128, etc.)
    static method operator TIMER_PRECISION takes nothing returns real
        return 1.0 / 32.0
    endmethod

endstruct
/////////////////////////// Source Code ///////////////////////////////////////

globals
    private framehandle tooltip
    private framehandle dummyButtonOn
    private framehandle dummyButtonOut

    private boolean onWorld = false
    private boolean array onWorldSync
endglobals

// Filter out (0,0) coords (see documentation above)
private function ValidateCoords takes real x, real y returns boolean
    return x!=0 or y!=0
endfunction

// This returns an async value.
function IsMouseOnWorld takes nothing returns boolean
    return BlzFrameIsVisible(tooltip)
endfunction

// This returns an async value.
function IsMouseOnWorldCoords takes real x, real y returns boolean
    return ValidateCoords(x, y) and IsMouseOnWorld()
endfunction

function IsPlayerMouseOnWorld takes player whichPlayer returns boolean
    return onWorldSync[GetPlayerId(whichPlayer)]
endfunction

function IsPlayerMouseOnWorldCoords takes player whichPlayer, real x, real y returns boolean
    return IsPlayerMouseOnWorld(whichPlayer) and ValidateCoords(x, y)
endfunction


private function onButton takes nothing returns nothing
    set onWorldSync[GetPlayerId(GetTriggerPlayer())] = BlzGetTriggerFrame() == dummyButtonOn
endfunction

private function onTimer takes nothing returns nothing
    // Everything in this block is async. Do not alter the game state here.
    if BlzFrameIsVisible(tooltip) != onWorld then
        set onWorld = not onWorld
        if onWorld then
            call BlzFrameClick(dummyButtonOn)
        else
            call BlzFrameClick(dummyButtonOut)
        endif
    endif
endfunction

private function onStart takes nothing returns nothing
    local trigger trig = CreateTrigger()
    set tooltip = BlzCreateFrameByType("FRAME", "WorldFrameTooltip", UIView.WorldFrame,"", 0)

    call BlzFrameSetAbsPoint(tooltip, FRAMEPOINT_CENTER, 0.4,0.3)
    call BlzFrameSetTooltip(UIView.WorldFrame, tooltip)

    set dummyButtonOn = BlzCreateFrameByType("BUTTON", "DummyButton", UIView.WorldFrame,"", 0)
    set dummyButtonOut = BlzCreateFrameByType("BUTTON", "DummyButton", UIView.WorldFrame,"", 0)
    call BlzTriggerRegisterFrameEvent(trig, dummyButtonOn, FRAMEEVENT_CONTROL_CLICK)
    call BlzTriggerRegisterFrameEvent(trig, dummyButtonOut, FRAMEEVENT_CONTROL_CLICK)

    call TriggerAddAction(trig, function onButton)
    call TimerStart(GetExpiredTimer(), Config.TIMER_PRECISION, true, function onTimer)
endfunction

private module Init
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 0., false, function onStart)
    endmethod
endmodule
private struct InitStruct extends array
    implement Init
endstruct

endlibrary
 
Last edited:
Top