Files
Renderive/Core/render/Button.h
T
2026-08-03 10:00:45 +08:00

165 lines
5.1 KiB
C++

#pragma once
#include "../base/Color.h"
#include "../base/Geometry.h"
#include "../base/Style.h"
#include "Canvas.h"
#include <algorithm>
#include <cstdint>
#include <string>
#include <utility>
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