190 lines
3.9 KiB
C++
190 lines
3.9 KiB
C++
#include <psemek/ui/selector.hpp>
|
|
#include <psemek/ui/screen.hpp>
|
|
#include <psemek/ui/positioner.hpp>
|
|
#include <psemek/ui/event_interceptor.hpp>
|
|
|
|
#include <psemek/geom/contains.hpp>
|
|
|
|
#include <stdexcept>
|
|
|
|
namespace psemek::ui
|
|
{
|
|
|
|
selector::selector()
|
|
: container_(this)
|
|
{}
|
|
|
|
selector::~selector()
|
|
{
|
|
release_children();
|
|
}
|
|
|
|
void selector::reshape(geom::box<float, 2> const & box)
|
|
{
|
|
shape_.box = box;
|
|
child_boxes_.clear();
|
|
|
|
auto st = merged_own_style();
|
|
|
|
geom::interval<float> x_range = geom::shrink<float>(box[0], (*st->inner_margin)[0]);
|
|
|
|
float y = box[1].min;
|
|
|
|
for (auto c : children())
|
|
{
|
|
float y_start = y;
|
|
y += (*st->inner_margin)[1];
|
|
|
|
if (c)
|
|
{
|
|
auto sc = c->size_constraints();
|
|
c->reshape({{x_range, {y, y + sc[1].min}}});
|
|
y += sc[1].min;
|
|
}
|
|
|
|
y += (*st->inner_margin)[1];
|
|
|
|
child_boxes_.push_back({{box[0], {y_start, y}}});
|
|
|
|
y += y_extra() * *st->scale;
|
|
}
|
|
}
|
|
|
|
geom::box<float, 2> selector::size_constraints() const
|
|
{
|
|
auto x_range = geom::interval<float>::full();
|
|
|
|
float y_sum = 0.f;
|
|
|
|
for (auto c : children())
|
|
{
|
|
if (!c) continue;
|
|
|
|
auto sc = c->size_constraints();
|
|
x_range &= sc[0];
|
|
|
|
y_sum += sc[1].min;
|
|
}
|
|
|
|
auto st = merged_own_style();
|
|
|
|
x_range += (*st->inner_margin)[0] * 2.f;
|
|
|
|
y_sum += (*st->inner_margin)[1] * 2.f * container_.size();
|
|
if (!container_.empty())
|
|
y_sum += *st->scale * y_extra() * static_cast<int>(container_.size() - 1);
|
|
|
|
return {{x_range, {y_sum, y_sum}}};
|
|
}
|
|
|
|
bool selector::on_event(mouse_move const & e)
|
|
{
|
|
auto const p = geom::cast<float>(e.position);
|
|
|
|
selected_ = std::nullopt;
|
|
for (std::size_t i = 0; i < child_boxes_.size(); ++i)
|
|
{
|
|
if (geom::contains(child_boxes_[i], p))
|
|
{
|
|
selected_ = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool selector::on_event(mouse_click const & e)
|
|
{
|
|
if (e.down && e.button == mouse_button::left && selected_)
|
|
{
|
|
if (callback_)
|
|
post([cb = callback_, i = *selected_]{ cb(i); });
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::size_t selector::size() const
|
|
{
|
|
return container_.size();
|
|
}
|
|
|
|
void selector::resize(std::size_t size)
|
|
{
|
|
container_.resize(size);
|
|
}
|
|
|
|
void selector::add(std::shared_ptr<element> child)
|
|
{
|
|
container_.add(child);
|
|
}
|
|
|
|
std::shared_ptr<element> selector::get(std::size_t index)
|
|
{
|
|
return container_.get(index);
|
|
}
|
|
|
|
void selector::clear()
|
|
{
|
|
container_.clear();
|
|
}
|
|
|
|
void selector::on_selected(std::function<void(std::size_t)> callback)
|
|
{
|
|
callback_ = std::move(callback);
|
|
}
|
|
|
|
bool spawn(element * root, std::shared_ptr<selector> selector, geom::point<int, 2> const & position, std::function<void(std::size_t)> on_selected)
|
|
{
|
|
ui::screen * screen = find_last_parent_of_type<struct screen>(root);
|
|
if (!screen)
|
|
return false;
|
|
|
|
auto event_interceptor = std::make_shared<struct event_interceptor>();
|
|
auto positioner = std::make_shared<struct positioner>();
|
|
|
|
event_interceptor->set_child(positioner);
|
|
positioner->set_child(selector);
|
|
|
|
auto close = [event_interceptor = event_interceptor.get(), selector = selector.get()]{
|
|
auto p = dynamic_cast<struct screen *>(event_interceptor->parent());
|
|
if (p)
|
|
p->remove_child(event_interceptor);
|
|
|
|
selector->on_selected([](std::size_t){});
|
|
};
|
|
|
|
selector->on_selected([close, cb = std::move(on_selected)](std::size_t index){
|
|
close();
|
|
cb(index);
|
|
});
|
|
|
|
event_interceptor->on_mouse_click([close](ui::mouse_click const & e) -> bool {
|
|
if (e.down && (e.button == ui::mouse_button::right || e.button == ui::mouse_button::left))
|
|
{
|
|
close();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
event_interceptor->on_key_press([close](key_press const & e) -> bool {
|
|
if (e.down && e.key == SDLK_ESCAPE)
|
|
{
|
|
close();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
send_fake_mouse_move_event(event_interceptor.get(), true);
|
|
positioner->set_position(geom::cast<float>(position), positioner::x_align::left, positioner::y_align::top);
|
|
screen->add_child(event_interceptor, screen::x_policy::fill, screen::y_policy::fill);
|
|
return true;
|
|
}
|
|
|
|
}
|