From f728d61f23c1d705169007cf88c321fb2b1b5000 Mon Sep 17 00:00:00 2001 From: Slendi Date: Fri, 10 Oct 2025 11:22:43 +0300 Subject: [PATCH] Selection color from accent color Signed-off-by: Slendi --- src/App.cpp | 91 +++++++++++++++++++++++++++----------------- src/App.hpp | 2 + src/ImGui.cpp | 26 +++++++------ src/ImGui.hpp | 22 +++++++++-- src/TextRenderer.cpp | 34 ++++++++--------- src/Tick.cpp | 9 ++++- 6 files changed, 116 insertions(+), 68 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index f5c379c..a15f821 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -3,10 +3,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -66,8 +66,8 @@ struct SurroundingSlice { int anchor { 0 }; }; -auto clamp_surrounding_text( - std::string const &text, int cursor, int anchor) -> SurroundingSlice +auto clamp_surrounding_text(std::string const &text, int cursor, int anchor) + -> SurroundingSlice { int const size = static_cast(text.size()); cursor = std::clamp(cursor, 0, size); @@ -78,19 +78,17 @@ auto clamp_surrounding_text( } int window_start = std::max(0, - std::min(cursor, anchor) - - static_cast(MAX_SURROUNDING_BYTES / 2)); + std::min(cursor, anchor) - static_cast(MAX_SURROUNDING_BYTES / 2)); int window_end = window_start + static_cast(MAX_SURROUNDING_BYTES); int const max_pos = std::max(cursor, anchor); if (window_end < max_pos) { window_end = max_pos; - window_start = std::max(0, - window_end - static_cast(MAX_SURROUNDING_BYTES)); + window_start + = std::max(0, window_end - static_cast(MAX_SURROUNDING_BYTES)); } if (window_end > size) window_end = size; - if (window_end - window_start - > static_cast(MAX_SURROUNDING_BYTES)) { + if (window_end - window_start > static_cast(MAX_SURROUNDING_BYTES)) { window_start = window_end - static_cast(MAX_SURROUNDING_BYTES); } @@ -357,8 +355,8 @@ auto App::init_wayland() -> void static zwp_text_input_v3_listener text_input_listener {}; { - auto ti_enter = [](void *data, zwp_text_input_v3 *, wl_surface *surface) - -> void { + auto ti_enter + = [](void *data, zwp_text_input_v3 *, wl_surface *surface) -> void { auto *app = static_cast(data); bool const focused_surface = surface && surface == app->m_wayland.surface; @@ -375,7 +373,8 @@ auto App::init_wayland() -> void } }; - auto ti_leave = [](void *data, zwp_text_input_v3 *, wl_surface *) -> void { + auto ti_leave + = [](void *data, zwp_text_input_v3 *, wl_surface *) -> void { auto *app = static_cast(data); app->m_ime.seat_focus = false; app->m_ime.enabled = false; @@ -386,8 +385,9 @@ auto App::init_wayland() -> void app->m_gui->ime_clear_preedit(); }; - auto ti_preedit = [](void *data, zwp_text_input_v3 *, char const *text, - int32_t cursor_begin, int32_t cursor_end) -> void { + auto ti_preedit + = [](void *data, zwp_text_input_v3 *, char const *text, + int32_t cursor_begin, int32_t cursor_end) -> void { auto *app = static_cast(data); auto &pending = app->m_ime.pending; pending.has_preedit = true; @@ -396,8 +396,8 @@ auto App::init_wayland() -> void pending.cursor_end = cursor_end; }; - auto ti_commit = [](void *data, zwp_text_input_v3 *, char const *text) - -> void { + auto ti_commit + = [](void *data, zwp_text_input_v3 *, char const *text) -> void { auto *app = static_cast(data); auto &pending = app->m_ime.pending; pending.has_commit = true; @@ -405,7 +405,7 @@ auto App::init_wayland() -> void }; auto ti_delete = [](void *data, zwp_text_input_v3 *, uint32_t before, - uint32_t after) -> void { + uint32_t after) -> void { auto *app = static_cast(data); auto &pending = app->m_ime.pending; pending.has_delete = true; @@ -413,15 +413,16 @@ auto App::init_wayland() -> void pending.after = after; }; - auto ti_done = [](void *data, zwp_text_input_v3 *, uint32_t serial) -> void { + auto ti_done + = [](void *data, zwp_text_input_v3 *, uint32_t serial) -> void { auto *app = static_cast(data); app->m_ime.pending_done = true; app->m_ime.pending_serial = serial; app->m_ime.surrounding_dirty = true; }; - text_input_listener = { ti_enter, ti_leave, ti_preedit, ti_commit, - ti_delete, ti_done }; + text_input_listener + = { ti_enter, ti_leave, ti_preedit, ti_commit, ti_delete, ti_done }; } static auto ensure_text_input = +[](App *app) -> void { @@ -481,8 +482,8 @@ auto App::init_wayland() -> void app->m_wayland.kde_blur_mgr = static_cast(wl_registry_bind( registry, name, &org_kde_kwin_blur_manager_interface, 1)); - } else if (std::strcmp(interface, - zwp_text_input_manager_v3_interface.name) + } else if (std::strcmp( + interface, zwp_text_input_manager_v3_interface.name) == 0) { app->m_wayland.text_input_mgr = static_cast(wl_registry_bind( @@ -588,15 +589,27 @@ void App::on_settings_changed(XdpSettings * /*self*/, char const *ns, char const *key, GVariant * /*value*/, gpointer data) { auto *app = static_cast(data); - if (g_strcmp0(ns, "org.freedesktop.appearance") == 0 - && g_strcmp0(key, "color-scheme") == 0) { - guint v = xdp_settings_read_uint(app->m_xdp.settings, - "org.freedesktop.appearance", "color-scheme", NULL, NULL); + if (g_strcmp0(ns, "org.freedesktop.appearance") == 0) { + if (g_strcmp0(key, "color-scheme") == 0) { + guint v = xdp_settings_read_uint(app->m_xdp.settings, + "org.freedesktop.appearance", "color-scheme", NULL, NULL); - if (v == 1) - app->m_active_theme = Theme::Dark; - else - app->m_active_theme = Theme::Light; + if (v == 1) + app->m_active_theme = Theme::Dark; + else + app->m_active_theme = Theme::Light; + } else if (g_strcmp0(key, "accent-color") == 0) { + auto val = xdp_settings_read_value(app->m_xdp.settings, + "org.freedesktop.appearance", "accent-color", NULL, NULL); + if (val) { + gdouble r, g, b; + g_variant_get(val, "(ddd)", &r, &g, &b); + app->m_accent_color.r = static_cast(r * 255); + app->m_accent_color.g = static_cast(g * 255); + app->m_accent_color.b = static_cast(b * 255); + g_variant_unref(val); + } + } } } @@ -612,6 +625,17 @@ auto App::init_theme_portal() -> void else m_active_theme = Theme::Light; + auto val = xdp_settings_read_value(m_xdp.settings, + "org.freedesktop.appearance", "accent-color", NULL, NULL); + if (val) { + gdouble r, g, b; + g_variant_get(val, "(ddd)", &r, &g, &b); + m_accent_color.r = static_cast(r * 255); + m_accent_color.g = static_cast(g * 255); + m_accent_color.b = static_cast(b * 255); + g_variant_unref(val); + } + g_signal_connect( m_xdp.settings, "changed", G_CALLBACK(on_settings_changed), this); } @@ -828,8 +852,7 @@ auto App::process_pending_text_input() -> void } if (m_ime.pending.has_commit) { - m_gui->ime_commit_text( - *m_ime.bound_text, m_ime.pending.commit_text); + m_gui->ime_commit_text(*m_ime.bound_text, m_ime.pending.commit_text); } if (m_ime.pending.has_preedit) { @@ -880,8 +903,8 @@ auto App::update_text_input_state( } if (auto info = m_gui->text_input_surrounding(id, text)) { - auto slice = clamp_surrounding_text( - info->text, info->cursor, info->anchor); + auto slice + = clamp_surrounding_text(info->text, info->cursor, info->anchor); if (m_ime.surrounding_dirty || slice.text != m_ime.last_surrounding || slice.cursor != m_ime.last_cursor || slice.anchor != m_ime.last_anchor) { diff --git a/src/App.hpp b/src/App.hpp index 088972f..53dbe10 100644 --- a/src/App.hpp +++ b/src/App.hpp @@ -191,5 +191,7 @@ private: bool m_running { true }; bool m_visible { true }; + Color m_accent_color { 127, 127, 255, 255 }; + int m_sfd { -1 }; }; diff --git a/src/ImGui.cpp b/src/ImGui.cpp index cf818a1..7ae0202 100644 --- a/src/ImGui.cpp +++ b/src/ImGui.cpp @@ -307,11 +307,11 @@ auto ImGui::text_input(std::size_t id, std::pmr::string &str, Rectangle rec, if (m_focused_id == 0) m_focused_id = id; - if (options.font_size > rec.height) { + if (style().font_size > rec.height) { TraceLog(LOG_WARNING, std::format("Text size for text input {} is bigger than height ({} " "> {}). Clipping will occur.", - id, options.font_size, rec.height) + id, style().font_size, rec.height) .c_str()); } @@ -545,7 +545,7 @@ auto ImGui::text_input(std::size_t id, std::pmr::string &str, Rectangle rec, && (!state.preedit_text.empty() || !state.preedit_cursor_hidden); if (m_font.has_value() && m_text_renderer) { - int const font_px = static_cast(options.font_size); + int const font_px = static_cast(style().font_size); std::string_view const prefix_view(str.data(), caret_byte); prefix_metrics = m_text_renderer->measure_text(*m_font, prefix_view, font_px); @@ -616,21 +616,21 @@ auto ImGui::text_input(std::size_t id, std::pmr::string &str, Rectangle rec, DrawRectangleLinesEx(rec, 1.0f, border_col); float const text_top = rec.y + VERTICAL_PADDING; - float const baseline_y = text_top + options.font_size; + float const baseline_y = text_top + style().font_size; float const max_caret_height = std::max(0.0f, rec.height - 2.0f * VERTICAL_PADDING); - float caret_height = options.font_size * (1.0f + CARET_DESCENT_FRACTION); + float caret_height = style().font_size * (1.0f + CARET_DESCENT_FRACTION); caret_height = std::min(caret_height, max_caret_height > 0.0f ? max_caret_height : caret_height); if (caret_height <= 0.0f) - caret_height = options.font_size; - float caret_top = baseline_y - options.font_size; + caret_height = style().font_size; + float caret_top = baseline_y - style().font_size; float const min_top = rec.y + VERTICAL_PADDING; float const max_top = rec.y + rec.height - VERTICAL_PADDING - caret_height; caret_top = std::clamp(caret_top, min_top, max_top); float caret_bottom = caret_top + caret_height; float const desired_bottom - = baseline_y + options.font_size * CARET_DESCENT_FRACTION; + = baseline_y + style().font_size * CARET_DESCENT_FRACTION; if (caret_bottom < desired_bottom) { float const adjust = desired_bottom - caret_bottom; caret_top = std::min(caret_top + adjust, max_top); @@ -650,12 +650,12 @@ auto ImGui::text_input(std::size_t id, std::pmr::string &str, Rectangle rec, BeginScissorMode(rec.x, rec.y, rec.width, rec.height); { if (m_font.has_value() && m_text_renderer) { - int const font_px = static_cast(options.font_size); + int const font_px = static_cast(style().font_size); Vector2 const base_pos { rec.x + HORIZONTAL_PADDING - state.scroll_offset.x, baseline_y, }; - Color const &text_color = options.text_color; + Color const &text_color = style().text_color; std::string_view const left_view(str.data(), caret_byte); std::string_view const right_view( str.data() + caret_byte, str.size() - caret_byte); @@ -679,10 +679,12 @@ auto ImGui::text_input(std::size_t id, std::pmr::string &str, Rectangle rec, selection_metrics.x, caret_height, }; - Color const highlight { 120, 160, 255, 100 }; + Color const highlight { style().selection_color }; DrawRectangleRec(sel_rect, highlight); } - Color const &preedit_color { options.preedit_color }; + Color const &preedit_color { selection_metrics.x > 0.0f + ? style().selection_text_color + : style().preedit_color }; m_text_renderer->draw_text(*m_font, std::string_view( state.preedit_text.data(), state.preedit_text.size()), diff --git a/src/ImGui.hpp b/src/ImGui.hpp index 2de4226..c198270 100644 --- a/src/ImGui.hpp +++ b/src/ImGui.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -14,12 +15,17 @@ constexpr float DEFAULT_FONT_SIZE { 24 }; struct TextInputOptions { bool multiline { false }; - float font_size { DEFAULT_FONT_SIZE }; - Color text_color { BLACK }; - Color preedit_color { BLACK }; }; struct ImGui { + struct Style { + float font_size { DEFAULT_FONT_SIZE }; + Color text_color { BLACK }; + Color preedit_color { BLACK }; + Color selection_color { 127, 127, 255, 255 }; + Color selection_text_color { WHITE }; + }; + ImGui(std::shared_ptr text_renderer); ImGui(ImGui const &) = delete; @@ -61,6 +67,14 @@ struct ImGui { void set_font(FontHandle font); + auto style() -> Style & { return m_styles.back(); } + auto push_style(Style const &style) -> Style & + { + m_styles.push(style); + return m_styles.back(); + } + auto push_style() -> Style & { return push_style(style()); } + [[nodiscard]] inline auto id(std::string_view const str) -> std::size_t { std::hash hasher; @@ -90,6 +104,8 @@ private: bool m_ctrl {}; bool m_shift {}; + std::queue