Skip to content

Commit 54e1282

Browse files
committed
Add button and scrollbar element families
1 parent 2c232fe commit 54e1282

File tree

6 files changed

+288
-0
lines changed

6 files changed

+288
-0
lines changed

builtin/ui/elem_defs.lua

+102
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,105 @@ function ui.Grid:_encode_fields()
107107

108108
return ui._encode("SZ", ui.Elem._encode_fields(self), ui._encode_flags(fl))
109109
end
110+
111+
ui.Button = ui._new_type(ui.Elem, "button", 0x05, true)
112+
113+
function ui.Button:new(props)
114+
ui.Elem.new(self, props)
115+
116+
self._disabled = props.disabled
117+
self._on_press = props.on_press
118+
end
119+
120+
function ui.Button:_encode_fields()
121+
local fl = ui._make_flags()
122+
123+
ui._shift_flag(fl, self._disabled)
124+
ui._shift_flag(fl, self._on_press)
125+
126+
return ui._encode("SZ", ui.Elem._encode_fields(self), ui._encode_flags(fl))
127+
end
128+
129+
ui.Button._handlers[0x00] = function(self, ev, data)
130+
if self._disabled then
131+
return nil
132+
end
133+
return self._on_press
134+
end
135+
136+
ui.Toggle = ui._new_type(ui.Elem, "toggle", 0x06, true)
137+
138+
ui.Check = ui.derive_elem(ui.Toggle, "check")
139+
ui.Switch = ui.derive_elem(ui.Toggle, "switch")
140+
141+
function ui.Toggle:new(props)
142+
ui.Elem.new(self, props)
143+
144+
self._disabled = props.disabled
145+
self._locked = props.locked
146+
self._family = props.family
147+
148+
self._selected = props.selected
149+
150+
self._on_press = props.on_press
151+
self._on_change = props.on_change
152+
153+
if self._family then
154+
assert(ui.is_id(self._family), "Element family must be an ID string")
155+
end
156+
end
157+
158+
function ui.Toggle:_encode_fields()
159+
local fl = ui._make_flags()
160+
161+
ui._shift_flag(fl, self._disabled)
162+
ui._shift_flag(fl, self._locked)
163+
164+
if ui._shift_flag(fl, self._family) then
165+
ui._encode_flag(fl, "z", self._family)
166+
end
167+
if ui._shift_flag(fl, self._selected ~= nil) then
168+
ui._shift_flag(fl, self._selected)
169+
end
170+
171+
ui._shift_flag(fl, self._on_press)
172+
ui._shift_flag(fl, self._on_change)
173+
174+
return ui._encode("SZ", ui.Elem._encode_fields(self), ui._encode_flags(fl))
175+
end
176+
177+
local function on_select(self, ev, data, handler)
178+
local selected = ui._decode("B", data)
179+
ev.selected = selected ~= 0
180+
181+
182+
return handler
183+
end
184+
185+
ui.Toggle._handlers[0x00] = function(self, ev, data)
186+
local selected = ui._decode("B", data)
187+
ev.selected = selected ~= 0
188+
189+
if self._disabled then
190+
-- It's impossible press a disabled radio button.
191+
return nil
192+
elseif self._locked and not ev.selected then
193+
-- It's also impossible to deselect a radio button that's locked.
194+
return nil
195+
end
196+
197+
return self._on_press
198+
end
199+
200+
ui.Toggle._handlers[0x01] = function(self, ev, data)
201+
local selected = ui._decode("B", data)
202+
ev.selected = selected ~= 0
203+
204+
if self._disabled and self._selected then
205+
-- It's impossible to select a disabled radio button, but it's possible
206+
-- for one to be deselected in a family.
207+
return nil
208+
end
209+
210+
return self._on_change
211+
end

builtin/ui/selector.lua

+12
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ local special_preds = {
162162
return result(elem._rindex == rindex)
163163
end
164164
end,
165+
166+
["family"] = function(family)
167+
assert(family == "*" or ui.is_id(family),
168+
"Expected '*' or ID string for ?family")
169+
170+
return function(elem)
171+
if family == "*" then
172+
return result(elem._family)
173+
end
174+
return result(elem._family == family)
175+
end
176+
end,
165177
}
166178

167179
local states_by_name = {

src/gui/elem.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ namespace ui
4848
CREATE(PLACE, Place);
4949
CREATE(FLEX, Flex);
5050
CREATE(GRID, Grid);
51+
CREATE(BUTTON, Button);
52+
CREATE(TOGGLE, Toggle);
5153
default:
5254
return nullptr;
5355
}

src/gui/elem.h

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ namespace ui
4545
PLACE = 0x02,
4646
FLEX = 0x03,
4747
GRID = 0x04,
48+
BUTTON = 0x05,
49+
TOGGLE = 0x06,
4850
};
4951

5052
// For standalone boxes, including the main box, that are always

src/gui/generic_elems.cpp

+113
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,117 @@ namespace ui
5858

5959
Elem::draw(canvas);
6060
}
61+
62+
void Button::reset()
63+
{
64+
Elem::reset();
65+
66+
m_disabled = false;
67+
}
68+
69+
void Button::read(std::istream &is)
70+
{
71+
auto super = newIs(readStr32(is));
72+
Elem::read(super);
73+
74+
u32 set_mask = readU32(is);
75+
76+
m_disabled = testShift(set_mask);
77+
78+
if (testShift(set_mask))
79+
enableEvent(ON_PRESS);
80+
}
81+
82+
bool Button::processInput(const SDL_Event &event)
83+
{
84+
bool handled = Elem::processInput(event);
85+
86+
if (getMainBox().wasTriggered() && testEvent(ON_PRESS)) {
87+
g_manager.sendMessage(createEvent(ON_PRESS).str());
88+
}
89+
90+
return handled;
91+
}
92+
93+
void Toggle::reset()
94+
{
95+
Elem::reset();
96+
97+
m_disabled = false;
98+
m_locked = false;
99+
m_family = "";
100+
}
101+
102+
void Toggle::read(std::istream &is)
103+
{
104+
auto super = newIs(readStr32(is));
105+
Elem::read(super);
106+
107+
u32 set_mask = readU32(is);
108+
109+
m_disabled = testShift(set_mask);
110+
m_locked = testShift(set_mask);
111+
112+
if (testShift(set_mask))
113+
m_family = readNullStr(is);
114+
if (testShift(set_mask))
115+
m_selected = testShift(set_mask);
116+
117+
if (testShift(set_mask))
118+
enableEvent(ON_PRESS);
119+
if (testShift(set_mask))
120+
enableEvent(ON_CHANGE);
121+
}
122+
123+
bool Toggle::processInput(const SDL_Event &event)
124+
{
125+
bool handled = Elem::processInput(event);
126+
127+
if (getMainBox().wasTriggered()) {
128+
if (!m_family.empty()) {
129+
// Clear all toggle buttons in this family except this one.
130+
clearFamily();
131+
}
132+
133+
// If this toggle button isn't locked, or if it's locked but not
134+
// selected, then flip the state.
135+
if (!m_locked || !m_selected) {
136+
m_selected = !m_selected;
137+
sendToggleEvent(ON_CHANGE);
138+
}
139+
140+
// In any case, we send a press event.
141+
sendToggleEvent(ON_PRESS);
142+
}
143+
144+
return handled;
145+
}
146+
147+
void Toggle::clearFamily()
148+
{
149+
for (Elem *elem : getWindow().getElems()) {
150+
// Ignore any elements that don't have the same type as us.
151+
if (elem->getType() != getType()) {
152+
continue;
153+
}
154+
155+
Toggle *toggle = (Toggle *)elem;
156+
157+
// Deselect any toggle buttons in our family except for ourself.
158+
if (toggle->m_family == m_family && toggle->m_selected && toggle != this) {
159+
toggle->m_selected = false;
160+
toggle->sendToggleEvent(ON_CHANGE);
161+
}
162+
}
163+
}
164+
165+
void Toggle::sendToggleEvent(u32 event) const
166+
{
167+
if (testEvent(event)) {
168+
auto os = createEvent(event);
169+
writeU8(os, m_selected);
170+
171+
g_manager.sendMessage(os.str());
172+
}
173+
}
61174
}

src/gui/generic_elems.h

+57
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,61 @@ namespace ui
5252

5353
virtual void draw(Canvas &canvas);
5454
};
55+
56+
class Button : public Elem
57+
{
58+
private:
59+
// Serialized constants; do not change values of entries.
60+
static constexpr u32 ON_PRESS = 0x00;
61+
62+
bool m_disabled;
63+
64+
public:
65+
Button(Window &window, std::string id) :
66+
Elem(window, std::move(id), Box::DYNAMIC | Box::HOLDABLE | Box::USE_SPACE)
67+
{}
68+
69+
virtual Type getType() const override { return BUTTON; }
70+
71+
virtual void reset() override;
72+
virtual void read(std::istream &is) override;
73+
74+
virtual bool isBoxDisabled(u32 group) const override { return m_disabled; }
75+
76+
virtual bool processInput(const SDL_Event &event) override;
77+
};
78+
79+
class Toggle : public Elem
80+
{
81+
private:
82+
// Serialized constants; do not change values of entries.
83+
static constexpr u32 ON_PRESS = 0x00;
84+
static constexpr u32 ON_CHANGE = 0x01;
85+
86+
bool m_disabled;
87+
bool m_locked;
88+
std::string m_family;
89+
90+
bool m_selected = false; // Persistent
91+
92+
public:
93+
Toggle(Window &window, std::string id) :
94+
Elem(window, std::move(id), Box::DYNAMIC | Box::HOLDABLE | Box::USE_SPACE)
95+
{}
96+
97+
virtual Type getType() const override { return TOGGLE; }
98+
99+
virtual void reset() override;
100+
virtual void read(std::istream &is) override;
101+
102+
virtual bool isBoxSelected(u32 group) const override { return m_selected; }
103+
virtual bool isBoxDisabled(u32 group) const override { return m_disabled; }
104+
105+
virtual bool processInput(const SDL_Event &event) override;
106+
107+
private:
108+
void clearFamily();
109+
110+
void sendToggleEvent(u32 event) const;
111+
};
55112
}

0 commit comments

Comments
 (0)