#pragma once #include "../base/Color.h" #include "../base/Geometry.h" #include "../base/Style.h" #include "Canvas.h" #include #include #include #include namespace renderive { enum class Button_State : std::uint8_t { Normal, Hovered, Pressed }; struct Button_Style { Color background{48, 48, 48}; Color hover_background{68, 68, 68}; Color pressed_background{32, 32, 32}; Color border{96, 96, 96}; Color hover_border{64, 142, 255}; Color pressed_border{32, 104, 220}; Color text{235, 235, 235}; Color hover_text{255, 255, 255}; Color pressed_text{255, 255, 255}; double horizontal_padding{8.0}; double vertical_padding{2.0}; double minimum_width{48.0}; double minimum_height{20.0}; }; struct Button_Event_Result { bool consumed{}; bool changed{}; bool clicked{}; }; class Button { public: Button() = default; explicit Button(std::string text) : text_(std::move(text)) {} void set_text(std::string text) { text_ = std::move(text); } const std::string& text() const { return text_; } void set_style(Button_Style style) { style_ = std::move(style); } const Button_Style& style() const { return style_; } void set_rect(RectF rect) { rect_ = rect; } const RectF& rect() const { return rect_; } void set_state(Button_State state) { state_ = state; } Button_State state() const { return state_; } void measure(const Canvas& canvas) { Text_Metrics metrics = canvas.measure_text(text_); preferred_width_ = std::max(style_.minimum_width, metrics.width + style_.horizontal_padding * 2.0); preferred_height_ = std::max(style_.minimum_height, metrics.line_height() + style_.vertical_padding * 2.0); } double preferred_width() const { return preferred_width_; } double preferred_height() const { return preferred_height_; } bool contains(PointF position) const { return !rect_.empty() && rect_.contains(position); } void draw(Canvas& canvas) const { if (rect_.empty()) return; canvas.set_brush(Brush{background_color(), Brush_Style::Solid}); canvas.fill_rect(rect_); canvas.set_pen(Pen{border_color()}); canvas.draw_rect(rect_); canvas.set_pen(Pen{text_color()}); canvas.draw_text(rect_, Text_Align::Center, text_); } private: Color background_color() const { if (state_ == Button_State::Pressed) return style_.pressed_background; if (state_ == Button_State::Hovered) return style_.hover_background; return style_.background; } Color border_color() const { if (state_ == Button_State::Pressed) return style_.pressed_border; if (state_ == Button_State::Hovered) return style_.hover_border; return style_.border; } Color text_color() const { if (state_ == Button_State::Pressed) return style_.pressed_text; if (state_ == Button_State::Hovered) return style_.hover_text; return style_.text; } std::string text_; Button_Style style_; RectF rect_; Button_State state_{Button_State::Normal}; double preferred_width_{}; double preferred_height_{}; }; class Button_Interaction { public: Button_Event_Result pointer_move(PointF position, const RectF& rect) { bool inside = !rect.empty() && rect.contains(position); Button_State next = pressed_ && inside ? Button_State::Pressed : inside ? Button_State::Hovered : Button_State::Normal; bool changed = set_state(next); return {inside || pressed_ || changed, changed, false}; } Button_Event_Result pointer_press(PointF position, const RectF& rect) { if (rect.empty() || !rect.contains(position)) return {}; pressed_ = true; bool changed = set_state(Button_State::Pressed); return {true, changed, false}; } Button_Event_Result pointer_release(PointF position, const RectF& rect) { bool inside = !rect.empty() && rect.contains(position); if (!pressed_) { bool changed = set_state(inside ? Button_State::Hovered : Button_State::Normal); return {false, changed, false}; } pressed_ = false; bool changed = set_state(inside ? Button_State::Hovered : Button_State::Normal); return {true, changed, inside}; } Button_Event_Result pointer_leave() { bool changed = set_state(Button_State::Normal); return {pressed_ || changed, changed, false}; } bool reset() { pressed_ = false; return set_state(Button_State::Normal); } Button_State state() const { return state_; } private: bool set_state(Button_State state) { if (state_ == state) return false; state_ = state; return true; } bool pressed_{}; Button_State state_{Button_State::Normal}; }; } // namespace renderive