From 0e74cf4ca026671cf97ffa5a4bc3597ee681b766 Mon Sep 17 00:00:00 2001 From: Konkitoman Date: Thu, 27 Jul 2023 15:08:21 +0300 Subject: [PATCH] Some work making the native window to look like the egui one, and to work the same --- crates/eframe/src/epi/mod.rs | 2 +- crates/eframe/src/native/run.rs | 44 +++- crates/egui/src/containers/window.rs | 298 ++++++++++++++++++++++++--- 3 files changed, 308 insertions(+), 36 deletions(-) diff --git a/crates/eframe/src/epi/mod.rs b/crates/eframe/src/epi/mod.rs index b27defddb..184b7d8d9 100644 --- a/crates/eframe/src/epi/mod.rs +++ b/crates/eframe/src/epi/mod.rs @@ -513,7 +513,7 @@ impl Default for NativeOptions { min_window_size: None, max_window_size: None, resizable: true, - transparent: false, + transparent: true, mouse_passthrough: false, active: true, diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 02938f788..f66efddfb 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -424,7 +424,10 @@ mod glow_integration { prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, surface::GlSurface, }; - use winit::dpi::{PhysicalPosition, PhysicalSize}; + use winit::{ + dpi::{PhysicalPosition, PhysicalSize}, + window::ResizeDirection, + }; use super::*; @@ -1252,6 +1255,45 @@ mod glow_integration { } } } + egui::window::ViewportCommand::InnerSize(width, height) => { + win.set_inner_size(PhysicalSize::new(width, height)); + } + egui::window::ViewportCommand::Resize( + top, + bottom, + right, + left, + ) => { + win.drag_resize_window( + match (top, bottom, right, left) { + (true, false, false, false) => { + ResizeDirection::North + } + (false, true, false, false) => { + ResizeDirection::South + } + (false, false, true, false) => { + ResizeDirection::East + } + (false, false, false, true) => { + ResizeDirection::West + } + (true, false, true, false) => { + ResizeDirection::NorthEast + } + (false, true, true, false) => { + ResizeDirection::SouthEast + } + (true, false, false, true) => { + ResizeDirection::NorthWest + } + (false, true, false, true) => { + ResizeDirection::SouthWest + } + _ => ResizeDirection::East, + }, + ); + } } } break; diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 1cde8a076..a42e1d560 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -147,6 +147,9 @@ impl ViewportBuilder { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum ViewportCommand { Drag, + InnerSize(u32, u32), + /// Top, Bottom, Right, Left + Resize(bool, bool, bool, bool), } /// Builder for a floating window which can be dragged, closed, collapsed, resized and scrolled (off by default). @@ -195,7 +198,9 @@ impl<'open> Window<'open> { .with_stroke(false) .min_size([96.0, 32.0]) .default_size([340.0, 420.0]), // Default inner size of a window - scroll: ScrollArea::neither(), + scroll: ScrollArea::both() + .auto_shrink([false, false]) + .enable_scrolling(true), collapsible: true, default_open: true, with_title_bar: true, @@ -461,15 +466,19 @@ impl<'open> Window<'open> { true }; - // let is_explicitly_closed = matches!(open, Some(false)); let is_open = is_open || ctx.memory(|mem| mem.everything_is_visible()); + + if !is_open { + return; + } + + let show_close_button = open.is_some(); + 'create_viewport: { if !embedded && ctx.is_desktop() { - if !is_open { - return; - } if let Some(size) = ctx.data(|data| data.get_temp::(area.id.with("size"))) { - let size = size.round() + ctx.style().spacing.window_margin.sum(); + let size = size.round() + + ctx.style().spacing.window_margin.sum() * ctx.pixels_per_point(); window_builder = window_builder.with_inner_size((size.x as u32 + 1, size.y as u32 + 1)); } else { @@ -478,36 +487,262 @@ impl<'open> Window<'open> { } window_builder.close_button = open.is_some(); window_builder.resizable = resize.is_resizable(); - - let area_id = area.id; + window_builder.decorations = !with_title_bar; + let pix = ctx.pixels_per_point(); + let min_size = resize.min_size * pix; + let max_size = resize.max_size * pix; + let max_size = if !max_size.is_finite() { + None + } else { + Some(max_size) + }; + window_builder.min_inner_size = Some((min_size.x as u32, min_size.y as u32)); + if let Some(max_size) = max_size { + window_builder.max_inner_size = Some((max_size.x as u32, max_size.y as u32)); + } ctx.create_viewport( window_builder, move |ctx, viewport_id, parent_viewport_id| { - let mut frame = frame.unwrap_or(Frame::window(&ctx.style())).rounding(0.0); - let count = ctx.input(|input| { - input - .events - .iter() - .filter(|event| { - if let Event::WindowEvent(WindowEvent::CloseRequested) = **event - { - true - } else { - false - } + let mut op = is_open; + let open = if show_close_button { + Some(&mut op) + } else { + None + }; + let scroll = scroll.clone(); + let title = title.clone(); + let frame = frame + .unwrap_or_else(|| Frame::window(&ctx.style())) + .outer_margin(0.0) + .shadow(Shadow::NONE) + .stroke(Stroke::new( + 1.0, + if ctx.input(|i| i.focused) { + Color32::BLUE + } else { + Color32::BROWN + }, + )); + + area.show_open_close_animation(ctx, &frame, is_open); + + let area_id = area.id; + let area_layer_id = area.layer(); + let resize_id = area_id.with("resize"); + let mut collapsing = CollapsingState::load_with_default_open( + ctx, + area_id.with("collapsing"), + default_open, + ); + + let is_collapsed = with_title_bar && !collapsing.is_open(); + let possible = PossibleInteractions::new(&area, &resize, is_collapsed); + + let area = area.movable(false); // We move it manually, or the area will move the window when we want to resize it + let resize = resize.resizable(false); // We move it manually + let mut resize = resize.id(resize_id); + + let mut area = area.begin(ctx); + let win_size = ctx.input(|i| i.screen_rect.size()); + area.state_mut().set_left_top_pos(Pos2::ZERO); + area.state_mut().size = win_size; + let title_content_spacing = 2.0 * ctx.style().spacing.item_spacing.y; + + let title_bar_height = if with_title_bar { + let style = ctx.style(); + ctx.fonts(|f| title.font_height(f, &style)) + title_content_spacing + } else { + 0.0 + }; + let margins = frame.outer_margin.sum() + + frame.inner_margin.sum() + + vec2(0.0, title_bar_height); + + if let Some(mut state) = resize::State::load(ctx, resize_id) { + state.requested_size = Some(win_size - margins); + state.store(ctx, resize_id); + } + + // First interact (move etc) to avoid frame delay: + let last_frame_outer_rect = area.state().rect(); + + let interaction = if possible.movable || possible.resizable() { + window_interaction( + ctx, + possible, + area_layer_id, + area_id.with("frame_resize"), + last_frame_outer_rect, + ) + .and_then(|window_interaction| { + // Calculate roughly how much larger the window size is compared to the inner rect + + let pointer_pos = ctx.input(|i| i.pointer.interact_pos())?; + let mut rect = window_interaction.start_rect; // prevent drift + + window_interaction.set_cursor(ctx); + if window_interaction.is_resize() { + ctx.viewport_command( + viewport_id, + ViewportCommand::Resize( + window_interaction.top, + window_interaction.bottom, + window_interaction.right, + window_interaction.left, + ), + ); + } else { + if ctx.input(|i| i.pointer.primary_pressed()) {} + } + ctx.memory_mut(|mem| mem.areas.move_to_top(area_layer_id)); + + Some(window_interaction) + }) + } else { + None + }; + + let hover_interaction = + resize_hover(ctx, possible, area_layer_id, last_frame_outer_rect); + + let mut area_content_ui = area.content_ui(ctx); + + let mut size = Vec2::new(1.0, 1.0); + + let content_inner = { + // BEGIN FRAME -------------------------------- + let frame_stroke = frame.stroke; + let mut frame = frame.begin(&mut area_content_ui); + + let title_bar = if with_title_bar { + let title_bar = show_title_bar( + &mut frame.content_ui, + title, + show_close_button, + &mut collapsing, + collapsible, + ); + resize.min_size.x = + resize.min_size.x.at_least(title_bar.rect.width()); // Prevent making window smaller than title bar width + Some(title_bar) + } else { + None + }; + + let (content_inner, content_response) = collapsing + .show_body_unindented(&mut frame.content_ui, |ui| { + resize.show(ui, |ui| { + if title_bar.is_some() { + ui.add_space(title_content_spacing); + } + + if scroll.has_any_bar() { + scroll + .show(ui, |ui| { + add_contents( + ui, + viewport_id, + parent_viewport_id, + ) + }) + .inner + } else { + add_contents(ui, viewport_id, parent_viewport_id) + } + }) }) - .count() - }); - if count > 0 { - ctx.data_mut(|data| { - data.insert_persisted(area_id.with("_open"), false) - }); + .map_or((None, None), |ir| (Some(ir.inner), Some(ir.response))); + if let Some(content_response) = &content_response { + size = content_response.rect.size() + } + + let outer_rect = frame.end(&mut area_content_ui).rect; + paint_resize_corner( + &mut area_content_ui, + &possible, + outer_rect, + frame_stroke, + ); + + // END FRAME -------------------------------- + + if let Some(title_bar) = title_bar { + title_bar.ui( + &mut area_content_ui, + outer_rect, + &content_response, + open, + &mut collapsing, + collapsible, + ); + } + + collapsing.store(ctx); + + if let Some(interaction) = interaction { + paint_frame_interaction( + &mut area_content_ui, + outer_rect, + interaction, + ctx.style().visuals.widgets.active, + ); + } else if let Some(hover_interaction) = hover_interaction { + if ctx.input(|i| i.pointer.has_pointer()) { + paint_frame_interaction( + &mut area_content_ui, + outer_rect, + hover_interaction, + ctx.style().visuals.widgets.hovered, + ); + } + } + content_inner + }; + + let size = ctx.round_vec_to_pixels(area_content_ui.min_size()); + + let full_response = area.end(ctx, area_content_ui); + + if !collapsing.is_open() { + let size = ctx.round_vec_to_pixels(full_response.rect.size()); + ctx.viewport_command( + viewport_id, + ViewportCommand::InnerSize(size.x as u32, size.y as u32), + ); + } + + if ctx.input(|i| i.pointer.any_pressed()) { + if let Some(interaction) = interaction { + if !interaction.is_resize() { + ctx.viewport_command(viewport_id, ViewportCommand::Drag); + println!("Drag"); + } else { + println!("Is resize") + } + } else { + println!("No interaction") + } + } + // let size = ctx.round_vec_to_pixels(full_response.rect.size()); + if win_size.x < size.x { + println!("Set size!"); + ctx.viewport_command( + viewport_id, + ViewportCommand::InnerSize(size.x as u32, win_size.y as u32), + ); + } + if win_size.y < size.y { + println!("Set size!"); + ctx.viewport_command( + viewport_id, + ViewportCommand::InnerSize(win_size.x as u32, size.y as u32), + ); + } + if show_close_button && op != is_open { + ctx.data_mut(|data| data.insert_persisted(area_id.with("_open"), op)); ctx.request_repaint_viewport(parent_viewport_id); } - CentralPanel::default().frame(frame).show(ctx, |ui| { - Some(add_contents(ui, viewport_id, parent_viewport_id)) - }); }, ); return; @@ -517,10 +752,6 @@ impl<'open> Window<'open> { area.show_open_close_animation(ctx, &frame, is_open); - if !is_open { - return; - } - let area_id = area.id; let area_layer_id = area.layer(); let resize_id = area_id.with("resize"); @@ -583,7 +814,6 @@ impl<'open> Window<'open> { let frame_stroke = frame.stroke; let mut frame = frame.begin(&mut area_content_ui); - let show_close_button = open.is_some(); let title_bar = if with_title_bar { let title_bar = show_title_bar( &mut frame.content_ui,