Resize windows by dragging any side or corner
This commit is contained in:
parent
f9bb9f71c4
commit
88bfcd585e
13 changed files with 353 additions and 16 deletions
|
@ -63,6 +63,7 @@
|
|||
let output = JSON.parse(wasm_bindgen.run_gui(g_wasm_app, JSON.stringify(input)));
|
||||
// console.log(`output: ${JSON.stringify(output)}`);
|
||||
document.body.style.cursor = from_emigui_cursor(output.cursor_icon);
|
||||
// console.log(`Translated ${output.cursor_icon} to ${document.body.style.cursor}`);
|
||||
if (output.open_url) {
|
||||
window.open(output.open_url, "_self");
|
||||
}
|
||||
|
@ -71,7 +72,10 @@
|
|||
function from_emigui_cursor(cursor) {
|
||||
if (cursor == "no_drop") { return "no-drop"; }
|
||||
else if (cursor == "not_allowed") { return "not-allowed"; }
|
||||
else if (cursor == "resize_horizontal") { return "ew-resize"; }
|
||||
else if (cursor == "resize_ne_sw") { return "nesw-resize"; }
|
||||
else if (cursor == "resize_nw_se") { return "nwse-resize"; }
|
||||
else if (cursor == "resize_vertical") { return "ns-resize"; }
|
||||
else if (cursor == "pointing_hand") { return "pointer"; }
|
||||
// TODO: more
|
||||
else {
|
||||
|
|
|
@ -15,7 +15,7 @@ This is the core library crate Emigui. It is fully platform independent without
|
|||
* [x] Kinetic windows
|
||||
* [ ] Windows should open from `UI`s and be boxed by parent ui.
|
||||
* Then we could open the example app inside a window in the example app, recursively.
|
||||
* [ ] Resize any side and corner on windows
|
||||
* [x] Resize any side and corner on windows
|
||||
* [ ] Fix autoshrink
|
||||
* [ ] Scroll areas
|
||||
* [x] Vertical scrolling
|
||||
|
|
|
@ -46,12 +46,24 @@ impl Area {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn layer(&self) -> Layer {
|
||||
Layer {
|
||||
order: self.order,
|
||||
id: self.id,
|
||||
}
|
||||
}
|
||||
|
||||
/// moveable by draggin the area?
|
||||
pub fn movable(mut self, movable: bool) -> Self {
|
||||
self.movable = movable;
|
||||
self.interactable |= movable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_movable(&self) -> bool {
|
||||
self.movable
|
||||
}
|
||||
|
||||
/// If false, clicks goes stright throught to what is behind us.
|
||||
/// Good for tooltips etc.
|
||||
pub fn interactable(mut self, interactable: bool) -> Self {
|
||||
|
|
|
@ -34,6 +34,14 @@ impl State {
|
|||
.clone()
|
||||
}
|
||||
|
||||
// Helper
|
||||
pub fn is_open(ctx: &Context, id: Id) -> Option<bool> {
|
||||
ctx.memory()
|
||||
.collapsing_headers
|
||||
.get(&id)
|
||||
.map(|state| state.open)
|
||||
}
|
||||
|
||||
pub fn toggle(&mut self, ui: &Ui) {
|
||||
self.open = !self.open;
|
||||
self.toggle_time = ui.input().time;
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::*;
|
|||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Frame {
|
||||
// On each side
|
||||
pub margin: Vec2,
|
||||
pub corner_radius: f32,
|
||||
pub fill_color: Option<Color>,
|
||||
|
@ -68,7 +69,7 @@ impl Frame {
|
|||
} = self;
|
||||
|
||||
let outer_rect = ui.available();
|
||||
let inner_rect = outer_rect.expand2(-margin);
|
||||
let inner_rect = outer_rect.shrink2(margin);
|
||||
let where_to_put_background = ui.paint_list_len();
|
||||
|
||||
let mut child_ui = ui.child_ui(inner_rect);
|
||||
|
|
|
@ -4,12 +4,17 @@ use crate::*;
|
|||
|
||||
#[derive(Clone, Copy, Debug, serde_derive::Deserialize, serde_derive::Serialize)]
|
||||
pub(crate) struct State {
|
||||
size: Vec2,
|
||||
pub(crate) size: Vec2,
|
||||
|
||||
/// Externally requested size (e.g. by Window) for the next frame
|
||||
pub(crate) requested_size: Option<Vec2>,
|
||||
}
|
||||
|
||||
// TODO: auto-shink/grow should be part of another container!
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Resize {
|
||||
id: Option<Id>,
|
||||
|
||||
/// If false, we are no enabled
|
||||
resizable: bool,
|
||||
|
||||
|
@ -34,6 +39,7 @@ pub struct Resize {
|
|||
impl Default for Resize {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
resizable: true,
|
||||
min_size: Vec2::splat(16.0),
|
||||
max_size: Vec2::infinity(),
|
||||
|
@ -49,6 +55,12 @@ impl Default for Resize {
|
|||
}
|
||||
|
||||
impl Resize {
|
||||
/// Assign an explicit and globablly unique id.
|
||||
pub fn id(mut self, id: Id) -> Self {
|
||||
self.id = Some(id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn default_width(mut self, width: f32) -> Self {
|
||||
self.default_size.x = width;
|
||||
self
|
||||
|
@ -81,6 +93,10 @@ impl Resize {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
self.resizable
|
||||
}
|
||||
|
||||
/// Not resizable, just takes the size of its contents.
|
||||
pub fn auto_sized(self) -> Self {
|
||||
self.default_size(Vec2::splat(f32::INFINITY))
|
||||
|
@ -152,10 +168,9 @@ impl Resize {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: a common trait for Things that follow this pattern
|
||||
impl Resize {
|
||||
pub fn show(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
|
||||
let id = ui.make_child_id("resize");
|
||||
let id = self.id.unwrap_or_else(|| ui.make_child_id("resize"));
|
||||
self.min_size = self.min_size.min(ui.available().size());
|
||||
self.max_size = self.max_size.min(ui.available().size());
|
||||
self.max_size = self.max_size.max(self.min_size);
|
||||
|
@ -164,7 +179,13 @@ impl Resize {
|
|||
Some(state) => (false, *state),
|
||||
None => {
|
||||
let default_size = self.default_size.clamp(self.min_size..=self.max_size);
|
||||
(true, State { size: default_size })
|
||||
(
|
||||
true,
|
||||
State {
|
||||
size: default_size,
|
||||
requested_size: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -199,6 +220,14 @@ impl Resize {
|
|||
None
|
||||
};
|
||||
|
||||
if let Some(requested_size) = state.requested_size.take() {
|
||||
state.size = requested_size;
|
||||
// We don't clamp to max size, because we want to be able to push against outer bounds.
|
||||
// For instance, if we are inside a bigger Resize region, we want to expand that.
|
||||
// state.size = state.size.clamp(self.min_size..=self.max_size);
|
||||
state.size = state.size.max(self.min_size);
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
let inner_rect = Rect::from_min_size(position, state.size);
|
||||
|
|
|
@ -100,10 +100,14 @@ impl ScrollArea {
|
|||
inner_rect.size() + vec2(current_scroll_bar_width, 0.0),
|
||||
);
|
||||
|
||||
let content_interact = outer_ui.interact_rect(inner_rect, scroll_area_id.with("area"));
|
||||
if content_interact.active {
|
||||
// Dragging scroll area to scroll:
|
||||
state.offset.y -= ctx.input().mouse_move.y;
|
||||
let content_is_too_small = content_size.y > inner_size.y;
|
||||
|
||||
if content_is_too_small {
|
||||
// Dragg contents to scroll (for touch screens mostly):
|
||||
let content_interact = outer_ui.interact_rect(inner_rect, scroll_area_id.with("area"));
|
||||
if content_interact.active {
|
||||
state.offset.y -= ctx.input().mouse_move.y;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check that nothing else is being inteacted with
|
||||
|
@ -111,7 +115,7 @@ impl ScrollArea {
|
|||
state.offset.y -= ctx.input().scroll_delta.y;
|
||||
}
|
||||
|
||||
let show_scroll_this_frame = content_size.y > inner_size.y || self.always_show_scroll;
|
||||
let show_scroll_this_frame = content_is_too_small || self.always_show_scroll;
|
||||
if show_scroll_this_frame || state.show_scroll {
|
||||
let left = inner_rect.right() + 2.0;
|
||||
let right = outer_rect.right();
|
||||
|
|
|
@ -136,15 +136,26 @@ impl<'open> Window<'open> {
|
|||
scroll,
|
||||
} = self;
|
||||
|
||||
let movable = area.is_movable();
|
||||
let area = area.movable(false); // We move it manually
|
||||
let resizable = resize.is_resizable();
|
||||
let resize = resize.resizable(false); // We move it manually
|
||||
|
||||
let window_id = Id::new(title_label.text());
|
||||
let area_layer = area.layer();
|
||||
let resize_id = window_id.with("resize");
|
||||
let collapsing_id = window_id.with("collapsing");
|
||||
|
||||
let resize = resize.id(resize_id);
|
||||
|
||||
if matches!(open, Some(false)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
||||
|
||||
Some(area.show(ctx, |ui| {
|
||||
let full_interact = area.show(ctx, |ui| {
|
||||
frame.show(ui, |ui| {
|
||||
let collapsing_id = ui.make_child_id("collapsing");
|
||||
let default_expanded = true;
|
||||
let mut collapsing = collapsing_header::State::from_memory_with_default_open(
|
||||
ui,
|
||||
|
@ -163,6 +174,7 @@ impl<'open> Window<'open> {
|
|||
.collapsing_headers
|
||||
.insert(collapsing_id, collapsing);
|
||||
|
||||
// TODO: fix collapsing window animation
|
||||
let content = collapsing.add_contents(ui, |ui| {
|
||||
resize.show(ui, |ui| {
|
||||
ui.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
|
||||
|
@ -195,10 +207,247 @@ impl<'open> Window<'open> {
|
|||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
});
|
||||
|
||||
let resizable =
|
||||
resizable && collapsing_header::State::is_open(ctx, collapsing_id).unwrap_or_default();
|
||||
|
||||
if movable || resizable {
|
||||
let possible = PossibleInteractions { movable, resizable };
|
||||
|
||||
// TODO: not when collapsed, and not when resizing or moving is disabled etc
|
||||
let pre_resize = ctx.round_rect_to_pixels(full_interact.rect);
|
||||
let new_rect = resize_window(
|
||||
ctx,
|
||||
possible,
|
||||
area_layer,
|
||||
window_id.with("frame_resize"),
|
||||
pre_resize,
|
||||
);
|
||||
let new_rect = ctx.round_rect_to_pixels(new_rect);
|
||||
if new_rect != pre_resize {
|
||||
let mut area_state = ctx.memory().areas.get(area_layer.id).unwrap();
|
||||
area_state.pos = new_rect.min;
|
||||
ctx.memory().areas.set_state(area_layer, area_state);
|
||||
|
||||
let mut resize_state = ctx.memory().resize.get(&resize_id).cloned().unwrap();
|
||||
// resize_state.size += new_rect.size() - pre_resize.size();
|
||||
// resize_state.size = new_rect.size() - some margin;
|
||||
resize_state.requested_size =
|
||||
Some(resize_state.size + new_rect.size() - pre_resize.size());
|
||||
ctx.memory().resize.insert(resize_id, resize_state);
|
||||
|
||||
ctx.memory().areas.move_to_top(area_layer);
|
||||
}
|
||||
}
|
||||
|
||||
Some(full_interact)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct PossibleInteractions {
|
||||
movable: bool,
|
||||
resizable: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FrameInteraction {
|
||||
area_layer: Layer,
|
||||
start_rect: Rect,
|
||||
start_mouse_pos: Pos2,
|
||||
left: bool,
|
||||
right: bool,
|
||||
top: bool,
|
||||
bottom: bool,
|
||||
}
|
||||
|
||||
impl FrameInteraction {
|
||||
pub fn set_cursor(&self, ctx: &Context) {
|
||||
if (self.left && self.top) || (self.right && self.bottom) {
|
||||
ctx.output().cursor_icon = CursorIcon::ResizeNwSe;
|
||||
} else if (self.right && self.top) || (self.left && self.bottom) {
|
||||
ctx.output().cursor_icon = CursorIcon::ResizeNeSw;
|
||||
} else if self.left || self.right {
|
||||
ctx.output().cursor_icon = CursorIcon::ResizeHorizontal;
|
||||
} else if self.bottom || self.top {
|
||||
ctx.output().cursor_icon = CursorIcon::ResizeVertical;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_resize(&self) -> bool {
|
||||
self.left || self.right || self.top || self.bottom
|
||||
}
|
||||
|
||||
pub fn is_pure_move(&self) -> bool {
|
||||
!self.is_resize()
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_window(
|
||||
ctx: &Context,
|
||||
possible: PossibleInteractions,
|
||||
area_layer: Layer,
|
||||
id: Id,
|
||||
mut rect: Rect,
|
||||
) -> Rect {
|
||||
if let Some(frame_interaction) = frame_interaction(ctx, possible, area_layer, id, rect) {
|
||||
frame_interaction.set_cursor(ctx);
|
||||
if let Some(mouse_pos) = ctx.input().mouse_pos {
|
||||
rect = frame_interaction.start_rect; // prevent drift
|
||||
|
||||
if frame_interaction.is_resize() {
|
||||
if frame_interaction.left {
|
||||
rect.min.x = ctx.round_to_pixel(mouse_pos.x);
|
||||
} else if frame_interaction.right {
|
||||
rect.max.x = ctx.round_to_pixel(mouse_pos.x);
|
||||
}
|
||||
|
||||
if frame_interaction.top {
|
||||
rect.min.y = ctx.round_to_pixel(mouse_pos.y);
|
||||
} else if frame_interaction.bottom {
|
||||
rect.max.y = ctx.round_to_pixel(mouse_pos.y);
|
||||
}
|
||||
} else {
|
||||
// movevement
|
||||
rect = rect.translate(mouse_pos - frame_interaction.start_mouse_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
fn frame_interaction(
|
||||
ctx: &Context,
|
||||
possible: PossibleInteractions,
|
||||
area_layer: Layer,
|
||||
id: Id,
|
||||
rect: Rect,
|
||||
) -> Option<FrameInteraction> {
|
||||
{
|
||||
let active_id = ctx.memory().active_id;
|
||||
if active_id.is_none() {
|
||||
let frame_interaction = ctx.memory().frame_interaction;
|
||||
if let Some(frame_interaction) = frame_interaction {
|
||||
if frame_interaction.area_layer == area_layer {
|
||||
eprintln!("Letting go of window");
|
||||
if frame_interaction.is_pure_move() {
|
||||
// Throw window:
|
||||
let mut area_state = ctx.memory().areas.get(area_layer.id).unwrap();
|
||||
area_state.vel = ctx.input().mouse_velocity;
|
||||
eprintln!("Throwing window with velocity {:?}", area_state.vel);
|
||||
ctx.memory().areas.set_state(area_layer, area_state);
|
||||
}
|
||||
ctx.memory().frame_interaction = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if active_id.is_some() && active_id != Some(id) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut frame_interaction = { ctx.memory().frame_interaction.clone() };
|
||||
|
||||
if frame_interaction.is_none() {
|
||||
if let Some(hover_frame_interaction) = resize_hover(ctx, possible, area_layer, rect) {
|
||||
hover_frame_interaction.set_cursor(ctx);
|
||||
if ctx.input().mouse_pressed {
|
||||
ctx.memory().active_id = Some(id);
|
||||
frame_interaction = Some(hover_frame_interaction);
|
||||
ctx.memory().frame_interaction = frame_interaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(frame_interaction) = frame_interaction {
|
||||
let is_active = ctx.memory().active_id == Some(id);
|
||||
|
||||
if is_active && frame_interaction.area_layer == area_layer {
|
||||
return Some(frame_interaction);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn resize_hover(
|
||||
ctx: &Context,
|
||||
possible: PossibleInteractions,
|
||||
area_layer: Layer,
|
||||
rect: Rect,
|
||||
) -> Option<FrameInteraction> {
|
||||
if let Some(mouse_pos) = ctx.input().mouse_pos {
|
||||
if let Some(top_layer) = ctx.memory().layer_at(mouse_pos) {
|
||||
if top_layer != area_layer && top_layer.order != Order::Background {
|
||||
return None; // Another window is on top here
|
||||
}
|
||||
}
|
||||
|
||||
let side_interact_radius = 5.0; // TODO: from style
|
||||
let corner_interact_radius = 10.0; // TODO
|
||||
if rect.expand(side_interact_radius).contains(mouse_pos) {
|
||||
let (mut left, mut right, mut top, mut bottom) = Default::default();
|
||||
if possible.resizable {
|
||||
right = (rect.right() - mouse_pos.x).abs() <= side_interact_radius;
|
||||
bottom = (rect.bottom() - mouse_pos.y).abs() <= side_interact_radius;
|
||||
|
||||
if rect.right_bottom().dist(mouse_pos) < corner_interact_radius {
|
||||
right = true;
|
||||
bottom = true;
|
||||
}
|
||||
|
||||
if possible.movable {
|
||||
left = (rect.left() - mouse_pos.x).abs() <= side_interact_radius;
|
||||
top = (rect.top() - mouse_pos.y).abs() <= side_interact_radius;
|
||||
|
||||
if rect.right_top().dist(mouse_pos) < corner_interact_radius {
|
||||
right = true;
|
||||
top = true;
|
||||
}
|
||||
if rect.left_top().dist(mouse_pos) < corner_interact_radius {
|
||||
left = true;
|
||||
top = true;
|
||||
}
|
||||
if rect.left_bottom().dist(mouse_pos) < corner_interact_radius {
|
||||
left = true;
|
||||
bottom = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
let any_resize = left || right || top || bottom;
|
||||
|
||||
if !any_resize && !possible.movable {
|
||||
return None;
|
||||
}
|
||||
|
||||
if any_resize || possible.movable {
|
||||
Some(FrameInteraction {
|
||||
area_layer,
|
||||
start_rect: rect,
|
||||
start_mouse_pos: mouse_pos,
|
||||
left,
|
||||
right,
|
||||
top,
|
||||
bottom,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
fn show_title_bar(
|
||||
ui: &mut Ui,
|
||||
title_label: Label,
|
||||
|
|
|
@ -151,6 +151,13 @@ impl Context {
|
|||
vec2(self.round_to_pixel(vec.x), self.round_to_pixel(vec.y))
|
||||
}
|
||||
|
||||
pub fn round_rect_to_pixels(&self, rect: Rect) -> Rect {
|
||||
Rect {
|
||||
min: self.round_pos_to_pixels(rect.min),
|
||||
max: self.round_pos_to_pixels(rect.max),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
pub fn begin_frame(self: &mut Arc<Self>, new_input: RawInput) {
|
||||
|
@ -173,7 +180,9 @@ impl Context {
|
|||
if let Some(mouse_pos) = new_raw_input.mouse_pos {
|
||||
self.mouse_tracker.add(new_raw_input.time, mouse_pos);
|
||||
} else {
|
||||
self.mouse_tracker.clear();
|
||||
// we do not clear the `mouse_tracker` here, because it is exactly when a finger has
|
||||
// released from the touch screen that we may want to assign a velocity to whatever
|
||||
// the user tried to throw
|
||||
}
|
||||
let new_input = GuiInput::from_last_and_new(&self.raw_input, &new_raw_input);
|
||||
self.previus_input = std::mem::replace(&mut self.input, new_input);
|
||||
|
|
|
@ -391,6 +391,18 @@ impl Rect {
|
|||
Rect::from_min_max(self.min - amnt, self.max + amnt)
|
||||
}
|
||||
|
||||
/// Shrink by this much in each direction, keeping the center
|
||||
#[must_use]
|
||||
pub fn shrink(self, amnt: f32) -> Self {
|
||||
self.shrink2(Vec2::splat(amnt))
|
||||
}
|
||||
|
||||
/// Shrink by this much in each direction, keeping the center
|
||||
#[must_use]
|
||||
pub fn shrink2(self, amnt: Vec2) -> Self {
|
||||
Rect::from_min_max(self.min + amnt, self.max - amnt)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn translate(self, amnt: Vec2) -> Self {
|
||||
Rect::from_min_size(self.min + amnt, self.size())
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{
|
||||
containers::{area, collapsing_header, menu, resize, scroll_area},
|
||||
containers::{area, collapsing_header, menu, resize, scroll_area, window},
|
||||
widgets::text_edit,
|
||||
Id, Layer, Pos2, Rect,
|
||||
};
|
||||
|
@ -24,6 +24,9 @@ pub struct Memory {
|
|||
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,
|
||||
pub(crate) text_edit: HashMap<Id, text_edit::State>,
|
||||
|
||||
#[serde(skip)]
|
||||
pub(crate) frame_interaction: Option<window::FrameInteraction>,
|
||||
|
||||
pub(crate) areas: Areas,
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,10 @@ pub enum CursorIcon {
|
|||
Default,
|
||||
/// Pointing hand, used for e.g. web links
|
||||
PointingHand,
|
||||
ResizeHorizontal,
|
||||
ResizeNeSw,
|
||||
ResizeNwSe,
|
||||
ResizeVertical,
|
||||
Text,
|
||||
}
|
||||
|
||||
|
|
|
@ -164,7 +164,10 @@ pub fn translate_cursor(cursor_icon: emigui::CursorIcon) -> glutin::MouseCursor
|
|||
match cursor_icon {
|
||||
CursorIcon::Default => glutin::MouseCursor::Default,
|
||||
CursorIcon::PointingHand => glutin::MouseCursor::Hand,
|
||||
CursorIcon::ResizeHorizontal => glutin::MouseCursor::EwResize,
|
||||
CursorIcon::ResizeNeSw => glutin::MouseCursor::NeswResize,
|
||||
CursorIcon::ResizeNwSe => glutin::MouseCursor::NwseResize,
|
||||
CursorIcon::ResizeVertical => glutin::MouseCursor::NsResize,
|
||||
CursorIcon::Text => glutin::MouseCursor::Text,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue