mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
Replace eframe::Frame commands and WindowInfo with egui (#3564)
* Part of https://github.com/emilk/egui/issues/3556 ## In short You now almost never need to use `eframe::Frame` - instead use `ui.input(|i| i.viewport())` for information about the current viewport (native window), and use `ctx.send_viewport_cmd` to modify it. ## In detail This PR removes most commands from `eframe::Frame`, and replaces them with `ViewportCommand`. So `frame.close()` becomes `ctx.send_viewport_cmd(ViewportCommand::Close)`, etc. `frame.info().window_info` is now also gone, replaced with `ui.input(|i| i.viewport())`. `frame.info().native_pixels_per_point` is replaced with `ui.input(|i| i.raw.native_pixels_per_point)`. `RawInput` now contains one `ViewportInfo` for each viewport. Screenshots are taken with `ctx.send_viewport_cmd(ViewportCommand::Screenshots)` and are returned in `egui::Event` which you can check with: ``` ust ui.input(|i| { for event in &i.raw.events { if let egui::Event::Screenshot { viewport_id, image } = event { // handle it here } } }); ``` ### Motivation You no longer need to pass around the `&eframe::Frame` everywhere. This also opens the door for other integrations to use the same API of `ViewportCommand`s.
This commit is contained in:
@@ -27,7 +27,7 @@ impl eframe::App for MyApp {
|
||||
self.allowed_to_close
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Try to close the window");
|
||||
});
|
||||
@@ -45,7 +45,7 @@ impl eframe::App for MyApp {
|
||||
|
||||
if ui.button("Yes!").clicked() {
|
||||
self.allowed_to_close = true;
|
||||
frame.close();
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
use eframe::egui;
|
||||
use eframe::egui::{self, ViewportCommand};
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
@@ -30,8 +30,8 @@ impl eframe::App for MyApp {
|
||||
egui::Rgba::TRANSPARENT.to_array() // Make sure we don't paint anything behind the rounded corners
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
custom_window_frame(ctx, frame, "egui with custom frame", |ui| {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
custom_window_frame(ctx, "egui with custom frame", |ui| {
|
||||
ui.label("This is just the contents of the window.");
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("egui theme:");
|
||||
@@ -41,12 +41,7 @@ impl eframe::App for MyApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn custom_window_frame(
|
||||
ctx: &egui::Context,
|
||||
frame: &mut eframe::Frame,
|
||||
title: &str,
|
||||
add_contents: impl FnOnce(&mut egui::Ui),
|
||||
) {
|
||||
fn custom_window_frame(ctx: &egui::Context, title: &str, add_contents: impl FnOnce(&mut egui::Ui)) {
|
||||
use egui::*;
|
||||
|
||||
let panel_frame = egui::Frame {
|
||||
@@ -66,7 +61,7 @@ fn custom_window_frame(
|
||||
rect.max.y = rect.min.y + title_bar_height;
|
||||
rect
|
||||
};
|
||||
title_bar_ui(ui, frame, title_bar_rect, title);
|
||||
title_bar_ui(ui, title_bar_rect, title);
|
||||
|
||||
// Add the contents:
|
||||
let content_rect = {
|
||||
@@ -80,12 +75,7 @@ fn custom_window_frame(
|
||||
});
|
||||
}
|
||||
|
||||
fn title_bar_ui(
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
title_bar_rect: eframe::epaint::Rect,
|
||||
title: &str,
|
||||
) {
|
||||
fn title_bar_ui(ui: &mut egui::Ui, title_bar_rect: eframe::epaint::Rect, title: &str) {
|
||||
use egui::*;
|
||||
|
||||
let painter = ui.painter();
|
||||
@@ -112,9 +102,11 @@ fn title_bar_ui(
|
||||
|
||||
// Interact with the title bar (drag to move window):
|
||||
if title_bar_response.double_clicked() {
|
||||
frame.set_maximized(!frame.info().window_info.maximized);
|
||||
let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
|
||||
ui.ctx()
|
||||
.send_viewport_cmd(ViewportCommand::Maximized(!is_maximized));
|
||||
} else if title_bar_response.is_pointer_button_down_on() {
|
||||
frame.drag_window();
|
||||
ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag);
|
||||
}
|
||||
|
||||
ui.allocate_ui_at_rect(title_bar_rect, |ui| {
|
||||
@@ -122,13 +114,13 @@ fn title_bar_ui(
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.visuals_mut().button_frame = false;
|
||||
ui.add_space(8.0);
|
||||
close_maximize_minimize(ui, frame);
|
||||
close_maximize_minimize(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Show some close/maximize/minimize buttons for the native window.
|
||||
fn close_maximize_minimize(ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
fn close_maximize_minimize(ui: &mut egui::Ui) {
|
||||
use egui::{Button, RichText};
|
||||
|
||||
let button_height = 12.0;
|
||||
@@ -137,22 +129,24 @@ fn close_maximize_minimize(ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
.add(Button::new(RichText::new("❌").size(button_height)))
|
||||
.on_hover_text("Close the window");
|
||||
if close_response.clicked() {
|
||||
frame.close();
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
|
||||
if frame.info().window_info.maximized {
|
||||
let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
|
||||
if is_maximized {
|
||||
let maximized_response = ui
|
||||
.add(Button::new(RichText::new("🗗").size(button_height)))
|
||||
.on_hover_text("Restore window");
|
||||
if maximized_response.clicked() {
|
||||
frame.set_maximized(false);
|
||||
ui.ctx()
|
||||
.send_viewport_cmd(ViewportCommand::Maximized(false));
|
||||
}
|
||||
} else {
|
||||
let maximized_response = ui
|
||||
.add(Button::new(RichText::new("🗗").size(button_height)))
|
||||
.on_hover_text("Maximize window");
|
||||
if maximized_response.clicked() {
|
||||
frame.set_maximized(true);
|
||||
ui.ctx().send_viewport_cmd(ViewportCommand::Maximized(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +154,6 @@ fn close_maximize_minimize(ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
.add(Button::new(RichText::new("🗕").size(button_height)))
|
||||
.on_hover_text("Minimize the window");
|
||||
if minimized_response.clicked() {
|
||||
frame.set_minimized(true);
|
||||
ui.ctx().send_viewport_cmd(ViewportCommand::Minimized(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ impl eframe::App for MyApp {
|
||||
ui.label("Hello from immediate viewport");
|
||||
});
|
||||
|
||||
if ctx.input(|i| i.raw.viewport.close_requested) {
|
||||
if ctx.input(|i| i.viewport().close_requested) {
|
||||
// Tell parent viewport that we should not show next frame:
|
||||
self.show_immediate_viewport = false;
|
||||
ctx.request_repaint(); // make sure there is a next frame
|
||||
@@ -90,7 +90,7 @@ impl eframe::App for MyApp {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.label("Hello from deferred viewport");
|
||||
});
|
||||
if ctx.input(|i| i.raw.viewport.close_requested) {
|
||||
if ctx.input(|i| i.viewport().close_requested) {
|
||||
// Tell parent to close us.
|
||||
show_deferred_viewport.store(false, Ordering::Relaxed);
|
||||
ctx.request_repaint(); // make sure there is a next frame
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
use eframe::egui;
|
||||
use eframe::egui::ColorImage;
|
||||
use egui_plot::{Legend, Line, Plot, PlotPoints};
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
@@ -19,16 +18,14 @@ fn main() -> Result<(), eframe::Error> {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyApp {
|
||||
screenshot: Option<ColorImage>,
|
||||
}
|
||||
struct MyApp {}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
let mut plot_rect = None;
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
if ui.button("Save Plot").clicked() {
|
||||
frame.request_screenshot();
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
|
||||
}
|
||||
|
||||
let my_plot = Plot::new("My Plot").legend(Legend::default());
|
||||
@@ -42,15 +39,25 @@ impl eframe::App for MyApp {
|
||||
plot_rect = Some(inner.response.rect);
|
||||
});
|
||||
|
||||
if let (Some(screenshot), Some(plot_location)) = (self.screenshot.take(), plot_rect) {
|
||||
// Check for returned screenshot:
|
||||
let screenshot = ctx.input(|i| {
|
||||
for event in &i.raw.events {
|
||||
if let egui::Event::Screenshot { image, .. } = event {
|
||||
return Some(image.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
if let (Some(screenshot), Some(plot_location)) = (screenshot, plot_rect) {
|
||||
if let Some(mut path) = rfd::FileDialog::new().save_file() {
|
||||
path.set_extension("png");
|
||||
|
||||
// for a full size application, we should put this in a different thread,
|
||||
// so that the GUI doesn't lag during saving
|
||||
|
||||
let pixels_per_point = frame.info().native_pixels_per_point;
|
||||
let plot = screenshot.region(&plot_location, pixels_per_point);
|
||||
let pixels_per_point = ctx.pixels_per_point();
|
||||
let plot = screenshot.region(&plot_location, Some(pixels_per_point));
|
||||
// save the plot to png
|
||||
image::save_buffer(
|
||||
&path,
|
||||
@@ -60,14 +67,8 @@ impl eframe::App for MyApp {
|
||||
image::ColorType::Rgba8,
|
||||
)
|
||||
.unwrap();
|
||||
eprintln!("Image saved to {path:?}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn post_rendering(&mut self, _screen_size_px: [u32; 2], frame: &eframe::Frame) {
|
||||
// this is inspired by the Egui screenshot example
|
||||
if let Some(screenshot) = frame.screenshot() {
|
||||
self.screenshot = Some(screenshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use eframe::egui::{self, ColorImage};
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
@@ -19,12 +21,12 @@ fn main() -> Result<(), eframe::Error> {
|
||||
struct MyApp {
|
||||
continuously_take_screenshots: bool,
|
||||
texture: Option<egui::TextureHandle>,
|
||||
screenshot: Option<ColorImage>,
|
||||
screenshot: Option<Arc<ColorImage>>,
|
||||
save_to_file: bool,
|
||||
}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
if let Some(screenshot) = self.screenshot.take() {
|
||||
self.texture = Some(ui.ctx().load_texture(
|
||||
@@ -42,7 +44,7 @@ impl eframe::App for MyApp {
|
||||
|
||||
if ui.button("save to 'top_left.png'").clicked() {
|
||||
self.save_to_file = true;
|
||||
frame.request_screenshot();
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
|
||||
}
|
||||
|
||||
ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| {
|
||||
@@ -55,9 +57,9 @@ impl eframe::App for MyApp {
|
||||
} else {
|
||||
ctx.set_visuals(egui::Visuals::light());
|
||||
};
|
||||
frame.request_screenshot();
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
|
||||
} else if ui.button("take screenshot!").clicked() {
|
||||
frame.request_screenshot();
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -68,28 +70,33 @@ impl eframe::App for MyApp {
|
||||
ui.spinner();
|
||||
}
|
||||
|
||||
// Check for returned screenshot:
|
||||
ui.input(|i| {
|
||||
for event in &i.raw.events {
|
||||
if let egui::Event::Screenshot { image, .. } = event {
|
||||
if self.save_to_file {
|
||||
let pixels_per_point = i.pixels_per_point();
|
||||
let region = egui::Rect::from_two_pos(
|
||||
egui::Pos2::ZERO,
|
||||
egui::Pos2 { x: 100., y: 100. },
|
||||
);
|
||||
let top_left_corner = image.region(®ion, Some(pixels_per_point));
|
||||
image::save_buffer(
|
||||
"top_left.png",
|
||||
top_left_corner.as_raw(),
|
||||
top_left_corner.width() as u32,
|
||||
top_left_corner.height() as u32,
|
||||
image::ColorType::Rgba8,
|
||||
)
|
||||
.unwrap();
|
||||
self.save_to_file = false;
|
||||
}
|
||||
self.screenshot = Some(image.clone());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ctx.request_repaint();
|
||||
});
|
||||
}
|
||||
|
||||
fn post_rendering(&mut self, _window_size: [u32; 2], frame: &eframe::Frame) {
|
||||
if let Some(screenshot) = frame.screenshot() {
|
||||
if self.save_to_file {
|
||||
let pixels_per_point = frame.info().native_pixels_per_point;
|
||||
let region =
|
||||
egui::Rect::from_two_pos(egui::Pos2::ZERO, egui::Pos2 { x: 100., y: 100. });
|
||||
let top_left_corner = screenshot.region(®ion, pixels_per_point);
|
||||
image::save_buffer(
|
||||
"top_left.png",
|
||||
top_left_corner.as_raw(),
|
||||
top_left_corner.width() as u32,
|
||||
top_left_corner.height() as u32,
|
||||
image::ColorType::Rgba8,
|
||||
)
|
||||
.unwrap();
|
||||
self.save_to_file = false;
|
||||
}
|
||||
self.screenshot = Some(screenshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ struct MyApp {
|
||||
}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let label_text = if self.has_next {
|
||||
"When this window is closed the next will be opened after a short delay"
|
||||
@@ -56,7 +56,7 @@ impl eframe::App for MyApp {
|
||||
ui.label(label_text);
|
||||
if ui.button("Close").clicked() {
|
||||
eprintln!("Pressed Close button");
|
||||
frame.close();
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -221,14 +221,14 @@ fn generic_ui(ui: &mut egui::Ui, children: &[Arc<RwLock<ViewportState>>]) {
|
||||
|
||||
ui.add_space(8.0);
|
||||
|
||||
if let Some(inner_rect) = ctx.input(|i| i.raw.viewport.inner_rect_px) {
|
||||
if let Some(inner_rect) = ctx.input(|i| i.viewport().inner_rect) {
|
||||
ui.label(format!(
|
||||
"Inner Rect: Pos: {:?}, Size: {:?}",
|
||||
inner_rect.min,
|
||||
inner_rect.size()
|
||||
));
|
||||
}
|
||||
if let Some(outer_rect) = ctx.input(|i| i.raw.viewport.outer_rect_px) {
|
||||
if let Some(outer_rect) = ctx.input(|i| i.viewport().outer_rect) {
|
||||
ui.label(format!(
|
||||
"Outer Rect: Pos: {:?}, Size: {:?}",
|
||||
outer_rect.min,
|
||||
@@ -258,12 +258,12 @@ fn generic_ui(ui: &mut egui::Ui, children: &[Arc<RwLock<ViewportState>>]) {
|
||||
data.insert_temp(container_id.with("pixels_per_point"), tmp_pixels_per_point);
|
||||
});
|
||||
}
|
||||
egui::gui_zoom::zoom_with_keyboard_shortcuts(&ctx, None);
|
||||
egui::gui_zoom::zoom_with_keyboard_shortcuts(&ctx);
|
||||
|
||||
if ctx.viewport_id() != ctx.parent_viewport_id() {
|
||||
let parent = ctx.parent_viewport_id();
|
||||
if ui.button("Set parent pos 0,0").clicked() {
|
||||
ctx.send_viewport_command_to(
|
||||
ctx.send_viewport_cmd_to(
|
||||
parent,
|
||||
egui::ViewportCommand::OuterPosition(egui::pos2(0.0, 0.0)),
|
||||
);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use eframe::{
|
||||
egui::{Button, CentralPanel, Context, UserAttentionType},
|
||||
CreationContext, NativeOptions,
|
||||
};
|
||||
use eframe::{egui, CreationContext, NativeOptions};
|
||||
use egui::{Button, CentralPanel, Context, UserAttentionType};
|
||||
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
let native_options = NativeOptions {
|
||||
initial_window_size: Some(eframe::egui::vec2(400., 200.)),
|
||||
initial_window_size: Some(egui::vec2(400., 200.)),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
@@ -54,11 +52,11 @@ impl Application {
|
||||
}
|
||||
|
||||
impl eframe::App for Application {
|
||||
fn update(&mut self, ctx: &Context, frame: &mut eframe::Frame) {
|
||||
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||
if let Some(request_at) = self.request_at {
|
||||
if request_at < SystemTime::now() {
|
||||
self.request_at = None;
|
||||
frame.request_user_attention(self.attention);
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::RequestUserAttention(self.attention));
|
||||
if self.auto_reset {
|
||||
self.auto_reset = false;
|
||||
self.reset_at = Some(SystemTime::now() + Self::attention_reset_timeout());
|
||||
@@ -69,7 +67,9 @@ impl eframe::App for Application {
|
||||
if let Some(reset_at) = self.reset_at {
|
||||
if reset_at < SystemTime::now() {
|
||||
self.reset_at = None;
|
||||
frame.request_user_attention(UserAttentionType::Reset);
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::RequestUserAttention(
|
||||
UserAttentionType::Reset,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ impl eframe::App for Application {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Attention type:");
|
||||
eframe::egui::ComboBox::new("attention", "")
|
||||
egui::ComboBox::new("attention", "")
|
||||
.selected_text(repr(self.attention))
|
||||
.show_ui(ui, |ui| {
|
||||
for kind in [
|
||||
|
||||
Reference in New Issue
Block a user