-
Notifications
You must be signed in to change notification settings - Fork 463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Working POC for buttons/buttons groups, PR up for commentary #3441
Conversation
@myk002 idk if it's possible but it'd be cool if we could just pass in the png file path for the sprites in lua and have it dynamically load up the texpos values, so you don't have to hardcode in the stuff in the cpp files as seen in this change set, is that a thing we could theoretically do? |
Textures can be dynamically loaded, but their texpos values cannot be cached in Lua since the dynamic textures will be unloaded when the world unloads (or maybe it's when the next world loads). We'd have to write code to invalidate the texpos values stored in cpp as well. |
This seems like a pretty infrequent occurrence, what would be needed to deal with it? |
So the background here is that the "texpos" value that the rendering system works with is an index into a DF array (std::vector). we can add textures to that array at any time and then use the index to render the tiles. the lifecycle of the textures in that array is determined by the value of when DFHack starts, it add textures to the end of the vector and increments This means that if a DFHack tool uses a dynamic texture, it must look up the texpos value every time it runs (which is not a terrible thing). The Textures module can likely handle dynamic reloading. It will definitely know when a world is unloaded (since we hook viewscreen changes and check for it) so it can invalidate textures properly. |
By the way, I am not attached to the current implementation of the Textures module. It was written quick and dirty to satisfy a need during the chaotic time of v50 migration. If you come up with a better replacement architecture/API, I will be nothing but pleased. |
more thoughts: "dynamic" is relative. we can create all the variations of a sprite sheet at load time and get them in at the same time as the other textures. this allows them to have the same lifecycle as all the other DFHack textures. this is "permanent" as long as DFHack itself is loaded before a world is loaded, which is currently always true, but may not always be the case, so it's still a good idea for scripts to look up the texpos values on each run. anyway, there's no reason why we need to worry about non-contiguous texpos values like I was fearing above. |
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 suggest that you take a look at TabBar
and Tab
to see if there are elements/strategies that you can adopt.
self:addviews { | ||
Label { | ||
-- view_id = "button_but", | ||
frame = { t = y, l = 0 }, | ||
text = tiles, | ||
on_click = function() | ||
if self.on_click_callback then self.on_click_callback({ group_index = self.group_index }) end | ||
self.selected = not self.selected | ||
self.tiles = nil | ||
self:updateLayout() | ||
end | ||
} | ||
} |
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.
Why multiple Labels instead of a single Label with multiple lines of tiles?
also note for later, once we get the tile variations in, that Label tokens now support a htile
field for the tile to use when the Label is hovered. That will work much better if the entire button is a single Label
@@ -2433,4 +2433,131 @@ function RangeSlider:onRenderBody(dc, rect) | |||
end | |||
end | |||
|
|||
GraphicButton = defclass(GraphicButton, ResizingPanel) | |||
GraphicButton.ATTRS { | |||
tiles = DEFAULT_NIL, |
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.
if this is not expected to be set by the parent, it shouldn't go into ATTRS
. it can just be a regular self.
field
height = 0, | ||
width = 0, | ||
graphics = '', | ||
graphic_file_dims = { x = 0, y = 0 }, | ||
pos = 0, |
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.
for parent-specified required values, it's better to default to DEFAULT_NIL
and then validate in init
self.selected = not self.selected | ||
self.tiles = nil | ||
self:updateLayout() |
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.
instead of re-initializing when the selected state changes, how about having two Label
s with visible
properties that are controlled by self.selected
? that would allow all import
ing to happen in init
, with display state handled by existing widget logic
function ButtonGroup:on_click(args) | ||
if self.type == 'radio' then | ||
for i, _ in ipairs(self.buttons) do | ||
self.buttons[i].selected = false |
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.
this is the opposite choice that TabBar
made, which keeps the selected state in the TabBar
and the individual Tab
s query the TabBar
for "am I selected"?
I don't have a strong preference for which approach we take, but TabBar
and ButtonGroup
should be using the same architecture.
Actually, TabBar
should be implemented as a radio-style ButtonGroup
, and Tab
s are just GraphicButton
s with text also on them. Perhaps that's a good use case to make sure that your implementation here is generalized enough.
Thanks for the review, i'm hoping i'll be able to make more progress this weekend, nervous to start touching stuff like the tabs since i don't want to break everything, but i'll give it a shot |
TabBar is only used by gui/control-panel and gui/launcher. if we make breaking changes, there won't be too much to update. |
Good to know, still working on this just slowly |
I made some crutch for similar case as a POC some time ago. During creating dynamic textures for chars (via sdl ttf) i needed to know, when i can use those created textures (cause as you said, game resetting caches), and when i needed to recreate new one. Well, i had made sort of game state tracker. Hooked into functions to render Main Menu, Loading New World and so. With help of that i can set callbacks to recreate textures. |
I'm working on bringing this to life vidcap-2024-05-18_17.54.13.mp4 |
Been working on buttons, this is the first working PoC. It allows wrapping a
CycleHotKeyLabel
in the newButtonGroup
class.The
GraphicButton
andButtonGroup
classes will automatically load texpos graphics from sprite files and stitch them together to draw buttons. Some C++ is required to get the graphics loaded.In the Lua, you need to pass in the name of the graphics (maps with the c++ code, which loads the texpos), and
file_dims
which is how many buttons there are and how many rows, so the following files arefile_dims = { x = 7, y = 1 },
andfile_dims = { x = 6, y = 1 },
respectively.It's also necessary to include the
button_dims
, which tell the classes what dimensions of texpos characters the buttons are, so for example the above icons are 4x3.Eventually maybe we can come up with a way to automatically determine these things...
NOTE: There's work in progress to add more button states like hover, pressing, etc... so this will be expanded as work moves forward.
The
ButtonGroup
class will automatically keep the hotkey label and buttons in sync, and both controls will work. Thehotkey_controller
field is just the original widget, so it should be easy to upgrade existing controls and add graphics likes this.Here's an example from
gui/design
This code creates the following example: