diff --git a/libs/ui/include/psemek/ui/color_picker.hpp b/libs/ui/include/psemek/ui/color_picker.hpp new file mode 100644 index 00000000..c29e4978 --- /dev/null +++ b/libs/ui/include/psemek/ui/color_picker.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +#include + +namespace psemek::ui +{ + + struct color_picker + : single_container + { + virtual gfx::color_rgba color() const = 0; + virtual void set_color(gfx::color_rgba value, bool notify = true) = 0; + + using on_value_changed_callback = std::function; + + virtual void on_value_changed(on_value_changed_callback callback) { on_value_changed_callback_ = callback; } + + protected: + on_value_changed_callback on_value_changed_callback_; + }; + + std::shared_ptr make_color_picker(element_factory & factory); + +} diff --git a/libs/ui/source/color_picker.cpp b/libs/ui/source/color_picker.cpp new file mode 100644 index 00000000..df2ea843 --- /dev/null +++ b/libs/ui/source/color_picker.cpp @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace psemek::ui +{ + + namespace + { + + struct color_picker_impl + : color_picker + { + color_picker_impl(element_factory & factory) + { + auto frame = std::make_shared(); + frame->set_min_size(geom::vector{250.f, 0.f}); + + auto main_layout = factory.make_grid_layout(); + main_layout->set_size(1, 2); + main_layout->set_column_weight(0, 0.f); + main_layout->set_outer_margin(false); + main_layout->set_width_first(false); + + color_view_ = std::make_shared(); + color_view_->set_square(true); + + auto sliders_layout = factory.make_grid_layout(); + sliders_layout->set_size(3, 2); + sliders_layout->set_column_weight(0, 0.f); + sliders_layout->set_outer_margin(false); + + hidden_label_ = factory.make_label("000"); + hidden_label_->set_hidden(true); + + auto hidden_screen = factory.make_screen(); + hidden_screen->add_child(hidden_label_, screen::x_policy::fill, screen::y_policy::fill); + + red_label_ = factory.make_label(""); + red_label_->set_halign(label::halignment::right); + red_label_->set_valign(label::valignment::center); + + green_label_ = factory.make_label(""); + green_label_->set_halign(label::halignment::right); + green_label_->set_valign(label::valignment::center); + + blue_label_ = factory.make_label(""); + blue_label_->set_halign(label::halignment::right); + blue_label_->set_valign(label::valignment::center); + + auto slider_callback = [this](int){ + set_color(color(), true); + }; + + red_slider_ = factory.make_slider(); + red_slider_->set_value_range({0, 255}, false); + red_slider_->on_value_changed(slider_callback, false); + + green_slider_ = factory.make_slider(); + green_slider_->set_value_range({0, 255}, false); + green_slider_->on_value_changed(slider_callback, false); + + blue_slider_ = factory.make_slider(); + blue_slider_->set_value_range({0, 255}, false); + blue_slider_->on_value_changed(slider_callback, false); + + hidden_screen->add_child(red_label_, screen::x_policy::fill, screen::y_policy::fill); + + sliders_layout->set(0, 0, hidden_screen); + sliders_layout->set(1, 0, green_label_); + sliders_layout->set(2, 0, blue_label_); + sliders_layout->set(0, 1, red_slider_); + sliders_layout->set(1, 1, green_slider_); + sliders_layout->set(2, 1, blue_slider_); + + main_layout->set(0, 0, color_view_); + main_layout->set(0, 1, sliders_layout); + + frame->set_child(main_layout); + + set_child(frame); + } + + struct shape const & shape() const override + { + return child_->shape(); + } + + void reshape(geom::box const & box) override + { + child_->reshape(box); + } + + void draw(painter &) const override + {} + + geom::box size_constraints() const override + { + return child_->size_constraints(); + } + + geom::interval width_constraints(float height) const override + { + return child_->width_constraints(height); + } + + geom::interval height_constraints(float width) const override + { + return child_->height_constraints(width); + } + + gfx::color_rgba color() const override + { + return { + red_slider_->value(), + green_slider_->value(), + blue_slider_->value(), + 255 + }; + } + + void set_color(gfx::color_rgba value, bool notify) override + { + color_view_->set_color(value); + + red_label_ ->set_tagged_text(util::to_string("[color:f00]", static_cast(value[0]), "[/color]")); + green_label_->set_tagged_text(util::to_string("[color:0f0]", static_cast(value[1]), "[/color]")); + blue_label_ ->set_tagged_text(util::to_string("[color:00f]", static_cast(value[2]), "[/color]")); + + red_slider_->set_value(value[0], false); + green_slider_->set_value(value[1], false); + blue_slider_->set_value(value[2], false); + + if (notify && on_value_changed_callback_) + post([cb = on_value_changed_callback_, value]{ cb(value); }); + } + + void style_updated() const override + { + color_picker::style_updated(); + + auto st = merged_own_style(); + if (!st->font) return; + + auto glyphs = st->font->shape("0123456789", {}); + + float max_width = 0.f; + char max_width_char = '0'; + for (auto const & g : glyphs) + if (geom::make_max(max_width, g.position[0].length())) + max_width_char = g.character; + + hidden_label_->set_text(std::string(3, max_width_char)); + } + + private: + std::shared_ptr color_view_; + std::shared_ptr