Add ui::selector (dropdown list component)
This commit is contained in:
parent
e3ab6d42b6
commit
a8f51e93ac
4 changed files with 180 additions and 0 deletions
|
|
@ -15,6 +15,7 @@
|
|||
#include <psemek/ui/spinbox.hpp>
|
||||
#include <psemek/ui/scroller.hpp>
|
||||
#include <psemek/ui/progress_bar.hpp>
|
||||
#include <psemek/ui/selector.hpp>
|
||||
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
|
||||
|
|
@ -47,6 +48,7 @@ namespace psemek::ui
|
|||
virtual std::shared_ptr<button> make_arrow_button(int direction);
|
||||
virtual std::shared_ptr<scroller> make_scroller();
|
||||
virtual std::shared_ptr<progress_bar> make_progress_bar();
|
||||
virtual std::shared_ptr<selector> make_selector();
|
||||
|
||||
virtual ~element_factory() {}
|
||||
};
|
||||
|
|
|
|||
50
libs/ui/include/psemek/ui/selector.hpp
Normal file
50
libs/ui/include/psemek/ui/selector.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/element.hpp>
|
||||
#include <psemek/ui/container_impl.hpp>
|
||||
#include <psemek/ui/box_shape.hpp>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct selector
|
||||
: element
|
||||
{
|
||||
selector();
|
||||
|
||||
children_range children() const override { return container_.children(); }
|
||||
|
||||
struct shape const & shape() const override { return shape_; }
|
||||
|
||||
void reshape(geom::box<float, 2> const & box) override;
|
||||
|
||||
geom::box<float, 2> size_constraints() const override;
|
||||
|
||||
bool on_event(mouse_move const & e) override;
|
||||
bool on_event(mouse_click const & e) override;
|
||||
|
||||
virtual std::size_t size() const;
|
||||
virtual void resize(std::size_t size);
|
||||
virtual void add(std::shared_ptr<element> child);
|
||||
virtual std::shared_ptr<element> get(std::size_t index);
|
||||
|
||||
virtual std::optional<std::size_t> selected() const { return selected_; }
|
||||
virtual void on_selected(std::function<void(std::size_t)> callback);
|
||||
|
||||
protected:
|
||||
|
||||
// extra distance between successive elements
|
||||
virtual int y_extra() const { return 0; }
|
||||
|
||||
std::vector<geom::box<float, 2>> child_boxes_;
|
||||
|
||||
private:
|
||||
container_impl container_;
|
||||
box_shape shape_;
|
||||
|
||||
std::optional<std::size_t> selected_;
|
||||
|
||||
std::function<void(std::size_t)> callback_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -121,4 +121,6 @@ namespace psemek::ui
|
|||
|
||||
std::shared_ptr<progress_bar> element_factory::make_progress_bar() { return nullptr; }
|
||||
|
||||
std::shared_ptr<selector> element_factory::make_selector() { return nullptr; }
|
||||
|
||||
}
|
||||
|
|
|
|||
126
libs/ui/source/selector.cpp
Normal file
126
libs/ui/source/selector.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#include <psemek/ui/selector.hpp>
|
||||
|
||||
#include <psemek/geom/contains.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
selector::selector()
|
||||
: container_(this)
|
||||
{}
|
||||
|
||||
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);
|
||||
|
||||
float y = box[1].min;
|
||||
|
||||
for (auto c : children())
|
||||
{
|
||||
float y_start = y;
|
||||
y += *st->inner_margin;
|
||||
|
||||
if (c)
|
||||
{
|
||||
auto sc = c->size_constraints();
|
||||
c->reshape({{x_range, {y, y + sc[1].min}}});
|
||||
y += sc[1].min;
|
||||
}
|
||||
|
||||
y += *st->inner_margin;
|
||||
|
||||
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 * 2.f;
|
||||
|
||||
y_sum += *st->inner_margin * 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_ && 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::on_selected(std::function<void(std::size_t)> callback)
|
||||
{
|
||||
callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue