- 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.
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: