-
Notifications
You must be signed in to change notification settings - Fork 61
New Feature/Toolset: Paradox Bar! #514
base: master
Are you sure you want to change the base?
Changes from 2 commits
7715c7e
31c2c0d
28e57a2
b83aa1b
1d94829
f0613f7
5414202
ae9bc46
59883e1
481dca5
ed2eaf3
247a3ee
76845de
f2b471d
eedab43
a9887d6
d6c877c
010da23
ccb61de
b957570
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,115 @@ | ||
| --This is a layer that lives at the very top of the UI. It's an excellent place for catching inputs globally or displaying a custom overlay | ||
| -- This layer is the home of the paradox bar | ||
| include("InstanceManager"); | ||
|
|
||
| --Members | ||
| local groupStackIM = InstanceManager:new("ParadoxBarGroupInstance", "Top", Controls.ParadoxBarStack); -- IM for the groups for the notifications | ||
| local groupStackInstances: table = {}; --Instances of the groupings for the notifications | ||
| local groupStacks: table = {}; --Instance managers for the individual notifications | ||
|
|
||
| --These following tables define the various behaviors a notification can contain, modifying a given effect will affect ALL notifications which use them. | ||
| --Feel free to add new behaviors here as necessary | ||
|
|
||
| --Paradoxbar sound | ||
| local paradoxBarSound = { | ||
| ["addSound"] = function() UI.PlaySound("NOTIFICATION_MISC_NEUTRAL"); end, | ||
| ["removeSound"] = function() UI.PlaySound("Map_Pin_Remove"); end | ||
| } | ||
|
|
||
| --Paradoxbar functions | ||
| local paradoxBarFuncs = { | ||
| ["deleteOnRMB"] = function(instance, group) | ||
| instance.Button:RegisterCallback(Mouse.eRClick, function() RemoveNotification(instance, group); end); | ||
| end, | ||
| ["debugPrint"] = function() print("Debug tooltip created"); end | ||
| } | ||
|
|
||
| --Paradoxbar behavior bundles | ||
| local paradoxBarBundles = { | ||
| ["standard"] = function(instance, group) paradoxBarFuncs["deleteOnRMB"](instance, group); paradoxBarSound["addSound"](); end | ||
| } | ||
|
|
||
| --These are the default notification templates | ||
| --Feel free to add to this as necessary | ||
|
|
||
| --Paradoxbar stock data | ||
| local paradoxBarStock = { | ||
| ["debug"] = {"Debug", "Controls_Circle", "This is a debug tooltip", "Dbg", {paradoxBarFuncs["debugPrint"], paradoxBarBundles["standard"]}}, | ||
| ["debug2"] = {"Debug2", "Controls_Circle", "This is a debug tooltip", "Dbg2", {paradoxBarFuncs["debugPrint"], paradoxBarBundles["standard"]}} | ||
| }; | ||
|
|
||
| -- This function handles adding new notifications to the paradox bar | ||
| -- Group is used for the purposes of chunking notifications together and is mandatory | ||
| -- If an ID is supplied, paradoxBarStock is checked for a matching ID and loads presets if available. If additional values are supplied, they are used to overwrite the default values | ||
| -- text is drawn directly on top of the icon and is meant to be used lightly. It will look ugly if you use any more than a few characters | ||
| -- funcs is an array of functions used for adding behavior to the notification. This is where closing behavior and other intricacies are defined | ||
| function AddNotification(ID, group, icon, tooltip, text, funcs) | ||
| local defaults = paradoxBarStock[ID]; | ||
| -- Group must be defined somehow or else we give up here | ||
| if(not group) then | ||
| group = defaults[1]; | ||
| if(not group) then return; end | ||
| end | ||
|
|
||
| --Creates a group for a specific notification group if it does not exist | ||
| if(groupStackInstances[group] == nil) then | ||
| groupStackInstances[group] = groupStackIM:GetInstance(); | ||
| groupStacks[group] = InstanceManager:new("ParadoxBarInstance", "Top", groupStackInstances[group].Stack); | ||
| end | ||
| instance = groupStacks[group]:GetInstance(); | ||
|
|
||
| --Applies defaults where appropriate | ||
| if(defaults) then | ||
| if(defaults[2]) then | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A small thing and I'm not sure how expensive these UI functions are (I guess it does nothing until a new render pass is done) But I'm not a fan of calling a function that will be called again just to overwrite the last call. Here we already know if icon is passed so maybe change the check to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't called every render pass, this function is meant to be called once each time a new notification event is thrown. As for the UI functions starting at line 55, these only get called once when a new notification group is being created. These are relatively heavy functions, but no more potential for a performance hit compared to what you'd see when multiple vanilla notifications are created.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Concerning your second point, I'm struggling to figure out what you mean, could you elaborate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. About the second point: if you call AddNotification with an icon and the ID have defaults you will call
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose that is somewhat inefficient. Alright, I'll patch that up |
||
| instance.Icon:SetTexture(defaults[2]); | ||
| end | ||
| if(defaults[3]) then | ||
| instance.Button:LocalizeAndSetToolTip(defaults[3]); | ||
| end | ||
| if(defaults[4]) then | ||
| instance.Text:SetText(defaults[4]); | ||
| end | ||
| if(defaults[5] and not funcs) then | ||
| funcs = defaults[5] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will always overwrite the passed funcs if there is a default. It might be what you are after but it does not follow the pattern that the rest of the function parameters are following in that passed parameters is used instead of default.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this behavior is different because we want overriding a template to allow full control of the notification. In the scenario where we merely append new functions to the list, it would only be possible to extend a given template's behavior, as opposed to completely manipulate it. This comes with the consequence of being more verbose, but I feel more control is better if you're in a situation requiring that you create a custom notification with one-off behavior in the first place There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I follow what you mean. Is the idea that calls to AddNotification only should pass funcs if it is a one off, ie if the passed ID does not have defaults?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what I mean, yes. If you plan to be using a specific type of notification more often than just once, it is advisable to compose a template first. That being said, that's why these behavior bundles are included, so that function composition is more convenient and idiomatic for the purposes of composing those one-offs. |
||
| end | ||
| end | ||
| if(icon) then | ||
| instance.Icon:SetTexture(icon); | ||
| end | ||
| if(tooltip) then | ||
| instance.Button:LocalizeAndSetToolTip(tooltip); | ||
| end | ||
| if(text) then | ||
| instance.Text:SetText(text); | ||
| end | ||
| --Invokes functions | ||
| if(funcs) then | ||
| for _,v in ipairs(funcs) do | ||
| v(instance, group, icon, tooltip, text); | ||
| end | ||
| end | ||
| --Reveals completed notification | ||
| instance.Top:SetHide(false); | ||
| end | ||
|
|
||
| function RemoveNotification(instance, group) | ||
| instance.SlideAnimation:Reverse(); | ||
| instance.SlideAnimation:RegisterEndCallback(function() | ||
| groupStacks[group]:ReleaseInstance(instance); | ||
| --This is a hacky workaround since releasing an instance doesn't necessarily actually remove it from a given stack | ||
| instance.Top:SetID("Exhausted"); | ||
| for _,v in ipairs(groupStackInstances[group].Stack:GetChildren()) do | ||
| if(v:GetID() ~= "Exhausted") then return; end | ||
| end | ||
| --Removes the group stack from the main notification stack | ||
| Controls.ParadoxBarStack:ReleaseChild(groupStackInstances[group].Top); | ||
| groupStackInstances[group] = nil; | ||
| end) | ||
| end | ||
|
|
||
| function Initialize() | ||
| groupStackIM:ResetInstances(); | ||
| LuaEvents.CQUI_AddNotification.Add(AddNotification); | ||
| ContextPtr:SetHide(false); | ||
| end | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,56 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <!-- This context is as high up on the z-axis as possible, which means it is excellent for handling mouse inputs globally and for elements that always need to appear above other things --> | ||
| <Context Name="CQUI_TopLayer"> | ||
| <Container ID="Frame" Size="Full,Full"> | ||
| <!-- Add an element as child of this container to place it free-floating --> | ||
|
|
||
| <!-- Add an element as child of this container to place it relative to where the vanilla controls reside. Set Alpha to 0.5 to visualize standins --> | ||
| <Container ID="Frame" Size="Full,Full"> | ||
| <!-- Positional mockup of the top half of the UI--> | ||
| <Stack ID="StandinStackTop" Anchor="C,T" StackGrowth="Down"> | ||
| <!-- Standin for the topbar area --> | ||
| <Box Anchor="C,T" Size="Full, 30" Color="Green" Alpha="0"/> | ||
| <Container ID="UnderTopBarArea" Anchor="C,T" Size="Full, 0" AutoSize="V"> | ||
| <Stack Anchor="L,T" StackGrowth="Right"> | ||
| <!-- Standin for the worldtracker --> | ||
| <Box Anchor="L,T" Size="370,270" Color="Red" Alpha="0"/> | ||
| <!-- CQUI paradox bar --> | ||
| <Stack ID="ParadoxBarStack" Anchor="L,T" StackGrowth="Right" StackPadding="-4"/> | ||
| </Stack> | ||
| <!-- Standin for the right button panel and diplo ribbon (when considerably full)--> | ||
| <Box Anchor="R,T" Size="650,140" Color="Beige" Alpha="0"/> | ||
| </Container> | ||
| </Stack> | ||
| <!-- Positional mockup of the bottom half of the UI --> | ||
| <Stack ID="StandinStackBottom" Anchor="C,B" StackGrowth="Up"> | ||
| <Container ID="BottomArea" Anchor="C,B" Size="Full,0" AutoSize="V"> | ||
| <!-- Standin for the minimap at maximum size --> | ||
| <Box Anchor="L,B" Size="780, 420" Color="Green" Alpha="0"/> | ||
| <Stack ID="StandinStackRight" Anchor="R,B" StackGrowth="Up"> | ||
| <!-- Standin for the actionpanel/unitpanel area --> | ||
| <Box Anchor="R,B" Size="560, 220" Color="Red" Alpha="0"/> | ||
| <!-- Standin for the notification area --> | ||
| <Box Anchor="R,B" Size="500,parent-400" Color="Gold" Alpha="0"/> | ||
| </Stack> | ||
| </Container> | ||
| </Stack> | ||
| </Container> | ||
|
|
||
| <Instance Name="ParadoxBarInstance"> | ||
| <Container ID="Top" AutoSize="1"> | ||
| <SlideAnim ID="SlideAnimation" Size="35,35" Offset="0,0" Begin="0,-10" EndOffset="0,20" Cycle="Once" Function="OutQuad" Speed="7"> | ||
| <Button ID="Button" Size="40,40" Tooltip=""> | ||
| <Image Texture="Controls_CircleBacking45" Size="35,35" Anchor="C,C" Offset="0,1" StretchMode="Fill" Sampler="Linear"/> | ||
| <Image ID="Icon" Anchor="C,C" Size="35,35" Texture="" StretchMode="Fill" Sampler="Linear" /> | ||
| <Label ID="Text" Anchor="C,C" Style="FontFlair14" Color="Green" FontStyle="Glow" String="Blah" Offset="0,1"/> | ||
| </Button> | ||
| </SlideAnim> | ||
| </Container> | ||
| </Instance> | ||
| <Instance Name="ParadoxBarGroupInstance"> | ||
| <Container ID="Top" AutoSize="1"> | ||
| <Grid Style="EnhancedToolTip" Size="55,40" Color="255,200,100,255" InnerPadding="10,5" AutoSize="V"> | ||
| <Stack ID="Stack" Offset="2,-7" Anchor="L,T" StackGrowth="Down" StackPadding="-7"/> | ||
| </Grid> | ||
| </Container> | ||
| </Instance> | ||
| </Context> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this should be done with key/value pairs instead of indexes. It is much easier to get the meaning when you read the code further down:
instance.Text:SetText(defaults[4]);vsinstance.Text:SetText(defaults.Text);And it makes it safer to not mess it up when adding new properties to the table/array, if you add a new value before the text then you have change all usages of defaults[4] to defaults[5].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be implementing this suggestion now, thanks for the heads up 👍