[ui] unify floating logo hand cursors with arrow fallback

This commit is contained in:
dijunkun
2026-10-27 17:07:03 +08:00
parent bfb2e4a84e
commit 20ede65038
2 changed files with 44 additions and 2 deletions
@@ -71,6 +71,7 @@
#elif (defined(__linux__) && !defined(__OHOS__))
#include <GL/gl.h>
#include <X11/Xatom.h>
#include <X11/Xcursor/Xcursor.h>
#include <X11/Xlib.h>
#include <unistd.h>
@@ -100,6 +101,41 @@ namespace {
using namespace std::chrono_literals;
bool SupportsControlHandCursor([[maybe_unused]] const slint::Window& window) {
#if defined(_WIN32)
return LoadCursorW(nullptr, IDC_HAND) != nullptr;
#elif defined(__linux__) && !defined(__OHOS__)
// Match winit's pointer aliases, without using any move/grab fallback.
constexpr const char* names[] = {"pointer", "hand2", "hand1", "hand",
"pointing_hand"};
if (window.wayland_surface()) {
const char* theme = std::getenv("XCURSOR_THEME");
for (const char* name : names) {
if (auto* images = XcursorLibraryLoadImages(name, theme, 24)) {
const bool available = images->nimage > 0;
XcursorImagesDestroy(images);
if (available) return true;
}
}
return false;
}
std::unique_ptr<Display, decltype(&XCloseDisplay)> display{
XOpenDisplay(nullptr), XCloseDisplay};
if (!display) return false;
for (const char* name : names) {
const Cursor cursor = XcursorLibraryLoadCursor(display.get(), name);
if (cursor != None) {
XFreeCursor(display.get(), cursor);
return true;
}
}
return false;
#else
// AppKit supplies pointingHandCursor; other adapters resolve pointer.
return true;
#endif
}
std::optional<std::string> GenerateRandomPassword(std::string_view current) {
constexpr std::string_view alphabet =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -3640,6 +3676,8 @@ void GuiApplication::SyncStreamWindow() {
if (!has_sessions) need_to_create_stream_window_ = false;
if (need_to_create_stream_window_ && !ui_->stream) {
ui_->stream.emplace(ui::StreamWindow::create());
(*ui_->stream)->set_control_hand_cursor_supported(
SupportsControlHandCursor(ui_->main->window()));
#if (defined(__linux__) && !defined(__OHOS__))
(*ui_->stream)->set_custom_titlebar(use_xwayland_gui_);
#endif
+6 -2
View File
@@ -102,6 +102,7 @@ component ControlBarButton inherits Rectangle {
in property <bool> show-logo: false;
in property <bool> logo-pressed: false;
in property <bool> logo-dragging: false;
in property <bool> hand-cursor-supported: true;
in property <float> reveal-progress: 1;
in property <string> badge: "";
in property <string> tooltip: "";
@@ -227,7 +228,7 @@ component ControlBarButton inherits Rectangle {
}
touch := TouchArea {
mouse-cursor: !root.action-enabled ? default : root.show-logo ? (root.logo-pressed ? grabbing : grab) : pointer;
mouse-cursor: root.action-enabled && root.hand-cursor-supported ? pointer : default;
scroll-event(event) => { accept }
pointer-event(event) => {
root.pointer-input(event.button, event.kind, self.mouse-x, self.mouse-y);
@@ -285,6 +286,7 @@ export component StreamWindow inherits Window {
default-font-size: ImGuiFontStyle.base;
in property <bool> custom-titlebar: false;
in property <bool> control-hand-cursor-supported: true;
in property <bool> native-video-enabled: false;
// Export the actual input surface in logical pixels. Native rendering and
// input mapping must follow the same layout, including title and tab bars.
@@ -1073,7 +1075,8 @@ export component StreamWindow inherits Window {
// The logo also retains a stable owner if another button is used.
if root.remote-pointer-active || root.control-drag-pending: TouchArea {
z: 15;
mouse-cursor: root.control-drag-pending ? MouseCursor.grabbing
mouse-cursor: root.control-drag-pending
? (root.control-hand-cursor-supported ? MouseCursor.pointer : MouseCursor.default)
: root.remote-cursor-active ? RemoteCursor.shape(root.remote-cursor-shape)
: MouseCursor.default;
pointer-event(event) => {
@@ -1249,6 +1252,7 @@ export component StreamWindow inherits Window {
width: root.control-toolbar-height;
height: root.control-toolbar-height;
show-logo: true;
hand-cursor-supported: root.control-hand-cursor-supported;
logo-pressed: root.control-drag-pending;
logo-dragging: self.logo-pressed && root.control-dragging;
tooltip: root.control-expanded ? StreamStrings.collapse-control : StreamStrings.expand-control;