Skip to content

Implement SMODS.GUI.scrollbar and SMODS.GUI.dropdown_select#1253

Open
ThunderEdge73 wants to merge 16 commits intoSteamodded:mainfrom
ThunderEdge73:main
Open

Implement SMODS.GUI.scrollbar and SMODS.GUI.dropdown_select#1253
ThunderEdge73 wants to merge 16 commits intoSteamodded:mainfrom
ThunderEdge73:main

Conversation

@ThunderEdge73
Copy link

@ThunderEdge73 ThunderEdge73 commented Mar 9, 2026

SMODS.GUI.scrollbar and SMODS.GUI.dropdown_select

This PR adds SMODS.GUI.scrollbar(args), a function that returns a UI node that can be scrolled. It can optionally be linked to an SMODS.UIScrollBox in args.scroll_collision_obj to automatically handle scrolling that object. Otherwise, it will update args.ref_table[args.ref_value] based on the scrollbar's progress. Finer control over the specific UI node type returned is possible via args.ui_type. This also adds SMODS.GUI.dropdown_select, a cleaner way to select an option from a list.

Why not use create_slider instead of SMODS.GUI.scrollbar?

create_slider creates a slider that also has a numeric display next to it by default, whereas SMODS.GUI.scrollbar does not. Furthermore, create_slider does not support mouse scrollwheel inputs, but SMODS.GUI.scrollbar does. SMODS.GUI.scrollbar also has a knob to indicate more clearly that it can be dragged and scrolled, much like how it is on websites.

Examples

For SMODS.GUI.scrollbar(args):

-- Copied from my personal mod, Multiverse
function Multiverse.credits_tab_definition()
	local rows = {}
	local contributor_text = DynaText({
		string = localize("mul_contributors"),
		colours = { G.C.UI.TEXT_LIGHT },
		shadow = true,
		float = true,
		silent = true,
		spacing = 5,
		scale = 1,
		rotate = true,
		pop_in = 0,
		text_effect = "mul_ui_multiverse_highlight",
	})

	rows[#rows + 1] = {
		n = G.UIT.R,
		config = { align = "cm", padding = 0.1 },
		nodes = {
			{
				n = G.UIT.O,
				config = {
					object = contributor_text,
				},
			},
		},
	}
	for _, entry in ipairs(Multiverse.credits_table) do
		if entry == "MISC_CREDITS" then
			local inspiration_text = DynaText({
				string = localize("mul_inspirations"),
				colours = { G.C.UI.TEXT_LIGHT },
				shadow = true,
				float = true,
				silent = true,
				spacing = 5,
				scale = 1,
				rotate = true,
				pop_in = 0,
				text_effect = "mul_ui_multiverse_highlight",
			})
			rows[#rows + 1] = {
				n = G.UIT.R,
				config = { align = "cm", padding = 0.1 },
				nodes = {
					{
						n = G.UIT.O,
						config = {
							object = inspiration_text,
						},
					},
				},
			}
			rows[#rows + 1] = {
				n = G.UIT.R,
				config = { align = "cm" },
				nodes = {
					{
						n = G.UIT.B,
						config = { w = 1, h = 0.1 },
					},
				},
			}
			local desc_nodes = {}
			localize({
				type = "other",
				key = "mul_misc_credits",
				nodes = desc_nodes,
				vars = {
					colours = {
						darken(HEX("8dffa8"), 0.2),
						HEX("F4A6C7"),
						HEX("800080"),
						HEX("FE0001"),
						HEX("4d1575"),
						HEX("7E7AFF"),
						HEX("ff8c8c"),
						HEX("fd9712"),
						HEX("f51bbc"),
						HEX("7a2eb6"),
						HEX("8b61ad"),
					},
				},
				scale = 1.075,
			})
			credits_rows = {}
			for _, v in ipairs(desc_nodes) do
				credits_rows[#credits_rows + 1] = { n = G.UIT.R, config = { align = "cl" }, nodes = v }
			end
			rows[#rows + 1] = {
				n = G.UIT.R,
				config = { align = "cm" },
				nodes = {
					{
						n = G.UIT.R,
						config = { align = "cm", r = 0.1, colour = G.C.WHITE, padding = 0.1 },
						nodes = {
							{ n = G.UIT.C, config = { align = "cm", padding = 0.05 }, nodes = credits_rows },
						},
					},
				},
			}
			rows[#rows + 1] = {
				n = G.UIT.R,
				config = { align = "cm" },
				nodes = {
					{
						n = G.UIT.B,
						config = { w = 1, h = 0.05 },
					},
				},
			}
		else
			rows[#rows + 1] = Multiverse.generate_credits_desc_nodes(entry)
		end
	end
	local scrollbox = SMODS.UIScrollBox({
		content = {
			definition = {
				n = G.UIT.ROOT,
				config = { colour = G.C.BLACK },
				nodes = {
					{
						n = G.UIT.C,
						config = { align = "cm", padding = 0.1 },
						nodes = rows,
					},
				},
			},
			config = { align = "cm" },
		},
		overflow = {
			node_config = {
				maxh = 6,
				r = 0.1,
			},
		},
	})
	return {
		n = G.UIT.ROOT,
		config = { align = "cm", colour = G.C.BLACK, padding = 0.1 },
		nodes = {
			{
				n = G.UIT.C,
				config = { align = "cm", colour = G.C.L_BLACK, padding = 0.1, r = 0.1, emboss = 0.05 },
				nodes = {
					{
						n = G.UIT.O,
						config = {
							align = "cm",
							object = scrollbox,
						},
					},
				},
			},
			{
				n = G.UIT.C,
				config = { align = "cm" },
				nodes = {
					SMODS.GUI.scrollbar({
						h = 6,
						w = 0.3,
						max = 1,
						min = 0,
						colour = Multiverse.C.TRANSMUTED_GRADIENT_SLOW,
						bg_colour = { 0, 0, 0, 0.15 },
						scroll_collision_obj = scrollbox,
						knob_h = 0.6,
						scroll_mult = 1.2,
					}),
				},
			},
		},
	}
end
2026-03-11_19-10-40.mp4

For SMODS.GUI.dropdown_select(args):

-- Again, copied from my personal mod, Multiverse
Multiverse.test_ui_def = function()
	return {
		n = G.UIT.ROOT,
		config = { colour = G.C.BLACK, r = 0.1, minw = 4, minh = 4, padding = 0.1, align = "cm" },
		nodes = {
			SMODS.GUI.dropdown_select({
				options = {
					"opt 1",
					"opt 2",
					"opt 3",
					"opt 4",
					"opt 5",
					"opt 6",
					"opt 7",
				},
				init_value = "opt 1",
				ref_table = Multiverse,
				ref_value = "test_dropdown",
				minw = 2,
				no_unselect = true,
				align = "cl",
			}),
			{
				n = G.UIT.R,
				config = {},
				nodes = {
					{
						n = G.UIT.B,
						config = { h = 3, w = 1 }
					}
				}
			},
			SMODS.GUI.dropdown_select({
				options = {
					"opt 1",
					"opt 2",
					"opt 3",
					"opt 4",
					"opt 5",
					"opt 6",
					"opt 7",
				},
				ref_table = Multiverse,
				ref_value = "test_dropdown2",
				minw = 3,
				align = "cr",
				is_option_disabled = function(o)
					return o == "opt 2"
				end,
				default = "None",
				callback = "testing",
				colour = G.C.BLUE,
				max_menu_h = 3,
				border_colour = G.C.BLUE,
				dropdown_bg_colour = G.C.BLACK,
				selected_colour = lighten(G.C.BLACK, 0.1),
			})
		},
	}
end

function G.FUNCS.testing(e)
	print("Pressed: " .. e.config.value)
end

SMODS.current_mod.extra_tabs = function()
	return {
		{
			label = "Music",
			tab_definition_function = function()
				return Multiverse.music_tab() -- irrelevant
			end,
		},
		{
			label = "Test",
			tab_definition_function = function()
				return Multiverse.test_ui_def()
			end,
		},
	}
end
2026-03-11_19-10-54.mp4

Additional Info:

  • I didn't modify api's or I've made a PR to the wiki repo.
  • I didn't modify api's or I've updated lsp definitions.
  • I didn't make new lovely files or all new lovely files have appropriate priority.

@ThunderEdge73

This comment was marked as outdated.

@ThunderEdge73 ThunderEdge73 marked this pull request as draft March 10, 2026 17:43
@ThunderEdge73
Copy link
Author

I need to polish this some more

@ThunderEdge73 ThunderEdge73 changed the title Implement SMODS.GUI.scrollbar Implement SMODS.GUI.scrollbar and SMODS.GUI.dropdown_select Mar 12, 2026
@ThunderEdge73 ThunderEdge73 marked this pull request as ready for review March 12, 2026 00:13
@ThunderEdge73
Copy link
Author

Now I feel confident enough in the contents of this PR, feel free to let me know if anything needs to be changed

@nh6574
Copy link
Member

nh6574 commented Mar 12, 2026

Amazing work! Tested it and everything works very smooth.

Some small suggestions for options that came up when testing:

  • An option to close the dropdown automatically when an option is selected would be helpful. In my test I did it by finding the node and calling the toggle function manually on it.
  • Speaking of finding the node, it is hard without being able to define an ID. args.id would be nice.
  • An option to allow the text for the options like args.align for the main box would also be useful.

@ThunderEdge73
Copy link
Author

ThunderEdge73 commented Mar 12, 2026

Amazing work! Tested it and everything works very smooth.

Some small suggestions for options that came up when testing:

  • An option to close the dropdown automatically when an option is selected would be helpful. In my test I did it by finding the node and calling the toggle function manually on it.
  • Speaking of finding the node, it is hard without being able to define an ID. args.id would be nice.
  • An option to allow the text for the options like args.align for the main box would also be useful.

I just addressed these three things.

args.id will assign the given id value to this UIBox, which represents the button itself. If you wanted this id to be set somewhere else, let me know.
image

args.close_on_select will automatically handle closing the dropdown when an option is selected if this is set to true - no need to handle this manually now.

The text alignment options have been added - args.option_align will align the text of the options.

@ThunderEdge73
Copy link
Author

Also, I was trying to adjust the math for the scrolling velocity, but the math wasn't cooperating with me tonight - I'll address the scrolling tomorrow, when I am less tired.

@ThunderEdge73
Copy link
Author

The scrolling stuff should be aok now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants