Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package fcitx5-gtk for openSUSE:Factory checked in at 2026-10-22 15:49:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/fcitx5-gtk (Old) and /work/SRC/openSUSE:Factory/.fcitx5-gtk.new.2252680 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "fcitx5-gtk" Thu Oct 22 15:49:05 2026 rev:16 rq:1386554 version:5.1.7 Changes: -------- --- /work/SRC/openSUSE:Factory/fcitx5-gtk/fcitx5-gtk.changes 2026-02-09 11:43:46.978814697 +0100 +++ /work/SRC/openSUSE:Factory/.fcitx5-gtk.new.2252680/fcitx5-gtk.changes 2026-10-22 15:50:13.508924215 +0200 @@ -1,0 +2,8 @@ +Sat Oct 17 15:55:35 UTC 2026 - Martin Pluskal <[email protected]> + +- Update to 5.1.7: + * Update cursor position when a key is pressed + * Replace gdk_popup with gtk_popover + * Fix parent window signal connection cleanup on destruction + +------------------------------------------------------------------- Old: ---- fcitx5-gtk-5.1.5.tar.zst New: ---- fcitx5-gtk-5.1.7.tar.zst ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ fcitx5-gtk.spec ++++++ --- /var/tmp/diff_new_pack.XCQh1B/_old 2026-10-22 15:50:14.311957877 +0200 +++ /var/tmp/diff_new_pack.XCQh1B/_new 2026-10-22 15:50:14.315958045 +0200 @@ -17,7 +17,7 @@ Name: fcitx5-gtk -Version: 5.1.5 +Version: 5.1.7 Release: 0 Summary: Gtk im module for fcitx5 and glib based dbus client library License: LGPL-2.1-or-later ++++++ fcitx5-gtk-5.1.5.tar.zst -> fcitx5-gtk-5.1.7.tar.zst ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fcitx5-gtk-5.1.5/CMakeLists.txt new/fcitx5-gtk-5.1.7/CMakeLists.txt --- old/fcitx5-gtk-5.1.5/CMakeLists.txt 2025-12-22 07:18:11.000000000 +0100 +++ new/fcitx5-gtk-5.1.7/CMakeLists.txt 2026-06-20 06:19:08.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.6) -project(fcitx5-gtk VERSION 5.1.5) +project(fcitx5-gtk VERSION 5.1.7) find_package(ECM REQUIRED 1.0.0) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fcitx5-gtk-5.1.5/README.md new/fcitx5-gtk-5.1.7/README.md --- old/fcitx5-gtk-5.1.5/README.md 2025-12-22 07:18:11.000000000 +0100 +++ new/fcitx5-gtk-5.1.7/README.md 2026-06-20 06:19:08.000000000 +0200 @@ -1,5 +1,5 @@ Gtk im module for fcitx5 and glib based dbus client library ========================================== -[](https://jenkins.fcitx-im.org/job/fcitx5-gtk/) +[](https://jenkins.fcitx-im.org/job/fcitx5-gtk/) [](https://scan.coverity.com/projects/fcitx-fcitx5-gtk) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fcitx5-gtk-5.1.5/gtk3/fcitximcontext.cpp new/fcitx5-gtk-5.1.7/gtk3/fcitximcontext.cpp --- old/fcitx5-gtk-5.1.5/gtk3/fcitximcontext.cpp 2025-12-22 07:18:11.000000000 +0100 +++ new/fcitx5-gtk-5.1.7/gtk3/fcitximcontext.cpp 2026-06-20 06:19:08.000000000 +0200 @@ -1624,6 +1624,12 @@ break; } + /* set_cursor_location_internal() will get origin from X server, + * it blocks UI. So delay it to idle callback. */ + gdk_threads_add_idle_full( + G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)_set_cursor_location_internal, + g_object_ref(fcitxcontext), (GDestroyNotify)g_object_unref); + _request_surrounding_text(&fcitxcontext); if (G_UNLIKELY(!fcitxcontext)) return FALSE; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fcitx5-gtk-5.1.5/gtk4/fcitximcontext.cpp new/fcitx5-gtk-5.1.7/gtk4/fcitximcontext.cpp --- old/fcitx5-gtk-5.1.5/gtk4/fcitximcontext.cpp 2025-12-22 07:18:11.000000000 +0100 +++ new/fcitx5-gtk-5.1.7/gtk4/fcitximcontext.cpp 2026-06-20 06:19:08.000000000 +0200 @@ -407,8 +407,6 @@ return; } - delete fcitxcontext->candidate_window; - fcitxcontext->candidate_window = nullptr; g_clear_object(&fcitxcontext->client_widget); if (!client_widget) return; @@ -417,8 +415,10 @@ _fcitx_im_context_set_capability(fcitxcontext, FALSE); - fcitxcontext->candidate_window = - new Gtk4InputWindow(_uiconfig, fcitxcontext->client); + if (!fcitxcontext->candidate_window) { + fcitxcontext->candidate_window = + new Gtk4InputWindow(_uiconfig, fcitxcontext->client); + } fcitxcontext->candidate_window->setParent(fcitxcontext->client_widget); fcitxcontext->candidate_window->setCursorRect(fcitxcontext->area); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fcitx5-gtk-5.1.5/gtk4/gtk4inputwindow.cpp new/fcitx5-gtk-5.1.7/gtk4/gtk4inputwindow.cpp --- old/fcitx5-gtk-5.1.5/gtk4/gtk4inputwindow.cpp 2025-12-22 07:18:11.000000000 +0100 +++ new/fcitx5-gtk-5.1.7/gtk4/gtk4inputwindow.cpp 2026-06-20 06:19:08.000000000 +0200 @@ -5,24 +5,239 @@ * */ #include "gtk4inputwindow.h" +#include "inputwindow.h" +#include <cairo.h> +#include <gdk/gdk.h> +#include <glib-object.h> +#include <glib.h> +#include <graphene.h> +#include <gsk/gsk.h> #include <gtk/gtk.h> -#include <gtk3/fcitxtheme.h> +#include <pango/pangocairo.h> +#include <tuple> + +G_BEGIN_DECLS + +#define FCITX_TYPE_CLIENT_SIDE_POPUP (fcitx_client_side_popup_get_type()) +G_DECLARE_FINAL_TYPE(FcitxClientSidePopup, fcitx_client_side_popup, FCITX, + CLIENT_SIDE_POPUP, GtkPopover) + +GtkWidget *fcitx_client_side_popup_new(void); + +G_END_DECLS + +struct _FcitxClientSidePopup { + GtkPopover parent_instance; + // You can add custom state fields here (e.g., colors, values, etc.) + fcitx::gtk::Gtk4InputWindow + *inputWindow; // Back-reference to the input window for interaction +}; + +G_DEFINE_TYPE(FcitxClientSidePopup, fcitx_client_side_popup, GTK_TYPE_POPOVER) + +static void fcitx_client_side_popup_snapshot(GtkWidget *widget, + GtkSnapshot *snapshot) { + int width = gtk_widget_get_width(widget); + int height = gtk_widget_get_height(widget); + + if (width <= 0 || height <= 0) { + return; + } + + // Create a bounding box for the Cairo context + graphene_rect_t bounds; + graphene_rect_init(&bounds, 0, 0, width, height); + + // Request a Cairo context from the GTK snapshot layer + cairo_t *cr = gtk_snapshot_append_cairo(snapshot, &bounds); + + if (auto *window = FCITX_CLIENT_SIDE_POPUP(widget)->inputWindow) { + window->draw(cr); + } + + // Clean up the cairo context + cairo_destroy(cr); +} + +static void fcitx_client_side_popup_set_parent(FcitxClientSidePopup *self, + GtkWidget *parent) { + if (parent == gtk_widget_get_parent(GTK_WIDGET(self))) { + return; + } + if (auto *oldParent = gtk_widget_get_parent(GTK_WIDGET(self))) { + g_signal_handlers_disconnect_by_data(oldParent, self); + gtk_widget_unparent(GTK_WIDGET(self)); + } + if (parent) { + gtk_widget_set_parent(GTK_WIDGET(self), parent); + g_signal_connect(parent, "destroy", + G_CALLBACK(+[](GtkWidget *widget, gpointer data) { + if (gtk_widget_get_parent(GTK_WIDGET(data)) == + widget) { + gtk_widget_unparent(GTK_WIDGET(data)); + } + }), + self); + } else { + gtk_popover_popdown(GTK_POPOVER(self)); + } +} + +static void fcitx_client_side_popup_init(FcitxClientSidePopup *self) { + self->inputWindow = nullptr; // Initialize back-reference + gtk_widget_add_css_class(GTK_WIDGET(self), "fcitx-popup"); + gtk_widget_set_halign(GTK_WIDGET(self), GTK_ALIGN_START); + gtk_popover_set_position(GTK_POPOVER(self), GTK_POS_BOTTOM); + gtk_popover_set_autohide(GTK_POPOVER(self), false); + gtk_popover_set_has_arrow(GTK_POPOVER(self), false); + gtk_popover_set_cascade_popdown(GTK_POPOVER(self), false); + gtk_popover_set_mnemonics_visible(GTK_POPOVER(self), false); + + auto *motionController = gtk_event_controller_motion_new(); + g_signal_connect(motionController, "motion", + G_CALLBACK(+[](GtkEventControllerMotion * /*controller*/, + gdouble x, gdouble y, gpointer user_data) { + auto *self = + static_cast<FcitxClientSidePopup *>(user_data); + if (self->inputWindow) { + self->inputWindow->motion(x, y); + } + }), + self); + g_signal_connect(motionController, "leave", + G_CALLBACK(+[](GtkEventControllerMotion * /*controller*/, + gpointer user_data) { + auto *self = + static_cast<FcitxClientSidePopup *>(user_data); + if (self->inputWindow) { + self->inputWindow->leave(); + } + }), + self); + gtk_widget_add_controller(GTK_WIDGET(self), + GTK_EVENT_CONTROLLER(motionController)); + + auto *scrollController = + gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL); + g_signal_connect( + scrollController, "scroll", + G_CALLBACK(+[](GtkEventControllerScroll * /*controller*/, gdouble x, + gdouble y, gpointer user_data) -> gboolean { + auto *self = static_cast<FcitxClientSidePopup *>(user_data); + if (self->inputWindow) { + return self->inputWindow->scroll(x, y); + } + return false; + }), + self); + gtk_widget_add_controller(GTK_WIDGET(self), + GTK_EVENT_CONTROLLER(scrollController)); + + auto *clickController = gtk_gesture_click_new(); + g_signal_connect( + clickController, "released", + G_CALLBACK(+[](GtkGestureClick *controller, gint /*n_press*/, gdouble x, + gdouble y, gpointer user_data) { + auto *self = static_cast<FcitxClientSidePopup *>(user_data); + if (self->inputWindow) { + guint button = gtk_gesture_single_get_current_button( + GTK_GESTURE_SINGLE(controller)); + if (button == GDK_BUTTON_PRIMARY) { + self->inputWindow->click(x, y); + } + } + }), + self); + gtk_widget_add_controller(GTK_WIDGET(self), + GTK_EVENT_CONTROLLER(clickController)); + + g_signal_connect(GTK_WIDGET(self), "notify::visible", + G_CALLBACK(+[](GtkWidget *self, GParamSpec * /*pspec*/, + gpointer /*data*/) { + auto inputWindow = + FCITX_CLIENT_SIDE_POPUP(self)->inputWindow; + // Do not become visible if the input window is not + // visible. This may happen when gtk_widget_show_all is + // called which recursively shows all children, + // including our attached popup. + if (!inputWindow || !inputWindow->visible()) { + gtk_widget_set_visible(self, FALSE); + fcitx_client_side_popup_set_parent( + FCITX_CLIENT_SIDE_POPUP(self), nullptr); + } + }), + nullptr); +} + +static void fcitx_client_side_popup_dispose(GObject *object) { + fcitx_client_side_popup_set_parent(FCITX_CLIENT_SIDE_POPUP(object), + nullptr); + G_OBJECT_CLASS(fcitx_client_side_popup_parent_class)->dispose(object); +} + +static void +fcitx_client_side_popup_class_init(FcitxClientSidePopupClass *klass) { + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + // Hook up our overridden functions + widget_class->snapshot = fcitx_client_side_popup_snapshot; + G_OBJECT_CLASS(klass)->dispose = fcitx_client_side_popup_dispose; +} + +GtkWidget *fcitx_client_side_popup_new(void) { + return GTK_WIDGET(g_object_new(FCITX_TYPE_CLIENT_SIDE_POPUP, nullptr)); +} namespace fcitx::gtk { +static void ensureFcitxPopupCss() { + static GtkCssProvider *provider = nullptr; + auto *display = gdk_display_get_default(); + if (provider || !display) { + return; + } + + provider = gtk_css_provider_new(); + const char *css = R"css( +popover.fcitx-popup contents { + box-shadow: none; + padding: 0px; + border-radius: 0px; + background-color: transparent; + background-clip: border-box; + border: none; +} +)css"; + gtk_css_provider_load_from_string(provider, css); + gtk_style_context_add_provider_for_display( + display, GTK_STYLE_PROVIDER(provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); +} + Gtk4InputWindow::Gtk4InputWindow(ClassicUIConfig *config, FcitxGClient *client) : InputWindow(config, client) { rect_.x = rect_.y = rect_.height = rect_.width = 0; - dummyWidget_.reset(GTK_WINDOW(gtk_window_new())); } Gtk4InputWindow::~Gtk4InputWindow() { - if (window_) { - g_signal_handlers_disconnect_by_data(window_.get(), this); - gdk_surface_destroy(window_.release()); - } - // Clean up weak pointer. setParent(nullptr); + + if (!window_) { + return; + } + auto *popup = FCITX_CLIENT_SIDE_POPUP(window_.get()); + popup->inputWindow = nullptr; + window_.reset(); +} + +void Gtk4InputWindow::init() { + if (!window_) { + ensureFcitxPopupCss(); + // We want to indefinitely keep the window alive through the lifetime of + // Gtk4InputWindow. + window_.reset(g_object_ref_sink(fcitx_client_side_popup_new())); + FCITX_CLIENT_SIDE_POPUP(window_.get())->inputWindow = this; + } } void Gtk4InputWindow::draw(cairo_t *cr) { paint(cr, width_, height_); } @@ -38,240 +253,118 @@ if (parent) { g_object_add_weak_pointer(G_OBJECT(parent), reinterpret_cast<gpointer *>(&parent_)); - resetWindow(); } parent_ = parent; + if (window_) { + fcitx_client_side_popup_set_parent( + FCITX_CLIENT_SIDE_POPUP(window_.get()), parent); + } } void Gtk4InputWindow::setCursorRect(GdkRectangle rect) { if (!parent_) { return; } - auto *root = gtk_widget_get_native(parent_); - if (!root) { - return; - } - - double px, py; - gtk_widget_translate_coordinates(parent_, GTK_WIDGET(root), rect.x, rect.y, - &px, &py); - double offsetX = 0, offsetY = 0; - - if (auto native = gtk_widget_get_native(GTK_WIDGET(root))) { - gtk_native_get_surface_transform(native, &offsetX, &offsetY); - } - rect.x = px + offsetX; - rect.y = py + offsetY; - - // Sanitize the rect value to workaround a mutter bug: - // https://gitlab.gnome.org/GNOME/mutter/-/issues/2525 - // The procedure make sure the rect is with in the parent window region. - const int rootWidth = gtk_widget_get_width(GTK_WIDGET(root)); - const int rootHeight = gtk_widget_get_height(GTK_WIDGET(root)); - if (rootWidth <= 0 || rootHeight <= 0) { - return; - } - rect.x = CLAMP(rect.x, 0, rootWidth - 1); - rect.y = CLAMP(rect.y, 0, rootHeight - 1); - rect.width = CLAMP(rect.width, 1, rootWidth - rect.x); - rect.height = CLAMP(rect.height, 1, rootHeight - rect.y); rect_ = rect; - - if (window_) { - reposition(); - } + reposition(); } void Gtk4InputWindow::update() { - if (!visible() || !parent_) { - resetWindow(); - return; - } + do { + if (!visible()) { + break; + } - syncFontOptions(); - std::tie(width_, height_) = sizeHint(); - if (width_ <= 0 || height_ <= 0) { - resetWindow(); - return; - } - auto native = gtk_widget_get_native(parent_); - if (!native) { - return; - } + init(); + syncFontOptions(); + std::tie(width_, height_) = sizeHint(); + if (width_ <= 0 || height_ <= 0) { + break; + } - auto surface = gtk_native_get_surface(native); - if (!surface) { + if (!reposition()) { + break; + } return; - } - + } while (false); if (window_) { - if (surface == gdk_popup_get_parent(GDK_POPUP(window_.get()))) { - gdk_surface_queue_render(window_.get()); - reposition(); - return; - } + gtk_popover_popdown(GTK_POPOVER(window_.get())); } - - resetWindow(); - window_.reset(gdk_surface_new_popup(surface, false)); - cairoCcontext_.reset(gdk_surface_create_cairo_context(window_.get())); - - auto mapped = [](GdkSurface *surface, GParamSpec *, gpointer user_data) { - Gtk4InputWindow *that = static_cast<Gtk4InputWindow *>(user_data); - that->surfaceNotifyMapped(surface); - }; - g_signal_connect(surface, "notify::mapped", G_CALLBACK(+mapped), this); - - auto surface_render = [](GdkSurface *surface, cairo_region_t *, - gpointer user_data) { - Gtk4InputWindow *that = static_cast<Gtk4InputWindow *>(user_data); - cairo_rectangle_int_t r; - r.x = 0; - r.y = 0; - r.width = gdk_surface_get_width(surface); - r.height = gdk_surface_get_height(surface); - auto region = cairo_region_create_rectangle(&r); - gdk_draw_context_begin_frame( - GDK_DRAW_CONTEXT(that->cairoCcontext_.get()), region); - cairo_region_destroy(region); - auto cr = gdk_cairo_context_cairo_create(that->cairoCcontext_.get()); - - static_cast<Gtk4InputWindow *>(user_data)->draw(cr); - - cairo_destroy(cr); - - gdk_draw_context_end_frame( - GDK_DRAW_CONTEXT(that->cairoCcontext_.get())); - return TRUE; - }; - auto event = [](GdkSurface *, gpointer event, gpointer user_data) { - return static_cast<Gtk4InputWindow *>(user_data)->event( - static_cast<GdkEvent *>(event)); - }; - g_signal_connect(window_.get(), "render", G_CALLBACK(+surface_render), - this); - g_signal_connect(window_.get(), "event", G_CALLBACK(+event), this); - - surfaceNotifyMapped(surface); } -void Gtk4InputWindow::reposition() { - if (!window_) { - return; +bool Gtk4InputWindow::reposition() { + if (!window_ || !parent_ || width_ <= 0 || height_ <= 0 || !visible()) { + return false; } - auto popupLayout = gdk_popup_layout_new(&rect_, GDK_GRAVITY_SOUTH_WEST, - GDK_GRAVITY_NORTH_WEST); - gdk_popup_layout_set_anchor_hints( - popupLayout, - static_cast<GdkAnchorHints>(GDK_ANCHOR_SLIDE_X | GDK_ANCHOR_FLIP_Y | - GDK_ANCHOR_SLIDE_Y)); - - gdk_popup_layout_set_shadow_width( - popupLayout, config_->theme_.shadowMargin.marginLeft, - config_->theme_.shadowMargin.marginRight, - config_->theme_.shadowMargin.marginTop, - config_->theme_.shadowMargin.marginBottom); - gdk_popup_present(GDK_POPUP(window_.get()), width_, height_, popupLayout); - gdk_popup_layout_unref(popupLayout); -} + // Check if parent is visible and mapped. If not, we should not pop up the + // candidate. + if (!gtk_widget_is_visible(parent_)) { + return false; + } -void Gtk4InputWindow::surfaceNotifyMapped(GdkSurface *surface) { - if (surface != gdk_popup_get_parent(GDK_POPUP(window_.get()))) { - return; + auto *native = gtk_widget_get_native(parent_); + if (!native) { + return false; } - if (!window_) { - return; + auto *surface = gtk_native_get_surface(GTK_NATIVE(native)); + if (!surface) { + return false; } if (!gdk_surface_get_mapped(surface)) { - resetWindow(); - return; - } else if (visible()) { - reposition(); + return false; } -} -void Gtk4InputWindow::resetWindow() { - if (!window_) { - return; - } - if (auto parent = gdk_popup_get_parent(GDK_POPUP(window_.get()))) { - g_signal_handlers_disconnect_by_data(parent, this); - g_signal_handlers_disconnect_by_data(window_.get(), this); - cairoCcontext_.reset(); - window_.reset(); - } + fcitx_client_side_popup_set_parent(FCITX_CLIENT_SIDE_POPUP(window_.get()), + parent_); + gtk_widget_set_size_request(window_.get(), width_, height_); + gtk_popover_set_pointing_to(GTK_POPOVER(window_.get()), &rect_); + gtk_popover_popup(GTK_POPOVER(window_.get())); + return true; } void Gtk4InputWindow::syncFontOptions() { - auto context = gtk_widget_get_pango_context(GTK_WIDGET(dummyWidget_.get())); + auto *context = gtk_widget_get_pango_context(window_.get()); + if (!context) { + return; + } pango_cairo_context_set_font_options( context_.get(), pango_cairo_context_get_font_options(context)); dpi_ = pango_cairo_context_get_resolution(context); pango_cairo_context_set_resolution(context_.get(), dpi_); } -gboolean Gtk4InputWindow::event(GdkEvent *event) { - auto eventType = gdk_event_get_event_type(event); - if (eventType == GDK_MOTION_NOTIFY) { - double x = 0, y = 0; - gdk_event_get_position(event, &x, &y); - if (hover(x, y)) { - gdk_surface_queue_render(window_.get()); - } - } else if (eventType == GDK_LEAVE_NOTIFY) { - auto oldHighlight = highlight(); - hoverIndex_ = -1; - if (highlight() != oldHighlight) { - gdk_surface_queue_render(window_.get()); - } - return true; - } else if (eventType == GDK_SCROLL) { - double vscroll_factor = 0.0; - double x_scroll, y_scroll; - // Handle discrete scrolling with a known constant delta; - const double delta = 1.0; - - // In Gtk 4, there will be either discrete or axis event. - auto direction = gdk_scroll_event_get_direction(event); - switch (direction) { - case GDK_SCROLL_UP: - vscroll_factor = -delta; - break; - case GDK_SCROLL_DOWN: - vscroll_factor = delta; - break; - case GDK_SCROLL_SMOOTH: - gdk_scroll_event_get_deltas(event, &x_scroll, &y_scroll); - // Handle smooth scrolling directly - vscroll_factor = y_scroll; - break; - default: - // no scrolling - break; - } - if (vscroll_factor != 0) { - scrollDelta_ += vscroll_factor; - while (scrollDelta_ >= delta) { - scrollDelta_ -= delta; - next(); - } - while (scrollDelta_ <= -delta) { - scrollDelta_ += delta; - prev(); - } - } - return true; - } else if (eventType == GDK_BUTTON_RELEASE) { - guint button = gdk_button_event_get_button(event); - if (button == 1) { - double x = 0, y = 0; - gdk_event_get_position(event, &x, &y); - click(x, y); - } +void Gtk4InputWindow::motion(double x, double y) { + if (hover(x, y)) { + gtk_widget_queue_draw(GTK_WIDGET(window_.get())); + } +} + +void Gtk4InputWindow::leave() { + auto oldHighlight = highlight(); + hoverIndex_ = -1; + if (highlight() != oldHighlight) { + gtk_widget_queue_draw(GTK_WIDGET(window_.get())); + } +} + +bool Gtk4InputWindow::scroll(double /*x*/, double y) { + if (y == 0) { + return false; + } + + scrollDelta_ += y; + const double delta = 1.0; + while (scrollDelta_ >= delta) { + scrollDelta_ -= delta; + next(); + } + while (scrollDelta_ <= -delta) { + scrollDelta_ += delta; + prev(); } - return false; + return true; } } // namespace fcitx::gtk diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fcitx5-gtk-5.1.5/gtk4/gtk4inputwindow.h new/fcitx5-gtk-5.1.7/gtk4/gtk4inputwindow.h --- old/fcitx5-gtk-5.1.5/gtk4/gtk4inputwindow.h 2025-12-22 07:18:11.000000000 +0100 +++ new/fcitx5-gtk-5.1.7/gtk4/gtk4inputwindow.h 2026-06-20 06:19:08.000000000 +0200 @@ -15,31 +15,30 @@ class Gtk4InputWindow : public InputWindow { public: Gtk4InputWindow(ClassicUIConfig *config, FcitxGClient *client); - ~Gtk4InputWindow(); void setParent(GtkWidget *parent); void update() override; void setCursorRect(GdkRectangle rect); + void draw(cairo_t *cr); + + void motion(double x, double y); + void leave(); + bool scroll(double x, double y); private: - void draw(cairo_t *cr); - gboolean event(GdkEvent *event); - void reposition(); - void surfaceNotifyMapped(GdkSurface *surface); - void resetWindow(); + void init(); + bool reposition(); void syncFontOptions(); bool supportAlpha = false; // Dummy widget to track font options. - UniqueCPtr<GtkWindow, gtk_window_destroy> dummyWidget_; - UniqueCPtr<GdkSurface, gdk_surface_destroy> window_; - UniqueCPtr<GdkCairoContext, g_object_unref> cairoCcontext_; - GtkWidget *parent_ = nullptr; + GObjectUniquePtr<GtkWidget> window_; size_t width_ = 1; size_t height_ = 1; GdkRectangle rect_; double scrollDelta_ = 0; + GtkWidget *parent_ = nullptr; }; } // namespace fcitx::gtk
