diff --git a/libs/ui/include/psemek/ui/default_element_factory.hpp b/libs/ui/include/psemek/ui/default_element_factory.hpp
index 17e98bf5..8f53a58a 100644
--- a/libs/ui/include/psemek/ui/default_element_factory.hpp
+++ b/libs/ui/include/psemek/ui/default_element_factory.hpp
@@ -17,6 +17,7 @@ namespace psemek::ui
std::shared_ptr make_frame() override;
std::shared_ptr make_window(std::string caption) override;
std::shared_ptr make_slider() override;
+ std::shared_ptr make_spinbox() override;
// directions:
// 0 - up
diff --git a/libs/ui/include/psemek/ui/element_factory.hpp b/libs/ui/include/psemek/ui/element_factory.hpp
index bb91e888..fa37fad0 100644
--- a/libs/ui/include/psemek/ui/element_factory.hpp
+++ b/libs/ui/include/psemek/ui/element_factory.hpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
@@ -29,6 +30,7 @@ namespace psemek::ui
virtual std::shared_ptr make_image_view(std::shared_ptr image);
virtual std::shared_ptr make_rich_image_view(std::shared_ptr image);
virtual std::shared_ptr make_slider();
+ virtual std::shared_ptr make_spinbox();
virtual ~element_factory() {}
};
diff --git a/libs/ui/include/psemek/ui/spinbox.hpp b/libs/ui/include/psemek/ui/spinbox.hpp
new file mode 100644
index 00000000..c31a4a2b
--- /dev/null
+++ b/libs/ui/include/psemek/ui/spinbox.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+
+#include
+
+namespace psemek::ui
+{
+
+ struct spinbox
+ : element
+ {
+ virtual int value() const { return value_; }
+ virtual void set_value(int v);
+
+ virtual geom::interval value_range() const { return value_range_; }
+ virtual void set_value_range(geom::interval i);
+
+ virtual bool wrap() const { return wrap_; }
+ virtual void set_wrap(bool w);
+
+ virtual void inc();
+ virtual void dec();
+
+ using on_value_changed_callback = std::function;
+
+ virtual void on_value_changed(on_value_changed_callback callback);
+
+ protected:
+ virtual void post_value_changed();
+
+ private:
+ int value_ = 0;
+ geom::interval value_range_ = geom::interval::full();
+ bool wrap_ = false;
+ on_value_changed_callback on_value_changed_;
+ };
+
+}
diff --git a/libs/ui/source/default_element_factory.cpp b/libs/ui/source/default_element_factory.cpp
index b44f0a3f..0ac7a7b3 100644
--- a/libs/ui/source/default_element_factory.cpp
+++ b/libs/ui/source/default_element_factory.cpp
@@ -476,6 +476,67 @@ namespace psemek::ui
geom::triangle> triangle_;
};
+ struct spinbox_impl
+ : spinbox
+ {
+ spinbox_impl()
+ {
+ auto layout = std::make_shared();
+ layout->set_row_count(2);
+
+ auto inc_button = std::make_shared(0);
+ auto dec_button = std::make_shared(1);
+
+ inc_button->on_click([this]{
+ inc();
+ });
+
+ dec_button->on_click([this]{
+ dec();
+ });
+
+ layout->set(0, 0, inc_button);
+ layout->set(1, 0, dec_button);
+ layout->set_outer_margin(false);
+
+ child_ = layout;
+ children_range_[0] = child_.get();
+ child_->set_parent(this);
+ }
+
+ geom::box size_constraints() const override
+ {
+ return child_->size_constraints();
+ }
+
+ children_range children() const override
+ {
+ return {children_range_};
+ }
+
+ struct shape const & shape() const override
+ {
+ return child_->shape();
+ }
+
+ void reshape(geom::box const & box) override
+ {
+ child_->reshape(box);
+ }
+
+ void draw(painter &) const override
+ {}
+
+ ~spinbox_impl()
+ {
+ release_children();
+ }
+
+ private:
+ std::shared_ptr child_;
+ element * children_range_[1] {nullptr};
+ };
+
}
struct default_element_factory::impl
@@ -516,6 +577,11 @@ namespace psemek::ui
return r;
}
+ std::shared_ptr default_element_factory::make_spinbox()
+ {
+ return std::make_shared();
+ }
+
std::shared_ptr default_element_factory::make_slider()
{
return std::make_shared();
diff --git a/libs/ui/source/element_factory.cpp b/libs/ui/source/element_factory.cpp
index 016ec370..d332912f 100644
--- a/libs/ui/source/element_factory.cpp
+++ b/libs/ui/source/element_factory.cpp
@@ -75,4 +75,6 @@ namespace psemek::ui
std::shared_ptr element_factory::make_slider() { return nullptr; }
+ std::shared_ptr element_factory::make_spinbox() { return nullptr; }
+
}
diff --git a/libs/ui/source/spinbox.cpp b/libs/ui/source/spinbox.cpp
new file mode 100644
index 00000000..f0454009
--- /dev/null
+++ b/libs/ui/source/spinbox.cpp
@@ -0,0 +1,56 @@
+#include
+
+namespace psemek::ui
+{
+
+ void spinbox::set_value(int v)
+ {
+ v = geom::clamp(v, value_range_);
+ if (v != value_)
+ {
+ value_ = v;
+ post_value_changed();
+ }
+ }
+
+ void spinbox::set_value_range(geom::interval i)
+ {
+ value_range_ = i;
+ set_value(value_);
+ }
+
+ void spinbox::set_wrap(bool w)
+ {
+ wrap_ = w;
+ }
+
+ void spinbox::inc()
+ {
+ if (wrap_)
+ set_value(((value_ + 1 - value_range_.min) % (value_range_.length() + 1)) + value_range_.min);
+ else
+ set_value(value_ + 1);
+ }
+
+ void spinbox::dec()
+ {
+ int const range = value_range_.length() + 1;
+ if (wrap_)
+ set_value(((value_ + range - 1 - value_range_.min) % range) + value_range_.min);
+ else
+ set_value(value_ - 1);
+ }
+
+ void spinbox::on_value_changed(on_value_changed_callback callback)
+ {
+ on_value_changed_ = std::move(callback);
+ post_value_changed();
+ }
+
+ void spinbox::post_value_changed()
+ {
+ if (on_value_changed_)
+ post([cb = on_value_changed_, v = value_]{ cb(v); });
+ }
+
+}