mirror of https://github.com/emilk/egui.git
Browse Source
<!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md) before opening a Pull Request! * Keep your PR:s small and focused. * The PR title is what ends up in the changelog, so make it descriptive! * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to test and add commits to your PR. * Remember to run `cargo fmt` and `cargo clippy`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review your PR, but my time is limited! --> * Closes #5053 * [x] I have followed the instructions in the PR template This fixes #5053 by adding a Sense parameter to UiBuilder, using that in Context::create_widget, so the Widget is registered with the right Sense / focusable. Additionally, I've added a ignore_focus param to create_widget, so the focus isn't surrendered / reregistered on Ui::interact_bg. The example from #5053 now works correctly: https://github.com/user-attachments/assets/a8a04b5e-7635-4e05-9ed8-e17d64910a35 <details><summary>Updated example code</summary> <p> ```rust ui.button("I can focus"); ui.scope_builder( UiBuilder::new() .sense(Sense::click()) .id_source("focus_test"), |ui| { ui.label("I can focus for a single frame"); let response = ui.interact_bg(); let t = if response.has_focus() { "has focus" } else { "doesn't have focus" }; ui.label(t); }, ); ui.button("I can't focus :("); ``` </p> </details> --- Also, I've added `Ui::interact_scope` to make it easier to read a Ui's response in advance, without having to know about the internals of how the Ui Ids get created. This makes it really easy to created interactive container elements or custom buttons, without having to use Galleys or Painter::add(Shape::Noop) to style based on the interaction. <details><summary> Example usage to create a simple button </summary> <p> ```rust use eframe::egui; use eframe::egui::{Frame, InnerResponse, Label, RichText, UiBuilder, Widget}; use eframe::NativeOptions; use egui::{CentralPanel, Sense, WidgetInfo}; pub fn main() -> eframe::Result { eframe::run_simple_native("focus test", NativeOptions::default(), |ctx, _frame| { CentralPanel::default().show(ctx, |ui| { ui.button("Regular egui Button"); custom_button(ui, |ui| { ui.label("Custom Button"); }); if custom_button(ui, |ui| { ui.label("You can even have buttons inside buttons:"); if ui.button("button inside button").clicked() { println!("Button inside button clicked!"); } }) .response .clicked() { println!("Custom button clicked!"); } }); }) } fn custom_button<R>( ui: &mut egui::Ui, content: impl FnOnce(&mut egui::Ui) -> R, ) -> InnerResponse<R> { let auto_id = ui.next_auto_id(); ui.skip_ahead_auto_ids(1); let response = ui.interact_scope( Sense::click(), UiBuilder::new().id_source(auto_id), |ui, response| { ui.style_mut().interaction.selectable_labels = false; let visuals = response .map(|r| ui.style().interact(&r)) .unwrap_or(&ui.visuals().noninteractive()); let text_color = visuals.text_color(); Frame::none() .fill(visuals.bg_fill) .stroke(visuals.bg_stroke) .rounding(visuals.rounding) .inner_margin(ui.spacing().button_padding) .show(ui, |ui| { ui.visuals_mut().override_text_color = Some(text_color); content(ui) }) .inner }, ); response .response .widget_info(|| WidgetInfo::new(egui::WidgetType::Button)); response } ``` </p> </details> https://github.com/user-attachments/assets/281bd65f-f616-4621-9764-18fd0d07698b --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>pull/5130/head
lucasmerlin
2 months ago
committed by
GitHub
11 changed files with 258 additions and 61 deletions
@ -0,0 +1,87 @@ |
|||||
|
use egui::{Frame, Label, RichText, Sense, UiBuilder, Widget}; |
||||
|
|
||||
|
/// Showcase [`egui::Ui::response`].
|
||||
|
#[derive(PartialEq, Eq, Default)] |
||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] |
||||
|
#[cfg_attr(feature = "serde", serde(default))] |
||||
|
pub struct InteractiveContainerDemo { |
||||
|
count: usize, |
||||
|
} |
||||
|
|
||||
|
impl crate::Demo for InteractiveContainerDemo { |
||||
|
fn name(&self) -> &'static str { |
||||
|
"\u{20E3} Interactive Container" |
||||
|
} |
||||
|
|
||||
|
fn show(&mut self, ctx: &egui::Context, open: &mut bool) { |
||||
|
egui::Window::new(self.name()) |
||||
|
.open(open) |
||||
|
.resizable(false) |
||||
|
.default_width(250.0) |
||||
|
.show(ctx, |ui| { |
||||
|
use crate::View as _; |
||||
|
self.ui(ui); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
impl crate::View for InteractiveContainerDemo { |
||||
|
fn ui(&mut self, ui: &mut egui::Ui) { |
||||
|
ui.vertical_centered(|ui| { |
||||
|
ui.add(crate::egui_github_link_file!()); |
||||
|
}); |
||||
|
|
||||
|
ui.horizontal_wrapped(|ui| { |
||||
|
ui.spacing_mut().item_spacing.x = 0.0; |
||||
|
ui.label("This demo showcases how to use "); |
||||
|
ui.code("Ui::response"); |
||||
|
ui.label(" to create interactive container widgets that may contain other widgets."); |
||||
|
}); |
||||
|
|
||||
|
let response = ui |
||||
|
.scope_builder( |
||||
|
UiBuilder::new() |
||||
|
.id_salt("interactive_container") |
||||
|
.sense(Sense::click()), |
||||
|
|ui| { |
||||
|
let response = ui.response(); |
||||
|
let visuals = ui.style().interact(&response); |
||||
|
let text_color = visuals.text_color(); |
||||
|
|
||||
|
Frame::canvas(ui.style()) |
||||
|
.fill(visuals.bg_fill.gamma_multiply(0.3)) |
||||
|
.stroke(visuals.bg_stroke) |
||||
|
.inner_margin(ui.spacing().menu_margin) |
||||
|
.show(ui, |ui| { |
||||
|
ui.set_width(ui.available_width()); |
||||
|
|
||||
|
ui.add_space(32.0); |
||||
|
ui.vertical_centered(|ui| { |
||||
|
Label::new( |
||||
|
RichText::new(format!("{}", self.count)) |
||||
|
.color(text_color) |
||||
|
.size(32.0), |
||||
|
) |
||||
|
.selectable(false) |
||||
|
.ui(ui); |
||||
|
}); |
||||
|
ui.add_space(32.0); |
||||
|
|
||||
|
ui.horizontal(|ui| { |
||||
|
if ui.button("Reset").clicked() { |
||||
|
self.count = 0; |
||||
|
} |
||||
|
if ui.button("+ 100").clicked() { |
||||
|
self.count += 100; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
}, |
||||
|
) |
||||
|
.response; |
||||
|
|
||||
|
if response.clicked() { |
||||
|
self.count += 1; |
||||
|
} |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue