Refactor ui::selector to support selector hierarchies
This commit is contained in:
parent
7b63614787
commit
3760741ba6
2 changed files with 121 additions and 42 deletions
|
|
@ -1,9 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <psemek/ui/element.hpp>
|
#include <psemek/ui/element.hpp>
|
||||||
#include <psemek/ui/container_impl.hpp>
|
|
||||||
#include <psemek/ui/box_shape.hpp>
|
#include <psemek/ui/box_shape.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace psemek::ui
|
namespace psemek::ui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ namespace psemek::ui
|
||||||
selector();
|
selector();
|
||||||
~selector() override;
|
~selector() override;
|
||||||
|
|
||||||
children_range children() const override { return container_.children(); }
|
children_range children() const override { return children_range_; }
|
||||||
|
|
||||||
struct shape const & shape() const override { return shape_; }
|
struct shape const & shape() const override { return shape_; }
|
||||||
|
|
||||||
|
|
@ -26,9 +27,10 @@ namespace psemek::ui
|
||||||
|
|
||||||
virtual std::size_t size() const;
|
virtual std::size_t size() const;
|
||||||
virtual void resize(std::size_t size);
|
virtual void resize(std::size_t size);
|
||||||
virtual void add(std::shared_ptr<element> child, bool submenu = false);
|
virtual std::size_t add(std::shared_ptr<element> child);
|
||||||
virtual std::shared_ptr<element> get(std::size_t index) const;
|
virtual std::shared_ptr<element> get(std::size_t index) const;
|
||||||
virtual bool submenu(std::size_t index) const;
|
virtual void set_submenu(std::size_t index, std::shared_ptr<selector> submenu);
|
||||||
|
virtual std::shared_ptr<selector> submenu(std::size_t index) const;
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
||||||
virtual geom::interval<float> y_range(std::size_t index) const;
|
virtual geom::interval<float> y_range(std::size_t index) const;
|
||||||
|
|
@ -37,7 +39,6 @@ namespace psemek::ui
|
||||||
|
|
||||||
virtual std::optional<std::size_t> selected() const { return selected_; }
|
virtual std::optional<std::size_t> selected() const { return selected_; }
|
||||||
virtual void on_selected(callback_type callback);
|
virtual void on_selected(callback_type callback);
|
||||||
virtual void on_submenu(callback_type callback);
|
|
||||||
|
|
||||||
virtual callback_type on_selected() const;
|
virtual callback_type on_selected() const;
|
||||||
|
|
||||||
|
|
@ -49,17 +50,24 @@ namespace psemek::ui
|
||||||
// extra distance between successive elements
|
// extra distance between successive elements
|
||||||
virtual int y_extra() const { return 0; }
|
virtual int y_extra() const { return 0; }
|
||||||
|
|
||||||
|
virtual void on_submenu(std::size_t index);
|
||||||
|
|
||||||
|
virtual void release_submenu();
|
||||||
|
|
||||||
std::vector<geom::box<float, 2>> child_boxes_;
|
std::vector<geom::box<float, 2>> child_boxes_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
container_impl container_;
|
std::vector<std::shared_ptr<element>> children_;
|
||||||
|
std::vector<element *> children_range_;
|
||||||
|
|
||||||
box_shape shape_;
|
box_shape shape_;
|
||||||
std::vector<bool> submenu_;
|
std::vector<std::shared_ptr<selector>> submenu_;
|
||||||
|
selector * active_submenu_ = nullptr;
|
||||||
|
float active_submenu_y_ = 0.f;
|
||||||
|
|
||||||
std::optional<std::size_t> selected_;
|
std::optional<std::size_t> selected_;
|
||||||
|
|
||||||
callback_type callback_;
|
callback_type callback_;
|
||||||
callback_type submenu_callback_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool spawn(element * root, std::shared_ptr<selector> selector, geom::point<float, 2> const & position);
|
bool spawn(element * root, std::shared_ptr<selector> selector, geom::point<float, 2> const & position);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include <psemek/ui/positioner.hpp>
|
#include <psemek/ui/positioner.hpp>
|
||||||
#include <psemek/ui/event_interceptor.hpp>
|
#include <psemek/ui/event_interceptor.hpp>
|
||||||
|
|
||||||
|
#include <psemek/util/recursive.hpp>
|
||||||
|
|
||||||
#include <psemek/geom/contains.hpp>
|
#include <psemek/geom/contains.hpp>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
@ -11,7 +13,7 @@ namespace psemek::ui
|
||||||
{
|
{
|
||||||
|
|
||||||
selector::selector()
|
selector::selector()
|
||||||
: container_(this)
|
: children_range_(1, nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
selector::~selector()
|
selector::~selector()
|
||||||
|
|
@ -22,7 +24,6 @@ namespace psemek::ui
|
||||||
void selector::reshape(geom::box<float, 2> const & box)
|
void selector::reshape(geom::box<float, 2> const & box)
|
||||||
{
|
{
|
||||||
shape_.box = box;
|
shape_.box = box;
|
||||||
child_boxes_.clear();
|
|
||||||
|
|
||||||
auto st = merged_own_style();
|
auto st = merged_own_style();
|
||||||
|
|
||||||
|
|
@ -30,8 +31,9 @@ namespace psemek::ui
|
||||||
|
|
||||||
float y = box[1].min;
|
float y = box[1].min;
|
||||||
|
|
||||||
for (auto c : children())
|
for (std::size_t i = 0; i < children_.size(); ++i)
|
||||||
{
|
{
|
||||||
|
auto c = children_[i].get();
|
||||||
float y_start = y;
|
float y_start = y;
|
||||||
y += (*st->inner_margin)[1];
|
y += (*st->inner_margin)[1];
|
||||||
|
|
||||||
|
|
@ -44,10 +46,21 @@ namespace psemek::ui
|
||||||
|
|
||||||
y += (*st->inner_margin)[1];
|
y += (*st->inner_margin)[1];
|
||||||
|
|
||||||
child_boxes_.push_back({{box[0], {y_start, y}}});
|
child_boxes_[i] = {{box[0], {y_start, y}}};
|
||||||
|
|
||||||
y += y_extra() * *st->scale;
|
y += y_extra() * *st->scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (active_submenu_)
|
||||||
|
{
|
||||||
|
auto sc = active_submenu_->size_constraints();
|
||||||
|
|
||||||
|
geom::box<float, 2> box{{{0.f, sc[0].min}, {0.f, sc[1].min}}};
|
||||||
|
box[0] += shape_.box[0].max;
|
||||||
|
box[1] += active_submenu_y_;
|
||||||
|
|
||||||
|
active_submenu_->reshape(box);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
geom::box<float, 2> selector::size_constraints() const
|
geom::box<float, 2> selector::size_constraints() const
|
||||||
|
|
@ -75,9 +88,9 @@ namespace psemek::ui
|
||||||
|
|
||||||
x_range += (*st->inner_margin)[0] * 2.f;
|
x_range += (*st->inner_margin)[0] * 2.f;
|
||||||
|
|
||||||
y_sum += (*st->inner_margin)[1] * 2.f * container_.size();
|
y_sum += (*st->inner_margin)[1] * 2.f * size();
|
||||||
if (!container_.empty())
|
if (!children_.empty())
|
||||||
y_sum += *st->scale * y_extra() * static_cast<int>(container_.size() - 1);
|
y_sum += *st->scale * y_extra() * static_cast<int>(size() - 1);
|
||||||
|
|
||||||
return {{x_range, {y_sum, y_sum}}};
|
return {{x_range, {y_sum, y_sum}}};
|
||||||
}
|
}
|
||||||
|
|
@ -96,12 +109,12 @@ namespace psemek::ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_selected && new_selected != selected_ && submenu_[*new_selected] && submenu_callback_)
|
if (new_selected && new_selected != selected_)
|
||||||
post([cb = submenu_callback_, i = *new_selected]{ cb(i); });
|
on_submenu(*new_selected);
|
||||||
|
|
||||||
selected_ = new_selected;
|
selected_ = new_selected;
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool selector::on_event(mouse_click const & e)
|
bool selector::on_event(mouse_click const & e)
|
||||||
|
|
@ -118,42 +131,50 @@ namespace psemek::ui
|
||||||
|
|
||||||
std::size_t selector::size() const
|
std::size_t selector::size() const
|
||||||
{
|
{
|
||||||
return container_.size();
|
return children_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void selector::resize(std::size_t size)
|
void selector::resize(std::size_t size)
|
||||||
{
|
{
|
||||||
container_.resize(size);
|
children_.resize(size);
|
||||||
submenu_.resize(size);
|
submenu_.resize(size);
|
||||||
child_boxes_.resize(size);
|
child_boxes_.resize(size);
|
||||||
|
children_range_.resize(size + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void selector::add(std::shared_ptr<element> child, bool submenu)
|
std::size_t selector::add(std::shared_ptr<element> child)
|
||||||
{
|
{
|
||||||
std::size_t index = container_.add(child);
|
children_.push_back(child);
|
||||||
if (index >= submenu_.size())
|
submenu_.push_back(nullptr);
|
||||||
{
|
child_boxes_.emplace_back();
|
||||||
submenu_.resize(index + 1);
|
children_range_.push_back(child.get());
|
||||||
child_boxes_.resize(submenu_.size());
|
child->set_parent(this);
|
||||||
}
|
|
||||||
submenu_[index] = submenu;
|
return children_.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<element> selector::get(std::size_t index) const
|
std::shared_ptr<element> selector::get(std::size_t index) const
|
||||||
{
|
{
|
||||||
return container_.get(index);
|
return children_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool selector::submenu(std::size_t index) const
|
void selector::set_submenu(std::size_t index, std::shared_ptr<selector> submenu)
|
||||||
|
{
|
||||||
|
submenu_[index] = std::move(submenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<selector> selector::submenu(std::size_t index) const
|
||||||
{
|
{
|
||||||
return submenu_[index];
|
return submenu_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void selector::clear()
|
void selector::clear()
|
||||||
{
|
{
|
||||||
container_.clear();
|
release_children();
|
||||||
|
children_.clear();
|
||||||
submenu_.clear();
|
submenu_.clear();
|
||||||
child_boxes_.clear();
|
child_boxes_.clear();
|
||||||
|
children_range_.assign(1, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
geom::interval<float> selector::y_range(std::size_t index) const
|
geom::interval<float> selector::y_range(std::size_t index) const
|
||||||
|
|
@ -166,36 +187,83 @@ namespace psemek::ui
|
||||||
callback_ = std::move(callback);
|
callback_ = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void selector::on_submenu(callback_type callback)
|
|
||||||
{
|
|
||||||
submenu_callback_ = std::move(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
selector::callback_type selector::on_selected() const
|
selector::callback_type selector::on_selected() const
|
||||||
{
|
{
|
||||||
return callback_;
|
return callback_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selector::on_submenu(std::size_t index)
|
||||||
|
{
|
||||||
|
release_submenu();
|
||||||
|
|
||||||
|
auto new_submenu = submenu_[index].get();
|
||||||
|
if (new_submenu)
|
||||||
|
{
|
||||||
|
children_range_[0] = new_submenu;
|
||||||
|
active_submenu_ = new_submenu;
|
||||||
|
new_submenu->set_parent(this);
|
||||||
|
active_submenu_y_ = child_boxes_[index][1].min;
|
||||||
|
post_reshape();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selector::release_submenu()
|
||||||
|
{
|
||||||
|
if (active_submenu_)
|
||||||
|
{
|
||||||
|
active_submenu_->release_submenu();
|
||||||
|
active_submenu_->set_parent(nullptr);
|
||||||
|
active_submenu_ = nullptr;
|
||||||
|
children_range_[0] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct opaque_event_interceptor
|
||||||
|
: event_interceptor
|
||||||
|
{
|
||||||
|
bool transparent() const override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool spawn(element * root, std::shared_ptr<selector> selector, geom::point<float, 2> const & position)
|
bool spawn(element * root, std::shared_ptr<selector> selector, geom::point<float, 2> const & position)
|
||||||
{
|
{
|
||||||
ui::screen * screen = find_last_parent_of_type<struct screen>(root);
|
ui::screen * screen = find_last_parent_of_type<struct screen>(root);
|
||||||
if (!screen)
|
if (!screen)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto event_interceptor = std::make_shared<struct event_interceptor>();
|
auto event_interceptor= std::make_shared<opaque_event_interceptor>();
|
||||||
auto positioner = std::make_shared<struct positioner>();
|
auto positioner = std::make_shared<struct positioner>();
|
||||||
|
|
||||||
event_interceptor->set_child(positioner);
|
|
||||||
positioner->set_child(selector);
|
positioner->set_child(selector);
|
||||||
|
event_interceptor->set_child(positioner);
|
||||||
|
|
||||||
auto close = [event_interceptor = event_interceptor.get(), selector = selector.get()]{
|
auto close = [selector_root = event_interceptor.get(), selector = selector.get()]{
|
||||||
selector->on_selected([](std::size_t){});
|
selector->on_selected([](std::size_t){});
|
||||||
|
|
||||||
auto p = dynamic_cast<struct screen *>(event_interceptor->parent());
|
auto p = dynamic_cast<struct screen *>(selector_root->parent());
|
||||||
if (p)
|
if (p)
|
||||||
p->remove_child(event_interceptor);
|
p->remove_child(selector_root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto patch_callback = [&](struct selector * target){
|
||||||
|
target->on_selected([close, cb = target->on_selected()](std::size_t index){
|
||||||
|
close();
|
||||||
|
if (cb)
|
||||||
|
cb(index);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
auto patch_callback_recursive = util::recursive([&](auto && self, struct selector * target) -> void {
|
||||||
|
patch_callback(target);
|
||||||
|
for (std::size_t i = 0; i < target->size(); ++i)
|
||||||
|
if (auto s = target->submenu(i))
|
||||||
|
self(s.get());
|
||||||
|
});
|
||||||
|
patch_callback_recursive(selector.get());
|
||||||
|
|
||||||
selector->on_selected([close, cb = selector->on_selected()](std::size_t index){
|
selector->on_selected([close, cb = selector->on_selected()](std::size_t index){
|
||||||
close();
|
close();
|
||||||
if (cb)
|
if (cb)
|
||||||
|
|
@ -222,7 +290,10 @@ namespace psemek::ui
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
event_interceptor->on_mouse_move([](mouse_move const &){ return true; });
|
||||||
|
|
||||||
send_fake_mouse_move_event(event_interceptor.get(), true);
|
send_fake_mouse_move_event(event_interceptor.get(), true);
|
||||||
|
|
||||||
positioner->set_position(position, positioner::x_align::left, positioner::y_align::top);
|
positioner->set_position(position, positioner::x_align::left, positioner::y_align::top);
|
||||||
screen->add_child(event_interceptor, screen::x_policy::fill, screen::y_policy::fill);
|
screen->add_child(event_interceptor, screen::x_policy::fill, screen::y_policy::fill);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue