From 958aea922f25959ac41164eae07ae66dbd76127b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 13 Mar 2021 12:38:03 +0100 Subject: [PATCH] Add a lot more CursorIcon's --- egui/src/data/output.rs | 111 ++++++++++++++++++-- egui/src/response.rs | 10 +- egui/src/widgets/plot.rs | 2 +- egui_demo_lib/src/apps/demo/demo_windows.rs | 1 + egui_demo_lib/src/apps/demo/tests.rs | 31 ++++++ egui_glium/src/backend.rs | 25 ++--- egui_glium/src/lib.rs | 46 ++++++-- egui_web/src/lib.rs | 38 ++++--- 8 files changed, 216 insertions(+), 48 deletions(-) diff --git a/egui/src/data/output.rs b/egui/src/data/output.rs index fcbaf8ab..5e8d2866 100644 --- a/egui/src/data/output.rs +++ b/egui/src/data/output.rs @@ -74,19 +74,118 @@ impl OpenUrl { /// A mouse cursor icon. /// /// egui emits a [`CursorIcon`] in [`Output`] each frame as a request to the integration. -#[derive(Clone, Copy, PartialEq)] +/// +/// Loosely based on . +#[derive(Clone, Copy, Debug, PartialEq)] pub enum CursorIcon { + /// Normal cursor icon, whatever that is. Default, + + /// Show no cursor + None, + + // ------------------------------------ + // Links and status: + /// A context menu is available + ContextMenu, + + /// Question mark + Help, + /// Pointing hand, used for e.g. web links PointingHand, - ResizeHorizontal, - ResizeNeSw, - ResizeNwSe, - ResizeVertical, + + /// Shows that processing is being done, but that the program is still interactive. + Progress, + + /// Not yet ready, try later. + Wait, + + // ------------------------------------ + // Selection: + /// Hover a cell in a table + Cell, + + /// For precision work + Crosshair, + + /// Text caret, e.g. "Click here to edit text" Text, - /// Used when moving + + /// Vertical text caret, e.g. "Click here to edit vertical text" + VerticalText, + + // ------------------------------------ + // Drag-and-drop: + /// Indicated an alias, e.g. a shortcut + Alias, + + /// Indicate that a copy will be made + Copy, + + /// Omnidirectional move icon (e.g. arrows in all cardinal directions) + Move, + + /// Can't drop here + NoDrop, + + /// Forbidden + NotAllowed, + + /// The thing you are hovering can be grabbed Grab, + + /// You are grabbing the thing you are hovering Grabbing, + + // ------------------------------------ + // Resizing and scrolling + /// Something can be scrolled in any direction (panned). + AllScroll, + + /// Horizontal resize `-` to make something wider or more narrow (left to/from right) + ResizeHorizontal, + /// Diagonal resize `/` (right-up to/from left-down) + ResizeNeSw, + /// Diagonal resize `\` (left-up to/from right-down) + ResizeNwSe, + /// Vertical resize `|` (up-down or down-up) + ResizeVertical, + + /// Enhance! + ZoomIn, + /// Let's get a better overview + ZoomOut, +} + +impl CursorIcon { + pub const ALL: [CursorIcon; 25] = [ + CursorIcon::Default, + CursorIcon::None, + CursorIcon::ContextMenu, + CursorIcon::Help, + CursorIcon::PointingHand, + CursorIcon::Progress, + CursorIcon::Wait, + CursorIcon::Cell, + CursorIcon::Crosshair, + CursorIcon::Text, + CursorIcon::VerticalText, + CursorIcon::Alias, + CursorIcon::Copy, + CursorIcon::Move, + CursorIcon::NoDrop, + CursorIcon::NotAllowed, + CursorIcon::Grab, + CursorIcon::Grabbing, + CursorIcon::AllScroll, + CursorIcon::ResizeHorizontal, + CursorIcon::ResizeNeSw, + CursorIcon::ResizeNwSe, + CursorIcon::ResizeVertical, + CursorIcon::ZoomIn, + CursorIcon::ZoomOut, + ]; } impl Default for CursorIcon { diff --git a/egui/src/response.rs b/egui/src/response.rs index 17b158b1..20bac7ce 100644 --- a/egui/src/response.rs +++ b/egui/src/response.rs @@ -1,6 +1,6 @@ use crate::{ emath::{lerp, Align, Pos2, Rect, Vec2}, - PointerButton, NUM_POINTER_BUTTONS, + CursorIcon, PointerButton, NUM_POINTER_BUTTONS, }; use crate::{CtxRef, Id, LayerId, Sense, Ui}; @@ -302,6 +302,14 @@ impl Response { self.on_hover_text(text) } + /// When hovered, use this icon for the mouse cursor. + pub fn on_hover_cursor(self, cursor: CursorIcon) -> Self { + if self.hovered() { + self.ctx.output().cursor_icon = cursor; + } + self + } + /// Check for more interactions (e.g. sense clicks on a `Response` returned from a label). /// /// ``` diff --git a/egui/src/widgets/plot.rs b/egui/src/widgets/plot.rs index f4731fe3..277f3c8d 100644 --- a/egui/src/widgets/plot.rs +++ b/egui/src/widgets/plot.rs @@ -475,7 +475,7 @@ impl Widget for Plot { prepared.ui(ui, &response); } - response + response.on_hover_cursor(CursorIcon::Crosshair) } } diff --git a/egui_demo_lib/src/apps/demo/demo_windows.rs b/egui_demo_lib/src/apps/demo/demo_windows.rs index 453a1678..c746de69 100644 --- a/egui_demo_lib/src/apps/demo/demo_windows.rs +++ b/egui_demo_lib/src/apps/demo/demo_windows.rs @@ -26,6 +26,7 @@ impl Default for Demos { Box::new(super::window_options::WindowOptions::default()), Box::new(super::tests::WindowResizeTest::default()), // Tests: + Box::new(super::tests::CursorTest::default()), Box::new(super::tests::IdTest::default()), Box::new(super::tests::InputTest::default()), Box::new(super::layout_test::LayoutTest::default()), diff --git a/egui_demo_lib/src/apps/demo/tests.rs b/egui_demo_lib/src/apps/demo/tests.rs index d96e832f..90b3476e 100644 --- a/egui_demo_lib/src/apps/demo/tests.rs +++ b/egui_demo_lib/src/apps/demo/tests.rs @@ -1,3 +1,34 @@ +#[derive(Default)] +pub struct CursorTest {} + +impl super::Demo for CursorTest { + fn name(&self) -> &'static str { + "Cursor Test" + } + + fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) { + egui::Window::new(self.name()).open(open).show(ctx, |ui| { + use super::View; + self.ui(ui); + }); + } +} + +impl super::View for CursorTest { + fn ui(&mut self, ui: &mut egui::Ui) { + ui.heading("Hover to switch cursor icon:"); + ui.vertical_centered_justified(|ui| { + for &cursor_icon in &egui::CursorIcon::ALL { + let _ = ui + .button(format!("{:?}", cursor_icon)) + .on_hover_cursor(cursor_icon); + } + }); + } +} + +// ---------------------------------------------------------------------------- + #[derive(Default)] pub struct IdTest {} diff --git a/egui_glium/src/backend.rs b/egui_glium/src/backend.rs index 908be3b5..1d953833 100644 --- a/egui_glium/src/backend.rs +++ b/egui_glium/src/backend.rs @@ -173,7 +173,7 @@ pub fn run(mut app: Box) -> ! { let mut previous_frame_time = None; let mut painter = Painter::new(&display); let mut clipboard = init_clipboard(); - let mut previous_cursor_icon = None; + let mut current_cursor_icon = CursorIcon::Default; #[cfg(feature = "persistence")] let mut last_auto_save = Instant::now(); @@ -209,14 +209,10 @@ pub fn run(mut app: Box) -> ! { ctx.clear_animations(); let (egui_output, _shapes) = ctx.end_frame(); - let new_cursor_icon = egui_output.cursor_icon; - handle_output(egui_output, clipboard.as_mut()); - display - .gl_window() - .window() - .set_cursor_icon(translate_cursor(new_cursor_icon)); - previous_cursor_icon = Some(new_cursor_icon); + set_cursor_icon(&display, egui_output.cursor_icon); + current_cursor_icon = egui_output.cursor_icon; + handle_output(egui_output, clipboard.as_mut()); // TODO: handle app_output // eprintln!("Warmed up in {} ms", warm_up_start.elapsed().as_millis()) @@ -285,18 +281,13 @@ pub fn run(mut app: Box) -> ! { } screen_reader.speak(&egui_output.events_description()); - let new_cursor_icon = egui_output.cursor_icon; - handle_output(egui_output, clipboard.as_mut()); - - if Some(new_cursor_icon) != previous_cursor_icon { + if current_cursor_icon != egui_output.cursor_icon { // call only when changed to prevent flickering near frame boundary // when Windows OS tries to control cursor icon for window resizing - display - .gl_window() - .window() - .set_cursor_icon(translate_cursor(new_cursor_icon)); - previous_cursor_icon = Some(new_cursor_icon); + set_cursor_icon(&display, egui_output.cursor_icon); + current_cursor_icon = egui_output.cursor_icon; } + handle_output(egui_output, clipboard.as_mut()); #[cfg(feature = "persistence")] if let Some(storage) = &mut storage { diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index a01d5a62..7166388a 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -263,17 +263,43 @@ pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option { }) } -pub fn translate_cursor(cursor_icon: egui::CursorIcon) -> glutin::window::CursorIcon { +fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option { match cursor_icon { - CursorIcon::Default => glutin::window::CursorIcon::Default, - CursorIcon::PointingHand => glutin::window::CursorIcon::Hand, - CursorIcon::ResizeHorizontal => glutin::window::CursorIcon::EwResize, - CursorIcon::ResizeNeSw => glutin::window::CursorIcon::NeswResize, - CursorIcon::ResizeNwSe => glutin::window::CursorIcon::NwseResize, - CursorIcon::ResizeVertical => glutin::window::CursorIcon::NsResize, - CursorIcon::Text => glutin::window::CursorIcon::Text, - CursorIcon::Grab => glutin::window::CursorIcon::Grab, - CursorIcon::Grabbing => glutin::window::CursorIcon::Grabbing, + CursorIcon::None => None, + + CursorIcon::Alias => Some(glutin::window::CursorIcon::Alias), + CursorIcon::AllScroll => Some(glutin::window::CursorIcon::AllScroll), + CursorIcon::Cell => Some(glutin::window::CursorIcon::Cell), + CursorIcon::ContextMenu => Some(glutin::window::CursorIcon::ContextMenu), + CursorIcon::Copy => Some(glutin::window::CursorIcon::Copy), + CursorIcon::Crosshair => Some(glutin::window::CursorIcon::Crosshair), + CursorIcon::Default => Some(glutin::window::CursorIcon::Default), + CursorIcon::Grab => Some(glutin::window::CursorIcon::Grab), + CursorIcon::Grabbing => Some(glutin::window::CursorIcon::Grabbing), + CursorIcon::Help => Some(glutin::window::CursorIcon::Help), + CursorIcon::Move => Some(glutin::window::CursorIcon::Move), + CursorIcon::NoDrop => Some(glutin::window::CursorIcon::NoDrop), + CursorIcon::NotAllowed => Some(glutin::window::CursorIcon::NotAllowed), + CursorIcon::PointingHand => Some(glutin::window::CursorIcon::Hand), + CursorIcon::Progress => Some(glutin::window::CursorIcon::Progress), + CursorIcon::ResizeHorizontal => Some(glutin::window::CursorIcon::EwResize), + CursorIcon::ResizeNeSw => Some(glutin::window::CursorIcon::NeswResize), + CursorIcon::ResizeNwSe => Some(glutin::window::CursorIcon::NwseResize), + CursorIcon::ResizeVertical => Some(glutin::window::CursorIcon::NsResize), + CursorIcon::Text => Some(glutin::window::CursorIcon::Text), + CursorIcon::VerticalText => Some(glutin::window::CursorIcon::VerticalText), + CursorIcon::Wait => Some(glutin::window::CursorIcon::Wait), + CursorIcon::ZoomIn => Some(glutin::window::CursorIcon::ZoomIn), + CursorIcon::ZoomOut => Some(glutin::window::CursorIcon::ZoomOut), + } +} + +fn set_cursor_icon(display: &glium::backend::glutin::Display, cursor_icon: egui::CursorIcon) { + if let Some(cursor_icon) = translate_cursor(cursor_icon) { + display.gl_window().window().set_cursor_visible(true); + display.gl_window().window().set_cursor_icon(cursor_icon); + } else { + display.gl_window().window().set_cursor_visible(false); } } diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index e4886602..60e84623 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -283,20 +283,32 @@ where } fn cursor_web_name(cursor: egui::CursorIcon) -> &'static str { - use egui::CursorIcon::*; match cursor { - Default => "default", - PointingHand => "pointer", - ResizeHorizontal => "ew-resize", - ResizeNeSw => "nesw-resize", - ResizeNwSe => "nwse-resize", - ResizeVertical => "ns-resize", - Text => "text", - Grab => "grab", - Grabbing => "grabbing", - // "no-drop" - // "not-allowed" - // default, help, pointer, progress, wait, cell, crosshair, text, alias, copy, move + egui::CursorIcon::Alias => "alias", + egui::CursorIcon::AllScroll => "all-scroll", + egui::CursorIcon::Cell => "cell", + egui::CursorIcon::ContextMenu => "context-menu", + egui::CursorIcon::Copy => "copy", + egui::CursorIcon::Crosshair => "crosshair", + egui::CursorIcon::Default => "default", + egui::CursorIcon::Grab => "grab", + egui::CursorIcon::Grabbing => "grabbing", + egui::CursorIcon::Help => "help", + egui::CursorIcon::Move => "move", + egui::CursorIcon::NoDrop => "no-drop", + egui::CursorIcon::None => "none", + egui::CursorIcon::NotAllowed => "not-allowed", + egui::CursorIcon::PointingHand => "pointer", + egui::CursorIcon::Progress => "progress", + egui::CursorIcon::ResizeHorizontal => "ew-resize", + egui::CursorIcon::ResizeNeSw => "nesw-resize", + egui::CursorIcon::ResizeNwSe => "nwse-resize", + egui::CursorIcon::ResizeVertical => "ns-resize", + egui::CursorIcon::Text => "text", + egui::CursorIcon::VerticalText => "vertical-text", + egui::CursorIcon::Wait => "wait", + egui::CursorIcon::ZoomIn => "zoom-in", + egui::CursorIcon::ZoomOut => "zoom-out", } }