Add ui slider element
This commit is contained in:
parent
0bf4324ba4
commit
e67b3f1fdc
9 changed files with 268 additions and 1 deletions
|
|
@ -25,6 +25,7 @@ namespace psemek::ui
|
||||||
std::shared_ptr<button> make_button() override;
|
std::shared_ptr<button> make_button() override;
|
||||||
std::shared_ptr<frame> make_frame() override;
|
std::shared_ptr<frame> make_frame() override;
|
||||||
std::shared_ptr<window> make_window(std::string caption) override;
|
std::shared_ptr<window> make_window(std::string caption) override;
|
||||||
|
std::shared_ptr<slider> make_slider() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
psemek_declare_pimpl
|
psemek_declare_pimpl
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <psemek/ui/grid_layout.hpp>
|
#include <psemek/ui/grid_layout.hpp>
|
||||||
#include <psemek/ui/image_view.hpp>
|
#include <psemek/ui/image_view.hpp>
|
||||||
#include <psemek/ui/rich_image_view.hpp>
|
#include <psemek/ui/rich_image_view.hpp>
|
||||||
|
#include <psemek/ui/slider.hpp>
|
||||||
|
|
||||||
#include <psemek/gfx/texture.hpp>
|
#include <psemek/gfx/texture.hpp>
|
||||||
|
|
||||||
|
|
@ -26,6 +27,7 @@ namespace psemek::ui
|
||||||
virtual std::shared_ptr<grid_layout> make_grid_layout();
|
virtual std::shared_ptr<grid_layout> make_grid_layout();
|
||||||
virtual std::shared_ptr<image_view> make_image_view(std::shared_ptr<gfx::texture_2d> image);
|
virtual std::shared_ptr<image_view> make_image_view(std::shared_ptr<gfx::texture_2d> image);
|
||||||
virtual std::shared_ptr<rich_image_view> make_rich_image_view(std::shared_ptr<gfx::texture_2d> image);
|
virtual std::shared_ptr<rich_image_view> make_rich_image_view(std::shared_ptr<gfx::texture_2d> image);
|
||||||
|
virtual std::shared_ptr<slider> make_slider();
|
||||||
|
|
||||||
virtual ~element_factory() {}
|
virtual ~element_factory() {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
55
libs/ui/include/psemek/ui/slider.hpp
Normal file
55
libs/ui/include/psemek/ui/slider.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/ui/element.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace psemek::ui
|
||||||
|
{
|
||||||
|
|
||||||
|
struct slider
|
||||||
|
: element
|
||||||
|
{
|
||||||
|
bool on_event(mouse_move const & e) override;
|
||||||
|
bool on_event(mouse_click const & e) override;
|
||||||
|
bool on_event(mouse_wheel const & e) override;
|
||||||
|
|
||||||
|
virtual geom::interval<int> value_range() const { return value_range_; }
|
||||||
|
virtual void set_value_range(geom::interval<int> i);
|
||||||
|
|
||||||
|
virtual int value() const { return value_; }
|
||||||
|
virtual void set_value(int v);
|
||||||
|
|
||||||
|
using on_value_changed_callback = std::function<void(int)>;
|
||||||
|
|
||||||
|
void on_value_changed(on_value_changed_callback callback);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class state_t
|
||||||
|
{
|
||||||
|
normal,
|
||||||
|
mouseover,
|
||||||
|
mousedown,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual state_t state() const { return state_; }
|
||||||
|
|
||||||
|
virtual void on_state_changed(state_t /* old */) {}
|
||||||
|
|
||||||
|
virtual geom::interval<float> slider_range() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
geom::interval<int> value_range_{0, 100};
|
||||||
|
int value_ = 0;
|
||||||
|
|
||||||
|
state_t state_ = state_t::normal;
|
||||||
|
std::optional<int> mouse_x_;
|
||||||
|
|
||||||
|
on_value_changed_callback callback_;
|
||||||
|
|
||||||
|
void post_value_changed();
|
||||||
|
|
||||||
|
int compute_value(int x) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,8 @@ namespace psemek::ui
|
||||||
std::optional<gfx::color_rgba> border_color;
|
std::optional<gfx::color_rgba> border_color;
|
||||||
std::optional<int> border_width;
|
std::optional<int> border_width;
|
||||||
|
|
||||||
|
std::optional<gfx::color_rgba> axis_color;
|
||||||
|
|
||||||
std::optional<geom::vector<int, 2>> shadow_offset;
|
std::optional<geom::vector<int, 2>> shadow_offset;
|
||||||
std::optional<gfx::color_rgba> shadow_color;
|
std::optional<gfx::color_rgba> shadow_color;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,86 @@ namespace psemek::ui
|
||||||
element * children_[3];
|
element * children_[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct slider_impl
|
||||||
|
: slider
|
||||||
|
{
|
||||||
|
struct shape const & shape() const override { return shape_; }
|
||||||
|
|
||||||
|
void reshape(geom::box<float, 2> const & bbox)
|
||||||
|
{
|
||||||
|
shape_.box = bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
geom::box<float, 2> size_constraints() const
|
||||||
|
{
|
||||||
|
static float const inf = std::numeric_limits<float>::infinity();
|
||||||
|
return {{{button_width(), inf}, {button_height(), inf}}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(painter & p) const
|
||||||
|
{
|
||||||
|
auto st = merged_style();
|
||||||
|
|
||||||
|
geom::box<float, 2> ab;
|
||||||
|
ab[0] = shape_.box[0];
|
||||||
|
ab[1] = geom::expand(geom::interval<float>::singleton(shape_.box[1].center()), axis_width() / 2.f);
|
||||||
|
|
||||||
|
gfx::color_rgba c = *st->fg_color;
|
||||||
|
if (state() == state_t::mouseover)
|
||||||
|
c = *st->highlight_color;
|
||||||
|
else if (state() == state_t::mousedown)
|
||||||
|
c = *st->action_color;
|
||||||
|
|
||||||
|
auto const r = slider_range();
|
||||||
|
|
||||||
|
float x = geom::lerp(r, geom::unlerp<float>(geom::cast<float>(value_range()), value()));
|
||||||
|
|
||||||
|
geom::box<float, 2> sb;
|
||||||
|
sb[0] = geom::expand(geom::interval<float>::singleton(x), button_width() / 2.f);
|
||||||
|
sb[1] = geom::expand(geom::interval<float>::singleton(shape_.box[1].center()), button_height() / 2.f);
|
||||||
|
|
||||||
|
if (*st->shadow_offset != geom::vector{0, 0})
|
||||||
|
{
|
||||||
|
auto off = geom::cast<float>(*st->shadow_offset);
|
||||||
|
p.draw_rect(ab + off, *st->shadow_color);
|
||||||
|
p.draw_rect(sb + off, *st->shadow_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.draw_rect(ab, *st->axis_color);
|
||||||
|
if (*st->border_width != 0)
|
||||||
|
p.draw_rect(sb, *st->border_color);
|
||||||
|
p.draw_rect(geom::shrink(sb, 1.f * (*st->border_width)), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
geom::interval<float> slider_range() const override
|
||||||
|
{
|
||||||
|
return geom::shrink(shape_.box[0], button_width() / 2.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
box_shape shape_;
|
||||||
|
|
||||||
|
int button_width() const
|
||||||
|
{
|
||||||
|
auto st = merged_style();
|
||||||
|
return *st->outer_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
int button_height() const
|
||||||
|
{
|
||||||
|
auto st = merged_style();
|
||||||
|
return 3 * (*st->outer_margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
int axis_width() const
|
||||||
|
{
|
||||||
|
auto st = merged_style();
|
||||||
|
return *st->inner_margin;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct default_element_factory::impl
|
struct default_element_factory::impl
|
||||||
|
|
@ -350,4 +430,9 @@ namespace psemek::ui
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<slider> default_element_factory::make_slider()
|
||||||
|
{
|
||||||
|
return std::make_shared<slider_impl>();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,4 +73,6 @@ namespace psemek::ui
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<slider> element_factory::make_slider() { return nullptr; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
118
libs/ui/source/slider.cpp
Normal file
118
libs/ui/source/slider.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
#include <psemek/ui/slider.hpp>
|
||||||
|
|
||||||
|
namespace psemek::ui
|
||||||
|
{
|
||||||
|
|
||||||
|
bool slider::on_event(mouse_move const & e)
|
||||||
|
{
|
||||||
|
bool const over = shape().contains(geom::cast<float>(e.position));
|
||||||
|
mouse_x_ = e.position[0];
|
||||||
|
|
||||||
|
switch (state_) {
|
||||||
|
case state_t::normal:
|
||||||
|
if (over)
|
||||||
|
{
|
||||||
|
state_ = state_t::mouseover;
|
||||||
|
on_state_changed(state_t::normal);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case state_t::mouseover:
|
||||||
|
if (!over)
|
||||||
|
{
|
||||||
|
state_ = state_t::normal;
|
||||||
|
on_state_changed(state_t::mouseover);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case state_t::mousedown:
|
||||||
|
if (mouse_x_)
|
||||||
|
set_value(compute_value(*mouse_x_));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool slider::on_event(mouse_click const & e)
|
||||||
|
{
|
||||||
|
if (e.button != mouse_button::left) return false;
|
||||||
|
|
||||||
|
switch (state_) {
|
||||||
|
case state_t::normal:
|
||||||
|
break;
|
||||||
|
case state_t::mouseover:
|
||||||
|
if (e.down)
|
||||||
|
{
|
||||||
|
state_ = state_t::mousedown;
|
||||||
|
if (mouse_x_)
|
||||||
|
set_value(compute_value(*mouse_x_));
|
||||||
|
on_state_changed(state_t::mouseover);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case state_t::mousedown:
|
||||||
|
if (!e.down)
|
||||||
|
{
|
||||||
|
state_ = state_t::mouseover;
|
||||||
|
on_state_changed(state_t::mousedown);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool slider::on_event(mouse_wheel const & e)
|
||||||
|
{
|
||||||
|
if (state_ == state_t::mouseover)
|
||||||
|
{
|
||||||
|
set_value(value_ + e.delta);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void slider::set_value_range(geom::interval<int> i)
|
||||||
|
{
|
||||||
|
if (i.empty()) throw std::runtime_error("Empty value range for ui::slider");
|
||||||
|
value_range_ = i;
|
||||||
|
set_value(value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void slider::set_value(int v)
|
||||||
|
{
|
||||||
|
v = geom::clamp(v, value_range_);
|
||||||
|
if (value_ != v)
|
||||||
|
{
|
||||||
|
value_ = v;
|
||||||
|
post_value_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void slider::on_value_changed(on_value_changed_callback callback)
|
||||||
|
{
|
||||||
|
callback_ = std::move(callback);
|
||||||
|
post_value_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
geom::interval<float> slider::slider_range() const
|
||||||
|
{
|
||||||
|
return shape().bbox()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void slider::post_value_changed()
|
||||||
|
{
|
||||||
|
if (callback_)
|
||||||
|
post([cb = callback_, value = value_]{
|
||||||
|
cb(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int slider::compute_value(int x) const
|
||||||
|
{
|
||||||
|
auto const range = slider_range();
|
||||||
|
return std::round(geom::unlerp(range, x * 1.f) * value_range_.length()) + value_range_.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ namespace psemek::ui
|
||||||
merge(dst.action_offset, src.action_offset);
|
merge(dst.action_offset, src.action_offset);
|
||||||
merge(dst.border_color, src.border_color);
|
merge(dst.border_color, src.border_color);
|
||||||
merge(dst.border_width, src.border_width);
|
merge(dst.border_width, src.border_width);
|
||||||
|
merge(dst.axis_color, src.axis_color);
|
||||||
merge(dst.shadow_offset, src.shadow_offset);
|
merge(dst.shadow_offset, src.shadow_offset);
|
||||||
merge(dst.shadow_color, src.shadow_color);
|
merge(dst.shadow_color, src.shadow_color);
|
||||||
merge(dst.inner_margin, src.inner_margin);
|
merge(dst.inner_margin, src.inner_margin);
|
||||||
|
|
@ -47,6 +48,8 @@ namespace psemek::ui
|
||||||
s.border_color = {255, 255, 255, 255};
|
s.border_color = {255, 255, 255, 255};
|
||||||
s.border_width = 3;
|
s.border_width = 3;
|
||||||
|
|
||||||
|
s.axis_color = {255, 255, 255, 255};
|
||||||
|
|
||||||
s.shadow_offset = {1, 1};
|
s.shadow_offset = {1, 1};
|
||||||
s.shadow_color = {0, 0, 0, 255};
|
s.shadow_color = {0, 0, 0, 255};
|
||||||
|
|
||||||
|
|
|
||||||
1
todo.md
1
todo.md
|
|
@ -26,4 +26,3 @@
|
||||||
* grid layout quadratic optimization
|
* grid layout quadratic optimization
|
||||||
* draggable window element
|
* draggable window element
|
||||||
* screen children sorting
|
* screen children sorting
|
||||||
* slider element
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue