mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-06-06 02:58:24 -04:00
Added recompui::IconButton
This commit is contained in:
@@ -183,6 +183,7 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/core/ui_context.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_icon_button.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_clickable.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp
|
||||
@@ -193,6 +194,7 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_span.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_svg.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_text_input.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_theme.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
|
||||
|
||||
@@ -100,17 +100,21 @@ namespace recompui {
|
||||
apply_theme_style(ThemeColor::Danger);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Basic: {
|
||||
apply_theme_style(ThemeColor::Text, true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unknown button style.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Button::apply_theme_style(recompui::ThemeColor color) {
|
||||
const uint8_t border_opacity = 204;
|
||||
const uint8_t background_opacity = 13;
|
||||
const uint8_t border_hover_opacity = 255;
|
||||
void Button::apply_theme_style(recompui::ThemeColor color, bool is_basic) {
|
||||
const uint8_t border_opacity = is_basic ? 0 : 204;
|
||||
const uint8_t background_opacity = is_basic ? 0 : 13;
|
||||
const uint8_t background_hover_opacity = 77;
|
||||
const uint8_t border_hover_opacity = is_basic ? background_hover_opacity : 255;
|
||||
|
||||
set_border_color(color, border_opacity);
|
||||
set_background_color(color, background_opacity);
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace recompui {
|
||||
Success,
|
||||
Warning,
|
||||
Danger,
|
||||
Basic, // No border, only shows background on hover or focus.
|
||||
};
|
||||
|
||||
class Button : public Element {
|
||||
@@ -34,7 +35,7 @@ namespace recompui {
|
||||
Style* get_hover_disabled_style() { return &hover_disabled_style; }
|
||||
void apply_button_style(ButtonStyle new_style);
|
||||
private:
|
||||
void apply_theme_style(recompui::ThemeColor color);
|
||||
void apply_theme_style(recompui::ThemeColor color, bool is_basic = false);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
|
||||
@@ -110,7 +110,7 @@ void Element::apply_style(Style *style) {
|
||||
// Skip redundant SetProperty calls to prevent dirtying unnecessary state.
|
||||
// This avoids expensive layout operations when a simple color-only style is applied.
|
||||
const Rml::Property* cur_value = base->GetLocalProperty(it.first);
|
||||
if (*cur_value != it.second) {
|
||||
if (cur_value == nullptr || *cur_value != it.second) {
|
||||
base->SetProperty(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "ui_icon_button.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
// Borders add width to the button, so this subtracts from the base size to bring it back to the expected size.
|
||||
static const float border_width_thickness = 1.1f;
|
||||
|
||||
IconButton::IconButton(Element *parent, const std::string &svg_src, ButtonStyle style, IconButtonSize size) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, EventType::Focus), "button") {
|
||||
this->style = style;
|
||||
this->size = size;
|
||||
float float_size_internal = static_cast<float>(size) - (border_width_thickness * 2.0f);
|
||||
|
||||
enable_focus();
|
||||
|
||||
set_display(Display::Flex);
|
||||
set_align_items(AlignItems::Center);
|
||||
set_justify_content(JustifyContent::Center);
|
||||
set_width(float_size_internal);
|
||||
set_min_width(float_size_internal);
|
||||
set_max_width(float_size_internal);
|
||||
set_height(float_size_internal);
|
||||
set_min_height(float_size_internal);
|
||||
set_max_height(float_size_internal);
|
||||
set_border_width(border_width_thickness);
|
||||
set_border_radius(float_size_internal * 0.5f);
|
||||
set_border_color(ThemeColor::Transparent);
|
||||
|
||||
set_cursor(Cursor::Pointer);
|
||||
set_color(ThemeColor::TextDim);
|
||||
set_tab_index(TabIndex::Auto);
|
||||
|
||||
hover_style.set_color(ThemeColor::Text);
|
||||
focus_style.set_color(ThemeColor::Text);
|
||||
disabled_style.set_color(ThemeColor::TextDim);
|
||||
disabled_style.set_cursor(Cursor::None);
|
||||
disabled_style.set_opacity(0.5f);
|
||||
hover_disabled_style.set_color(ThemeColor::TextDim);
|
||||
|
||||
float icon_size = 0;
|
||||
switch (size) {
|
||||
case IconButtonSize::Mini:
|
||||
icon_size = 16.0f;
|
||||
break;
|
||||
case IconButtonSize::Small:
|
||||
icon_size = 24.0f;
|
||||
break;
|
||||
case IconButtonSize::Medium:
|
||||
icon_size = 32.0f;
|
||||
break;
|
||||
case IconButtonSize::Large:
|
||||
default:
|
||||
icon_size = 32.0f;
|
||||
break;
|
||||
case IconButtonSize::XLarge:
|
||||
icon_size = 40.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
ContextId context = get_current_context();
|
||||
|
||||
svg = context.create_element<Svg>(this, svg_src);
|
||||
svg->set_width(icon_size);
|
||||
svg->set_color(ThemeColor::TextDim);
|
||||
|
||||
apply_button_style(style);
|
||||
|
||||
// transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out;
|
||||
}
|
||||
|
||||
void IconButton::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Click:
|
||||
if (is_enabled()) {
|
||||
for (const auto &function : pressed_callbacks) {
|
||||
function();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Hover:
|
||||
{
|
||||
bool hover_active = std::get<EventHover>(e.variant).active && is_enabled();
|
||||
set_style_enabled(hover_state, hover_active);
|
||||
svg->set_color(hover_active ? ThemeColor::Text : ThemeColor::TextDim);
|
||||
}
|
||||
break;
|
||||
case EventType::Enable:
|
||||
{
|
||||
bool enable_active = std::get<EventEnable>(e.variant).active;
|
||||
set_style_enabled(disabled_state, !enable_active);
|
||||
if (enable_active) {
|
||||
set_cursor(Cursor::Pointer);
|
||||
set_focusable(true);
|
||||
svg->set_color(ThemeColor::TextDim);
|
||||
}
|
||||
else {
|
||||
set_cursor(Cursor::None);
|
||||
set_focusable(false);
|
||||
svg->set_color(ThemeColor::TextDim);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType::Focus:
|
||||
{
|
||||
bool focus_active = std::get<EventFocus>(e.variant).active;
|
||||
set_style_enabled(focus_state, focus_active);
|
||||
svg->set_color(focus_active ? ThemeColor::Text : ThemeColor::TextDim);
|
||||
}
|
||||
break;
|
||||
case EventType::Update:
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown event type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IconButton::add_pressed_callback(std::function<void()> callback) {
|
||||
pressed_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void IconButton::apply_button_style(ButtonStyle new_style) {
|
||||
style = new_style;
|
||||
switch (style) {
|
||||
case ButtonStyle::Primary: {
|
||||
apply_theme_style(ThemeColor::Primary);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Secondary: {
|
||||
apply_theme_style(ThemeColor::Secondary);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Tertiary: {
|
||||
apply_theme_style(ThemeColor::Text);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Success: {
|
||||
apply_theme_style(ThemeColor::Success);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Warning: {
|
||||
apply_theme_style(ThemeColor::Warning);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Danger: {
|
||||
apply_theme_style(ThemeColor::Danger);
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Basic: {
|
||||
apply_theme_style(ThemeColor::Text, true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unknown button style.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IconButton::apply_theme_style(recompui::ThemeColor color, bool is_basic) {
|
||||
const uint8_t background_opacity = is_basic ? 0 : 13;
|
||||
const uint8_t border_opacity = is_basic ? 0 : 204;
|
||||
const uint8_t background_hover_opacity = 77;
|
||||
const uint8_t border_hover_opacity = is_basic ? background_hover_opacity : 255;
|
||||
|
||||
set_border_color(color, border_opacity);
|
||||
set_background_color(color, background_opacity);
|
||||
hover_style.set_border_color(color, border_hover_opacity);
|
||||
hover_style.set_background_color(color, background_hover_opacity);
|
||||
focus_style.set_border_color(color, border_hover_opacity);
|
||||
focus_style.set_background_color(color, background_hover_opacity);
|
||||
|
||||
add_style(&hover_style, hover_state);
|
||||
add_style(&focus_style, focus_state);
|
||||
add_style(&disabled_style, disabled_state);
|
||||
add_style(&hover_disabled_style, { hover_state, disabled_state });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
#include "ui_button.h"
|
||||
#include "ui_svg.h"
|
||||
|
||||
namespace recompui {
|
||||
enum class IconButtonSize {
|
||||
Mini = 20, // 20x20 (Inline with body text)
|
||||
Small = 32, // 32x32
|
||||
Medium = 48, // 48x48
|
||||
Large = 56, // 56x56
|
||||
Default = IconButtonSize::Large,
|
||||
XLarge = 72, // 72x72
|
||||
};
|
||||
|
||||
class IconButton : public Element {
|
||||
protected:
|
||||
ButtonStyle style = ButtonStyle::Primary;
|
||||
IconButtonSize size = IconButtonSize::Default;
|
||||
Style hover_style;
|
||||
Style focus_style;
|
||||
Style disabled_style;
|
||||
Style hover_disabled_style;
|
||||
std::list<std::function<void()>> pressed_callbacks;
|
||||
Svg *svg;
|
||||
|
||||
// Element overrides.
|
||||
virtual void process_event(const Event &e) override;
|
||||
std::string_view get_type_name() override { return "IconButton"; }
|
||||
public:
|
||||
IconButton(Element *parent, const std::string &svg_src, ButtonStyle style, IconButtonSize size = IconButtonSize::Default);
|
||||
void add_pressed_callback(std::function<void()> callback);
|
||||
Style* get_hover_style() { return &hover_style; }
|
||||
Style* get_focus_style() { return &focus_style; }
|
||||
Style* get_disabled_style() { return &disabled_style; }
|
||||
Style* get_hover_disabled_style() { return &hover_disabled_style; }
|
||||
void apply_button_style(ButtonStyle new_style);
|
||||
private:
|
||||
void apply_theme_style(recompui::ThemeColor color, bool is_basic = false);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "ui_svg.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Svg::Svg(Element *parent, std::string_view src) : Element(parent, 0, "svg") {
|
||||
set_src(src);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class Svg : public Element {
|
||||
protected:
|
||||
std::string_view get_type_name() override { return "Svg"; }
|
||||
public:
|
||||
Svg(Element *parent, std::string_view src);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
@@ -688,11 +688,11 @@ ModMenu::ModMenu(Element *parent) : Element(parent) {
|
||||
Element* footer_spacer = context.create_element<Element>(footer_container);
|
||||
footer_spacer->set_flex(1.0f, 0.0f);
|
||||
|
||||
refresh_button = context.create_element<Button>(footer_container, "Refresh", recompui::ButtonStyle::Tertiary);
|
||||
refresh_button = context.create_element<IconButton>(footer_container, "icons/Reset.svg", recompui::ButtonStyle::Secondary, recompui::IconButtonSize::XLarge);
|
||||
refresh_button->add_pressed_callback([this](){ refresh_mods(true); });
|
||||
refresh_button->set_nav_manual(NavDirection::Up, mod_tab_id);
|
||||
|
||||
mods_folder_button = context.create_element<Button>(footer_container, "Open Mods Folder", recompui::ButtonStyle::Primary);
|
||||
mods_folder_button = context.create_element<Button>(footer_container, "Open Mods Folder", recompui::ButtonStyle::Tertiary);
|
||||
mods_folder_button->add_pressed_callback([this](){ open_mods_folder(); });
|
||||
mods_folder_button->set_nav(NavDirection::Up, configure_button);
|
||||
mods_folder_button->set_nav_manual(NavDirection::Up, mod_tab_id);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "librecomp/mods.hpp"
|
||||
#include "elements/ui_scroll_container.h"
|
||||
#include "elements/ui_icon_button.h"
|
||||
#include "ui_config_sub_menu.h"
|
||||
#include "ui_mod_details_panel.h"
|
||||
|
||||
@@ -101,7 +102,7 @@ private:
|
||||
Container *body_empty_container = nullptr;
|
||||
Container *footer_container = nullptr;
|
||||
Button *install_mods_button = nullptr;
|
||||
Button *refresh_button = nullptr;
|
||||
IconButton *refresh_button = nullptr;
|
||||
Button *mods_folder_button = nullptr;
|
||||
int32_t active_mod_index = -1;
|
||||
std::vector<ModEntryButton *> mod_entry_buttons;
|
||||
|
||||
Reference in New Issue
Block a user