mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-22 17:18:39 +00:00
Add button, toggle, and option elements
This commit is contained in:
parent
c7fca1b956
commit
68612c6900
9 changed files with 407 additions and 0 deletions
106
builtin/ui/clickable_elems.lua
Normal file
106
builtin/ui/clickable_elems.lua
Normal file
|
@ -0,0 +1,106 @@
|
|||
-- Luanti
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
-- Copyright (C) 2024 v-rob, Vincent Robinson <robinsonvincent89@gmail.com>
|
||||
|
||||
ui.Button = ui._new_type(ui.Elem, "button", 0x02, true)
|
||||
|
||||
function ui.Button:_init(props)
|
||||
ui.Elem._init(self, props)
|
||||
|
||||
self._disabled = ui._opt(props.disabled, "boolean")
|
||||
self._on_press = ui._opt(props.on_press, "function")
|
||||
end
|
||||
|
||||
function ui.Button:_encode_fields()
|
||||
local fl = ui._make_flags()
|
||||
|
||||
ui._shift_flag(fl, self._disabled)
|
||||
ui._shift_flag(fl, self._on_press)
|
||||
|
||||
return ui._encode("SZ", ui.Elem._encode_fields(self), ui._encode_flags(fl))
|
||||
end
|
||||
|
||||
ui.Button._handlers[0x00] = function(self, ev, data)
|
||||
return self._on_press
|
||||
end
|
||||
|
||||
ui.Toggle = ui._new_type(ui.Elem, "toggle", 0x03, true)
|
||||
|
||||
ui.Check = ui.derive_elem(ui.Toggle, "check")
|
||||
ui.Switch = ui.derive_elem(ui.Toggle, "switch")
|
||||
|
||||
function ui.Toggle:_init(props)
|
||||
ui.Elem._init(self, props)
|
||||
|
||||
self._disabled = ui._opt(props.disabled, "boolean")
|
||||
self._selected = ui._opt(props.selected, "boolean")
|
||||
|
||||
self._on_press = ui._opt(props.on_press, "function")
|
||||
self._on_change = ui._opt(props.on_change, "function")
|
||||
end
|
||||
|
||||
function ui.Toggle:_encode_fields()
|
||||
local fl = ui._make_flags()
|
||||
|
||||
ui._shift_flag(fl, self._disabled)
|
||||
ui._shift_flag_bool(fl, self._selected)
|
||||
|
||||
ui._shift_flag(fl, self._on_press)
|
||||
ui._shift_flag(fl, self._on_change)
|
||||
|
||||
return ui._encode("SZ", ui.Elem._encode_fields(self), ui._encode_flags(fl))
|
||||
end
|
||||
|
||||
ui.Toggle._handlers[0x00] = function(self, ev, data)
|
||||
return self._on_press
|
||||
end
|
||||
|
||||
ui.Toggle._handlers[0x01] = function(self, ev, data)
|
||||
local selected = ui._decode("B", data)
|
||||
ev.selected = selected ~= 0
|
||||
|
||||
return self._on_change
|
||||
end
|
||||
|
||||
ui.Option = ui._new_type(ui.Elem, "option", 0x04, true)
|
||||
|
||||
ui.Radio = ui.derive_elem(ui.Option, "radio")
|
||||
|
||||
function ui.Option:_init(props)
|
||||
ui.Elem._init(self, props)
|
||||
|
||||
self._disabled = ui._opt(props.disabled, "boolean")
|
||||
self._selected = ui._opt(props.selected, "boolean")
|
||||
|
||||
self._family = ui._opt(props.family, "id")
|
||||
|
||||
self._on_press = ui._opt(props.on_press, "function")
|
||||
self._on_change = ui._opt(props.on_change, "function")
|
||||
end
|
||||
|
||||
function ui.Option:_encode_fields()
|
||||
local fl = ui._make_flags()
|
||||
|
||||
ui._shift_flag(fl, self._disabled)
|
||||
ui._shift_flag_bool(fl, self._selected)
|
||||
|
||||
if ui._shift_flag(fl, self._family) then
|
||||
ui._encode_flag(fl, "z", self._family)
|
||||
end
|
||||
|
||||
ui._shift_flag(fl, self._on_press)
|
||||
ui._shift_flag(fl, self._on_change)
|
||||
|
||||
return ui._encode("SZ", ui.Elem._encode_fields(self), ui._encode_flags(fl))
|
||||
end
|
||||
|
||||
ui.Option._handlers[0x00] = function(self, ev, data)
|
||||
return self._on_press
|
||||
end
|
||||
|
||||
ui.Option._handlers[0x01] = function(self, ev, data)
|
||||
local selected = ui._decode("B", data)
|
||||
ev.selected = selected ~= 0
|
||||
|
||||
return self._on_change
|
||||
end
|
|
@ -11,6 +11,7 @@ dofile(UI_PATH .. "selector.lua")
|
|||
dofile(UI_PATH .. "style.lua")
|
||||
dofile(UI_PATH .. "elem.lua")
|
||||
|
||||
dofile(UI_PATH .. "clickable_elems.lua")
|
||||
dofile(UI_PATH .. "static_elems.lua")
|
||||
|
||||
dofile(UI_PATH .. "window.lua")
|
||||
|
|
|
@ -246,6 +246,19 @@ func_preds["nth_last_match"] = function(str)
|
|||
end
|
||||
end
|
||||
|
||||
func_preds["family"] = function(family)
|
||||
if family == "*" then
|
||||
return function(elem)
|
||||
return result(elem._family ~= nil)
|
||||
end
|
||||
end
|
||||
|
||||
assert(ui.is_id(family), "Expected '*' or ID string for ?family()")
|
||||
return function(elem)
|
||||
return result(elem._family == family)
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_term(str, pred)
|
||||
str = str:trim()
|
||||
assert(str ~= "", "Expected selector term")
|
||||
|
|
|
@ -19,6 +19,10 @@ local prelude_theme = ui.Style {
|
|||
ui.Style "image" {
|
||||
icon_scale = 0,
|
||||
},
|
||||
ui.Style "check, switch, radio" {
|
||||
icon_place = "left",
|
||||
text_align = "left",
|
||||
},
|
||||
}
|
||||
|
||||
function ui.get_prelude_theme()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
set(ui_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/box.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clickable_elems.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/elem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/manager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/static_elems.cpp
|
||||
|
|
173
src/ui/clickable_elems.cpp
Normal file
173
src/ui/clickable_elems.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
// Luanti
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2024 v-rob, Vincent Robinson <robinsonvincent89@gmail.com>
|
||||
|
||||
#include "ui/clickable_elems.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "ui/manager.h"
|
||||
#include "util/serialize.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void Button::reset()
|
||||
{
|
||||
Elem::reset();
|
||||
|
||||
m_disabled = false;
|
||||
}
|
||||
|
||||
void Button::read(std::istream &is)
|
||||
{
|
||||
auto super = newIs(readStr32(is));
|
||||
Elem::read(super);
|
||||
|
||||
u32 set_mask = readU32(is);
|
||||
|
||||
m_disabled = testShift(set_mask);
|
||||
|
||||
if (testShift(set_mask))
|
||||
enableEvent(ON_PRESS);
|
||||
}
|
||||
|
||||
bool Button::processInput(const SDL_Event &event)
|
||||
{
|
||||
return getMain().processFullPress(event, UI_CALLBACK(onPress));
|
||||
}
|
||||
|
||||
void Button::onPress()
|
||||
{
|
||||
if (!m_disabled && testEvent(ON_PRESS)) {
|
||||
g_manager.sendMessage(createEvent(ON_PRESS).str());
|
||||
}
|
||||
}
|
||||
|
||||
void Toggle::reset()
|
||||
{
|
||||
Elem::reset();
|
||||
|
||||
m_disabled = false;
|
||||
}
|
||||
|
||||
void Toggle::read(std::istream &is)
|
||||
{
|
||||
auto super = newIs(readStr32(is));
|
||||
Elem::read(super);
|
||||
|
||||
u32 set_mask = readU32(is);
|
||||
|
||||
m_disabled = testShift(set_mask);
|
||||
testShiftBool(set_mask, m_selected);
|
||||
|
||||
if (testShift(set_mask))
|
||||
enableEvent(ON_PRESS);
|
||||
if (testShift(set_mask))
|
||||
enableEvent(ON_CHANGE);
|
||||
}
|
||||
|
||||
bool Toggle::processInput(const SDL_Event &event)
|
||||
{
|
||||
return getMain().processFullPress(event, UI_CALLBACK(onPress));
|
||||
}
|
||||
|
||||
void Toggle::onPress()
|
||||
{
|
||||
if (m_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_selected = !m_selected;
|
||||
|
||||
// Send both a press and a change event since both occurred.
|
||||
if (testEvent(ON_PRESS)) {
|
||||
g_manager.sendMessage(createEvent(ON_PRESS).str());
|
||||
}
|
||||
if (testEvent(ON_CHANGE)) {
|
||||
auto os = createEvent(ON_CHANGE);
|
||||
writeU8(os, m_selected);
|
||||
|
||||
g_manager.sendMessage(os.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Option::reset()
|
||||
{
|
||||
Elem::reset();
|
||||
|
||||
m_disabled = false;
|
||||
m_family.clear();
|
||||
}
|
||||
|
||||
void Option::read(std::istream &is)
|
||||
{
|
||||
auto super = newIs(readStr32(is));
|
||||
Elem::read(super);
|
||||
|
||||
u32 set_mask = readU32(is);
|
||||
|
||||
m_disabled = testShift(set_mask);
|
||||
testShiftBool(set_mask, m_selected);
|
||||
|
||||
if (testShift(set_mask))
|
||||
m_family = readNullStr(is);
|
||||
|
||||
if (testShift(set_mask))
|
||||
enableEvent(ON_PRESS);
|
||||
if (testShift(set_mask))
|
||||
enableEvent(ON_CHANGE);
|
||||
}
|
||||
|
||||
bool Option::processInput(const SDL_Event &event)
|
||||
{
|
||||
return getMain().processFullPress(event, UI_CALLBACK(onPress));
|
||||
}
|
||||
|
||||
void Option::onPress()
|
||||
{
|
||||
if (m_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a press event for this pressed option button.
|
||||
if (testEvent(ON_PRESS)) {
|
||||
g_manager.sendMessage(createEvent(ON_PRESS).str());
|
||||
}
|
||||
|
||||
// Select this option button unconditionally before deselecting the
|
||||
// others in the family.
|
||||
onChange(true);
|
||||
|
||||
// If this option button has no family, then don't do anything else
|
||||
// since there may be other buttons with the same empty family string.
|
||||
if (m_family.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we find any other option buttons in this family, deselect them.
|
||||
for (Elem *elem : getWindow().getElems()) {
|
||||
if (elem->getType() != getType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Option *option = (Option *)elem;
|
||||
if (option->m_family == m_family && option != this) {
|
||||
option->onChange(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Option::onChange(bool selected)
|
||||
{
|
||||
bool was_selected = m_selected;
|
||||
m_selected = selected;
|
||||
|
||||
// If the state of the option button changed, send a change event.
|
||||
if (was_selected != m_selected && testEvent(ON_CHANGE)) {
|
||||
auto os = createEvent(ON_CHANGE);
|
||||
writeU8(os, m_selected);
|
||||
|
||||
g_manager.sendMessage(os.str());
|
||||
}
|
||||
}
|
||||
}
|
102
src/ui/clickable_elems.h
Normal file
102
src/ui/clickable_elems.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Luanti
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2024 v-rob, Vincent Robinson <robinsonvincent89@gmail.com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui/box.h"
|
||||
#include "ui/elem.h"
|
||||
#include "ui/helpers.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class Button : public Elem
|
||||
{
|
||||
private:
|
||||
// Serialized constants; do not change values of entries.
|
||||
static constexpr u32 ON_PRESS = 0x00;
|
||||
|
||||
bool m_disabled;
|
||||
|
||||
public:
|
||||
Button(Window &window, std::string id) :
|
||||
Elem(window, std::move(id))
|
||||
{}
|
||||
|
||||
virtual Type getType() const override { return BUTTON; }
|
||||
|
||||
virtual void reset() override;
|
||||
virtual void read(std::istream &is) override;
|
||||
|
||||
virtual bool isBoxDisabled(const Box &box) const override { return m_disabled; }
|
||||
|
||||
virtual bool processInput(const SDL_Event &event) override;
|
||||
|
||||
private:
|
||||
void onPress();
|
||||
};
|
||||
|
||||
class Toggle : public Elem
|
||||
{
|
||||
private:
|
||||
// Serialized constants; do not change values of entries.
|
||||
static constexpr u32 ON_PRESS = 0x00;
|
||||
static constexpr u32 ON_CHANGE = 0x01;
|
||||
|
||||
bool m_disabled;
|
||||
bool m_selected = false; // Persistent
|
||||
|
||||
public:
|
||||
Toggle(Window &window, std::string id) :
|
||||
Elem(window, std::move(id))
|
||||
{}
|
||||
|
||||
virtual Type getType() const override { return TOGGLE; }
|
||||
|
||||
virtual void reset() override;
|
||||
virtual void read(std::istream &is) override;
|
||||
|
||||
virtual bool isBoxSelected(const Box &box) const override { return m_selected; }
|
||||
virtual bool isBoxDisabled(const Box &box) const override { return m_disabled; }
|
||||
|
||||
virtual bool processInput(const SDL_Event &event) override;
|
||||
|
||||
private:
|
||||
void onPress();
|
||||
};
|
||||
|
||||
class Option : public Elem
|
||||
{
|
||||
private:
|
||||
// Serialized constants; do not change values of entries.
|
||||
static constexpr u32 ON_PRESS = 0x00;
|
||||
static constexpr u32 ON_CHANGE = 0x01;
|
||||
|
||||
bool m_disabled;
|
||||
std::string m_family;
|
||||
|
||||
bool m_selected = false; // Persistent
|
||||
|
||||
public:
|
||||
Option(Window &window, std::string id) :
|
||||
Elem(window, std::move(id))
|
||||
{}
|
||||
|
||||
virtual Type getType() const override { return OPTION; }
|
||||
|
||||
virtual void reset() override;
|
||||
virtual void read(std::istream &is) override;
|
||||
|
||||
virtual bool isBoxSelected(const Box &box) const override { return m_selected; }
|
||||
virtual bool isBoxDisabled(const Box &box) const override { return m_disabled; }
|
||||
|
||||
virtual bool processInput(const SDL_Event &event) override;
|
||||
|
||||
private:
|
||||
void onPress();
|
||||
void onChange(bool selected);
|
||||
};
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
#include "util/serialize.h"
|
||||
|
||||
// Include every element header for Elem::create()
|
||||
#include "ui/clickable_elems.h"
|
||||
#include "ui/static_elems.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
@ -29,6 +30,9 @@ namespace ui
|
|||
switch (type) {
|
||||
CREATE(ELEM, Elem);
|
||||
CREATE(ROOT, Root);
|
||||
CREATE(BUTTON, Button);
|
||||
CREATE(TOGGLE, Toggle);
|
||||
CREATE(OPTION, Option);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace ui
|
|||
{
|
||||
ELEM = 0x00,
|
||||
ROOT = 0x01,
|
||||
BUTTON = 0x02,
|
||||
TOGGLE = 0x03,
|
||||
OPTION = 0x04,
|
||||
};
|
||||
|
||||
// The main box is always the zeroth item in the Box::NO_GROUP group.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue