//! Show a custom window frame instead of the default OS window chrome decorations. #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release use eframe::egui; fn main() -> Result<(), eframe::Error> { let options = eframe::NativeOptions { // Hide the OS-specific "chrome" around the window: decorated: false, // To have rounded corners we need transparency: transparent: true, min_window_size: Some(egui::vec2(400.0, 100.0)), initial_window_size: Some(egui::vec2(400.0, 240.0)), ..Default::default() }; eframe::run_native( "Custom window frame", // unused title options, Box::new(|_cc| Box::new(MyApp::default())), ) } #[derive(Default)] struct MyApp {} impl eframe::App for MyApp { fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { 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| { ui.label("This is just the contents of the window."); ui.horizontal(|ui| { ui.label("egui theme:"); egui::widgets::global_dark_light_mode_buttons(ui); }); }); } } fn custom_window_frame( ctx: &egui::Context, frame: &mut eframe::Frame, title: &str, add_contents: impl FnOnce(&mut egui::Ui), ) { use egui::*; let panel_frame = egui::Frame { fill: ctx.style().visuals.window_fill(), rounding: 10.0.into(), stroke: ctx.style().visuals.widgets.noninteractive.fg_stroke, outer_margin: 0.5.into(), // so the stroke is within the bounds ..Default::default() }; CentralPanel::default().frame(panel_frame).show(ctx, |ui| { let app_rect = ui.max_rect(); let title_bar_height = 32.0; let title_bar_rect = { let mut rect = app_rect; rect.max.y = rect.min.y + title_bar_height; rect }; title_bar_ui(ui, frame, title_bar_rect, title); // Add the contents: let content_rect = { let mut rect = app_rect; rect.min.y = title_bar_rect.max.y; rect } .shrink(4.0); let mut content_ui = ui.child_ui(content_rect, *ui.layout()); add_contents(&mut content_ui); }); } fn title_bar_ui( ui: &mut egui::Ui, frame: &mut eframe::Frame, title_bar_rect: eframe::epaint::Rect, title: &str, ) { use egui::*; let painter = ui.painter(); let title_bar_response = ui.interact(title_bar_rect, Id::new("title_bar"), Sense::click()); // Paint the title: painter.text( title_bar_rect.center(), Align2::CENTER_CENTER, title, FontId::proportional(20.0), ui.style().visuals.text_color(), ); // Paint the line under the title: painter.line_segment( [ title_bar_rect.left_bottom() + vec2(1.0, 0.0), title_bar_rect.right_bottom() + vec2(-1.0, 0.0), ], ui.visuals().widgets.noninteractive.bg_stroke, ); // Interact with the title bar (drag to move window): if title_bar_response.double_clicked() { frame.set_maximized(!frame.info().window_info.maximized); } else if title_bar_response.is_pointer_button_down_on() { frame.drag_window(); } ui.allocate_ui_at_rect(title_bar_rect, |ui| { ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |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); }); }); } /// Show some close/maximize/minimize buttons for the native window. fn close_maximize_minimize(ui: &mut egui::Ui, frame: &mut eframe::Frame) { use egui::{Button, RichText}; let button_height = 12.0; let close_response = ui .add(Button::new(RichText::new("❌").size(button_height))) .on_hover_text("Close the window"); if close_response.clicked() { frame.close(); } if frame.info().window_info.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); } } 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); } } let minimized_response = ui .add(Button::new(RichText::new("🗕").size(button_height))) .on_hover_text("Minimize the window"); if minimized_response.clicked() { frame.set_minimized(true); } }