From 471a91966bb65866b5ccf2caf5db1520cf9a78ce Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Mon, 31 Mar 2025 18:32:54 +0200 Subject: [PATCH] Add Atomics, IntoAtomics, Atomic, AtomicKind, implement Button with new Atomics --- crates/egui/src/containers/menu.rs | 13 +- crates/egui/src/style.rs | 2 +- crates/egui/src/widget_layout.rs | 337 ++++++++++++------ crates/egui/src/widgets/button.rs | 455 ++++++++++++------------ examples/hello_world_simple/src/main.rs | 14 +- 5 files changed, 484 insertions(+), 337 deletions(-) diff --git a/crates/egui/src/containers/menu.rs b/crates/egui/src/containers/menu.rs index 7511d07d1..36c25b667 100644 --- a/crates/egui/src/containers/menu.rs +++ b/crates/egui/src/containers/menu.rs @@ -1,7 +1,8 @@ use crate::style::StyleModifier; use crate::{ - Button, Color32, Context, Frame, Id, InnerResponse, Layout, Popup, PopupCloseBehavior, - Response, Style, Ui, UiBuilder, UiKind, UiStack, UiStackInfo, Widget, WidgetText, + Button, Color32, Context, Frame, Id, InnerResponse, IntoAtomics, Layout, Popup, + PopupCloseBehavior, Response, Style, Ui, UiBuilder, UiKind, UiStack, UiStackInfo, Widget, + WidgetText, }; use emath::{vec2, Align, RectAlign, Vec2}; use epaint::Stroke; @@ -242,8 +243,8 @@ pub struct MenuButton<'a> { } impl<'a> MenuButton<'a> { - pub fn new(text: impl Into) -> Self { - Self::from_button(Button::new(text)) + pub fn new(text: impl IntoAtomics<'a>) -> Self { + Self::from_button(Button::new(text.into_atomics())) } /// Set the config for the menu. @@ -292,8 +293,8 @@ impl<'a> SubMenuButton<'a> { /// The default right arrow symbol: `"⏵"` pub const RIGHT_ARROW: &'static str = "⏵"; - pub fn new(text: impl Into) -> Self { - Self::from_button(Button::new(text).right_text("⏵")) + pub fn new(text: impl IntoAtomics<'a>) -> Self { + Self::from_button(Button::new(text.into_atomics()).right_text("⏵")) } /// Create a new submenu button from a [`Button`]. diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 38d403749..194de5005 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -1514,7 +1514,7 @@ impl Widgets { inactive: WidgetVisuals { weak_bg_fill: Color32::from_gray(230), // button background bg_fill: Color32::from_gray(230), // checkbox background - bg_stroke: Default::default(), + bg_stroke: Stroke::new(1.0, Color32::default()), fg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // button text corner_radius: CornerRadius::same(2), expansion: 0.0, diff --git a/crates/egui/src/widget_layout.rs b/crates/egui/src/widget_layout.rs index a98fe223d..d0b5b6980 100644 --- a/crates/egui/src/widget_layout.rs +++ b/crates/egui/src/widget_layout.rs @@ -3,65 +3,44 @@ use emath::{Align2, Vec2}; use epaint::Galley; use std::sync::Arc; -/// Naming: AtimicItem -enum WidgetLayoutItemType<'a> { - Text(WidgetText), - Image(Image<'a>), - Custom(Vec2), - Grow, -} - -enum SizedWidgetLayoutItemType<'a> { +pub enum SizedAtomicKind<'a> { Text(Arc), Image(Image<'a>, Vec2), Custom(Vec2), Grow, } -struct Item { - align2: Align2, -} - -impl Default for Item { - fn default() -> Self { - Self { - align2: Align2::LEFT_CENTER, - } - } -} - -impl SizedWidgetLayoutItemType<'_> { +impl SizedAtomicKind<'_> { pub fn size(&self) -> Vec2 { match self { - SizedWidgetLayoutItemType::Text(galley) => galley.size(), - SizedWidgetLayoutItemType::Image(_, size) => *size, - SizedWidgetLayoutItemType::Custom(size) => *size, - SizedWidgetLayoutItemType::Grow => Vec2::ZERO, + SizedAtomicKind::Text(galley) => galley.size(), + SizedAtomicKind::Image(_, size) => *size, + SizedAtomicKind::Custom(size) => *size, + SizedAtomicKind::Grow => Vec2::ZERO, } } } /// AtomicLayout -struct WidgetLayout<'a> { - /// TODO: SmallVec? - items: Vec<(Item, WidgetLayoutItemType<'a>)>, +pub struct WidgetLayout<'a> { + pub atomics: Atomics<'a>, gap: f32, - frame: Frame, - sense: Sense, + pub(crate) frame: Frame, + pub(crate) sense: Sense, } impl<'a> WidgetLayout<'a> { - pub fn new() -> Self { + pub fn new(atomics: impl IntoAtomics<'a>) -> Self { Self { - items: Vec::new(), + atomics: atomics.into_atomics(), gap: 4.0, frame: Frame::default(), sense: Sense::hover(), } } - pub fn add(mut self, item: Item, kind: impl Into>) -> Self { - self.items.push((item, kind.into())); + pub fn add(mut self, atomic: impl Into>) -> Self { + self.atomics.add(atomic.into()); self } @@ -93,27 +72,25 @@ impl<'a> WidgetLayout<'a> { let mut grow_count = 0; - for (item, kind) in self.items { - let (preferred_size, sized) = match kind { - WidgetLayoutItemType::Text(text) => { + for (item) in self.atomics.0 { + let (preferred_size, sized) = match item.kind { + AtomicKind::Text(text) => { let galley = text.into_galley(ui, None, available_width, TextStyle::Button); ( galley.size(), // TODO - SizedWidgetLayoutItemType::Text(galley), + SizedAtomicKind::Text(galley), ) } - WidgetLayoutItemType::Image(image) => { + AtomicKind::Image(image) => { let size = image.load_and_calc_size(ui, Vec2::min(available_size, Vec2::splat(16.0))); let size = size.unwrap_or_default(); - (size, SizedWidgetLayoutItemType::Image(image, size)) + (size, SizedAtomicKind::Image(image, size)) } - WidgetLayoutItemType::Custom(size) => { - (size, SizedWidgetLayoutItemType::Custom(size)) - } - WidgetLayoutItemType::Grow => { + AtomicKind::Custom(size) => (size, SizedAtomicKind::Custom(size)), + AtomicKind::Grow => { grow_count += 1; - (Vec2::ZERO, SizedWidgetLayoutItemType::Grow) + (Vec2::ZERO, SizedAtomicKind::Grow) } }; let size = sized.size(); @@ -123,7 +100,7 @@ impl<'a> WidgetLayout<'a> { height = height.max(size.y); - sized_items.push((item, sized)); + sized_items.push(sized); } if sized_items.len() > 1 { @@ -147,28 +124,29 @@ impl<'a> WidgetLayout<'a> { let mut cursor = content_rect.left(); - for (item, sized) in sized_items { + for sized in sized_items { let size = sized.size(); let width = match sized { - SizedWidgetLayoutItemType::Grow => grow_width, + SizedAtomicKind::Grow => grow_width, _ => size.x, }; let frame = content_rect.with_min_x(cursor).with_max_x(cursor + width); cursor = frame.right() + self.gap; - let rect = item.align2.align_size_within_rect(size, frame); + let align = Align2::CENTER_CENTER; + let rect = align.align_size_within_rect(size, frame); match sized { - SizedWidgetLayoutItemType::Text(galley) => { + SizedAtomicKind::Text(galley) => { ui.painter() .galley(rect.min, galley, ui.visuals().text_color()); } - SizedWidgetLayoutItemType::Image(image, _) => { + SizedAtomicKind::Image(image, _) => { image.paint_at(ui, rect); } - SizedWidgetLayoutItemType::Custom(_) => {} - SizedWidgetLayoutItemType::Grow => {} + SizedAtomicKind::Custom(_) => {} + SizedAtomicKind::Grow => {} } } @@ -176,62 +154,217 @@ impl<'a> WidgetLayout<'a> { } } -pub struct WLButton<'a> { - wl: WidgetLayout<'a>, +// pub struct WLButton<'a> { +// wl: WidgetLayout<'a>, +// } +// +// impl<'a> WLButton<'a> { +// pub fn new(text: impl Into) -> Self { +// Self { +// wl: WidgetLayout::new() +// .sense(Sense::click()) +// .add(Item::default(), WidgetLayoutItemType::Text(text.into())), +// } +// } +// +// pub fn image(image: impl Into>) -> Self { +// Self { +// wl: WidgetLayout::new().sense(Sense::click()).add( +// Item::default(), +// WidgetLayoutItemType::Image(image.into().max_size(Vec2::splat(16.0))), +// ), +// } +// } +// +// pub fn image_and_text(image: impl Into>, text: impl Into) -> Self { +// Self { +// wl: WidgetLayout::new() +// .sense(Sense::click()) +// .add(Item::default(), WidgetLayoutItemType::Image(image.into())) +// .add(Item::default(), WidgetLayoutItemType::Text(text.into())), +// } +// } +// +// pub fn right_text(mut self, text: impl Into) -> Self { +// self.wl = self +// .wl +// .add(Item::default(), WidgetLayoutItemType::Grow) +// .add(Item::default(), WidgetLayoutItemType::Text(text.into())); +// self +// } +// } +// +// impl<'a> Widget for WLButton<'a> { +// fn ui(mut self, ui: &mut Ui) -> Response { +// let response = ui.ctx().read_response(ui.next_auto_id()); +// +// let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| { +// ui.style().interact(&response) +// }); +// +// self.wl.frame = self +// .wl +// .frame +// .inner_margin(ui.style().spacing.button_padding) +// .fill(visuals.bg_fill) +// .stroke(visuals.bg_stroke) +// .corner_radius(visuals.corner_radius); +// +// self.wl.show(ui) +// } +// } + +pub enum AtomicKind<'a> { + Text(WidgetText), + Image(Image<'a>), + Custom(Vec2), + Grow, } -impl<'a> WLButton<'a> { - pub fn new(text: impl Into) -> Self { - Self { - wl: WidgetLayout::new() - .sense(Sense::click()) - .add(Item::default(), WidgetLayoutItemType::Text(text.into())), - } - } +pub struct Atomic<'a> { + size: Option, + grow: bool, + pub kind: AtomicKind<'a>, +} - pub fn image(image: impl Into>) -> Self { - Self { - wl: WidgetLayout::new().sense(Sense::click()).add( - Item::default(), - WidgetLayoutItemType::Image(image.into().max_size(Vec2::splat(16.0))), - ), - } - } - - pub fn image_and_text(image: impl Into>, text: impl Into) -> Self { - Self { - wl: WidgetLayout::new() - .sense(Sense::click()) - .add(Item::default(), WidgetLayoutItemType::Image(image.into())) - .add(Item::default(), WidgetLayoutItemType::Text(text.into())), - } - } - - pub fn right_text(mut self, text: impl Into) -> Self { - self.wl = self - .wl - .add(Item::default(), WidgetLayoutItemType::Grow) - .add(Item::default(), WidgetLayoutItemType::Text(text.into())); - self +pub fn a<'a>(i: impl Into>) -> Atomic<'a> { + Atomic { + size: None, + grow: false, + kind: i.into(), } } -impl<'a> Widget for WLButton<'a> { - fn ui(mut self, ui: &mut Ui) -> Response { - let response = ui.ctx().read_response(ui.next_auto_id()); +impl Atomic<'_> { + // pub fn size(mut self, size: Vec2) -> Self { + // self.size = Some(size); + // self + // } + // + // pub fn grow(mut self, grow: bool) -> Self { + // self.grow = grow; + // self + // } +} - let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| { - ui.style().interact(&response) - }); +trait AtomicExt<'a> { + fn a_size(self, size: Vec2) -> Atomic<'a>; + fn a_grow(self, grow: bool) -> Atomic<'a>; +} - self.wl.frame = self - .wl - .frame - .inner_margin(ui.style().spacing.button_padding) - .fill(visuals.bg_fill) - .stroke(visuals.bg_stroke) - .corner_radius(visuals.corner_radius); +impl<'a, T> AtomicExt<'a> for T +where + T: Into> + Sized, +{ + fn a_size(self, size: Vec2) -> Atomic<'a> { + let mut atomic = self.into(); + atomic.size = Some(size); + atomic + } - self.wl.show(ui) + fn a_grow(self, grow: bool) -> Atomic<'a> { + let mut atomic = self.into(); + atomic.grow = grow; + atomic } } + +impl<'a, T> From for Atomic<'a> +where + T: Into>, +{ + fn from(value: T) -> Self { + Atomic { + size: None, + grow: false, + kind: value.into(), + } + } +} + +impl<'a> From> for AtomicKind<'a> { + fn from(value: Image<'a>) -> Self { + AtomicKind::Image(value) + } +} + +// impl<'a> From<&str> for AtomicKind<'a> { +// fn from(value: &str) -> Self { +// AtomicKind::Text(value.into()) +// } +// } + +impl<'a, T> From for AtomicKind<'a> +where + T: Into, +{ + fn from(value: T) -> Self { + AtomicKind::Text(value.into()) + } +} + +pub struct Atomics<'a>(Vec>); + +impl<'a> Atomics<'a> { + pub fn add(&mut self, atomic: impl Into>) { + self.0.push(atomic.into()); + } + + pub fn add_front(&mut self, atomic: impl Into>) { + self.0.insert(0, atomic.into()); + } + + pub fn iter_mut(&mut self) -> impl Iterator> { + self.0.iter_mut() + } +} + +impl<'a, T> IntoAtomics<'a> for T +where + T: Into>, +{ + fn collect(self, atomics: &mut Atomics<'a>) { + atomics.add(self); + } +} + +pub trait IntoAtomics<'a> { + fn collect(self, atomics: &mut Atomics<'a>); + + fn into_atomics(self) -> Atomics<'a> + where + Self: Sized, + { + let mut atomics = Atomics(Vec::new()); + self.collect(&mut atomics); + atomics + } +} + +impl<'a> IntoAtomics<'a> for Atomics<'a> { + fn collect(self, atomics: &mut Atomics<'a>) { + atomics.0.extend(self.0); + } +} + +macro_rules! all_the_atomics { + ($($T:ident),*) => { + impl<'a, $($T),*> IntoAtomics<'a> for ($($T),*) + where + $($T: IntoAtomics<'a>),* + { + fn collect(self, atomics: &mut Atomics<'a>) { + #[allow(non_snake_case)] + let ($($T),*) = self; + $($T.collect(atomics);)* + } + } + }; +} + +all_the_atomics!(); +all_the_atomics!(T0, T1); +all_the_atomics!(T0, T1, T2); +all_the_atomics!(T0, T1, T2, T3); +all_the_atomics!(T0, T1, T2, T3, T4); +all_the_atomics!(T0, T1, T2, T3, T4, T5); diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index c0701c193..50feabfac 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -1,6 +1,7 @@ use crate::{ - widgets, Align, Color32, CornerRadius, FontSelection, Image, NumExt, Rect, Response, Sense, - Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetType, + widgets, Align, Atomic, AtomicKind, Color32, CornerRadius, Frame, Image, IntoAtomics, NumExt, + Rect, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, + WidgetLayout, WidgetText, WidgetType, }; /// Clickable button with text. @@ -23,26 +24,35 @@ use crate::{ /// ``` #[must_use = "You should put this widget in a ui with `ui.add(widget);`"] pub struct Button<'a> { - image: Option>, - text: Option, - right_text: WidgetText, wrap_mode: Option, /// None means default for interact fill: Option, stroke: Option, - sense: Sense, small: bool, frame: Option, min_size: Vec2, corner_radius: Option, selected: bool, image_tint_follows_text_color: bool, + + wl: WidgetLayout<'a>, } impl<'a> Button<'a> { - pub fn new(text: impl Into) -> Self { - Self::opt_image_and_text(None, Some(text.into())) + pub fn new(text: impl IntoAtomics<'a>) -> Self { + Self { + wrap_mode: None, + fill: None, + stroke: None, + small: false, + frame: None, + min_size: Vec2::ZERO, + corner_radius: None, + selected: false, + image_tint_follows_text_color: false, + wl: WidgetLayout::new(text.into_atomics()).sense(Sense::click()), + } } /// Creates a button with an image. The size of the image as displayed is defined by the provided size. @@ -58,21 +68,14 @@ impl<'a> Button<'a> { } pub fn opt_image_and_text(image: Option>, text: Option) -> Self { - Self { - text, - image, - right_text: Default::default(), - wrap_mode: None, - fill: None, - stroke: None, - sense: Sense::click(), - small: false, - frame: None, - min_size: Vec2::ZERO, - corner_radius: None, - selected: false, - image_tint_follows_text_color: false, + let mut button = Self::new(()); + if let Some(image) = image { + button.wl.atomics.add(image); } + if let Some(text) = text { + button.wl.atomics.add(text); + } + button } /// Set the wrap mode for the text. @@ -106,7 +109,6 @@ impl<'a> Button<'a> { #[inline] pub fn fill(mut self, fill: impl Into) -> Self { self.fill = Some(fill.into()); - self.frame = Some(true); self } @@ -122,9 +124,6 @@ impl<'a> Button<'a> { /// Make this a small button, suitable for embedding into text. #[inline] pub fn small(mut self) -> Self { - if let Some(text) = self.text { - self.text = Some(text.text_style(TextStyle::Body)); - } self.small = true; self } @@ -140,7 +139,7 @@ impl<'a> Button<'a> { /// Change this to a drag-button with `Sense::drag()`. #[inline] pub fn sense(mut self, sense: Sense) -> Self { - self.sense = sense; + self.wl.sense = sense; self } @@ -184,15 +183,15 @@ impl<'a> Button<'a> { /// /// See also [`Self::right_text`]. #[inline] - pub fn shortcut_text(mut self, shortcut_text: impl Into) -> Self { - self.right_text = shortcut_text.into().weak(); + pub fn shortcut_text(mut self, shortcut_text: impl Into>) -> Self { + self.wl = self.wl.add(shortcut_text); self } /// Show some text on the right side of the button. #[inline] - pub fn right_text(mut self, right_text: impl Into) -> Self { - self.right_text = right_text.into(); + pub fn right_text(mut self, right_text: impl Into>) -> Self { + self.wl = self.wl.add(right_text.into()); self } @@ -205,225 +204,237 @@ impl<'a> Button<'a> { } impl Widget for Button<'_> { - fn ui(self, ui: &mut Ui) -> Response { + fn ui(mut self, ui: &mut Ui) -> Response { let Button { - text, - image, - right_text, wrap_mode, fill, stroke, - sense, small, frame, min_size, corner_radius, selected, image_tint_follows_text_color, + mut wl, } = self; - let frame = frame.unwrap_or_else(|| ui.visuals().button_frame); + let has_frame = frame.unwrap_or_else(|| ui.visuals().button_frame); - let default_font_height = || { - let font_selection = FontSelection::default(); - let font_id = font_selection.resolve(ui.style()); - ui.fonts(|f| f.row_height(&font_id)) - }; - - let text_font_height = ui - .fonts(|fonts| text.as_ref().map(|wt| wt.font_height(fonts, ui.style()))) - .unwrap_or_else(default_font_height); - - let mut button_padding = if frame { + let mut button_padding = if has_frame { ui.spacing().button_padding } else { Vec2::ZERO }; if small { button_padding.y = 0.0; + wl.atomics.iter_mut().for_each(|a| match &mut a.kind { + AtomicKind::Text(text) => { + *text = std::mem::take(text).small(); + } + _ => {} + }) } - let (space_available_for_image, right_text_font_height) = if let Some(text) = &text { - let font_height = ui.fonts(|fonts| text.font_height(fonts, ui.style())); - ( - Vec2::splat(font_height), // Reasonable? - font_height, - ) - } else { - ( - ui.available_size() - 2.0 * button_padding, - default_font_height(), - ) - }; + let response = ui.ctx().read_response(ui.next_auto_id()); - let image_size = if let Some(image) = &image { - image - .load_and_calc_size(ui, space_available_for_image) - .unwrap_or(space_available_for_image) - } else { - Vec2::ZERO - }; - - let gap_before_right_text = ui.spacing().item_spacing.x; - - let mut text_wrap_width = ui.available_width() - 2.0 * button_padding.x; - if image.is_some() { - text_wrap_width -= image_size.x + ui.spacing().icon_spacing; - } - - // Note: we don't wrap the right text - let right_galley = (!right_text.is_empty()).then(|| { - right_text.into_galley( - ui, - Some(TextWrapMode::Extend), - f32::INFINITY, - TextStyle::Button, - ) + let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| { + ui.style().interact(&response) }); - if let Some(right_galley) = &right_galley { - // Leave space for the right text: - text_wrap_width -= gap_before_right_text + right_galley.size().x; - } + wl.frame = if has_frame { + wl.frame + .inner_margin(button_padding) + .fill(fill.unwrap_or(visuals.bg_fill)) + .stroke(stroke.unwrap_or(visuals.bg_stroke)) + .corner_radius(corner_radius.unwrap_or(visuals.corner_radius)) + } else { + Frame::new() + }; - let galley = - text.map(|text| text.into_galley(ui, wrap_mode, text_wrap_width, TextStyle::Button)); + let response = wl.show(ui); - let mut desired_size = Vec2::ZERO; - if image.is_some() { - desired_size.x += image_size.x; - desired_size.y = desired_size.y.max(image_size.y); - } - if image.is_some() && galley.is_some() { - desired_size.x += ui.spacing().icon_spacing; - } - if let Some(galley) = &galley { - desired_size.x += galley.size().x; - desired_size.y = desired_size.y.max(galley.size().y).max(text_font_height); - } - if let Some(right_galley) = &right_galley { - desired_size.x += gap_before_right_text + right_galley.size().x; - desired_size.y = desired_size - .y - .max(right_galley.size().y) - .max(right_text_font_height); - } - desired_size += 2.0 * button_padding; - if !small { - desired_size.y = desired_size.y.at_least(ui.spacing().interact_size.y); - } - desired_size = desired_size.at_least(min_size); + // TODO: How to get text? + // response.widget_info(|| { + // if let Some(galley) = &galley { + // WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), galley.text()) + // } else { + // WidgetInfo::new(WidgetType::Button) + // } + // }); - let (rect, mut response) = ui.allocate_at_least(desired_size, sense); - response.widget_info(|| { - if let Some(galley) = &galley { - WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), galley.text()) - } else { - WidgetInfo::new(WidgetType::Button) - } - }); + // + // let space_available_for_image = if let Some(text) = &text { + // let font_height = ui.fonts(|fonts| text.font_height(fonts, ui.style())); + // Vec2::splat(font_height) // Reasonable? + // } else { + // ui.available_size() - 2.0 * button_padding + // }; + // + // let image_size = if let Some(image) = &image { + // image + // .load_and_calc_size(ui, space_available_for_image) + // .unwrap_or(space_available_for_image) + // } else { + // Vec2::ZERO + // }; + // + // let gap_before_right_text = ui.spacing().item_spacing.x; + // + // let mut text_wrap_width = ui.available_width() - 2.0 * button_padding.x; + // if image.is_some() { + // text_wrap_width -= image_size.x + ui.spacing().icon_spacing; + // } + // + // // Note: we don't wrap the right text + // let right_galley = (!right_text.is_empty()).then(|| { + // right_text.into_galley( + // ui, + // Some(TextWrapMode::Extend), + // f32::INFINITY, + // TextStyle::Button, + // ) + // }); + // + // if let Some(right_galley) = &right_galley { + // // Leave space for the right text: + // text_wrap_width -= gap_before_right_text + right_galley.size().x; + // } + // + // let galley = + // text.map(|text| text.into_galley(ui, wrap_mode, text_wrap_width, TextStyle::Button)); + // + // let mut desired_size = Vec2::ZERO; + // if image.is_some() { + // desired_size.x += image_size.x; + // desired_size.y = desired_size.y.max(image_size.y); + // } + // if image.is_some() && galley.is_some() { + // desired_size.x += ui.spacing().icon_spacing; + // } + // if let Some(galley) = &galley { + // desired_size.x += galley.size().x; + // desired_size.y = desired_size.y.max(galley.size().y); + // } + // if let Some(right_galley) = &right_galley { + // desired_size.x += gap_before_right_text + right_galley.size().x; + // desired_size.y = desired_size.y.max(right_galley.size().y); + // } + // desired_size += 2.0 * button_padding; + // if !small { + // desired_size.y = desired_size.y.at_least(ui.spacing().interact_size.y); + // } + // desired_size = desired_size.at_least(min_size); + // + // let (rect, mut response) = ui.allocate_at_least(desired_size, sense); + // response.widget_info(|| { + // if let Some(galley) = &galley { + // WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), galley.text()) + // } else { + // WidgetInfo::new(WidgetType::Button) + // } + // }); + // + // if ui.is_rect_visible(rect) { + // let visuals = ui.style().interact(&response); + // + // let (frame_expansion, frame_cr, frame_fill, frame_stroke) = if selected { + // let selection = ui.visuals().selection; + // ( + // Vec2::ZERO, + // CornerRadius::ZERO, + // selection.bg_fill, + // selection.stroke, + // ) + // } else if frame { + // let expansion = Vec2::splat(visuals.expansion); + // ( + // expansion, + // visuals.corner_radius, + // visuals.weak_bg_fill, + // visuals.bg_stroke, + // ) + // } else { + // Default::default() + // }; + // let frame_cr = corner_radius.unwrap_or(frame_cr); + // let frame_fill = fill.unwrap_or(frame_fill); + // let frame_stroke = stroke.unwrap_or(frame_stroke); + // ui.painter().rect( + // rect.expand2(frame_expansion), + // frame_cr, + // frame_fill, + // frame_stroke, + // epaint::StrokeKind::Inside, + // ); + // + // let mut cursor_x = rect.min.x + button_padding.x; + // + // if let Some(image) = &image { + // let mut image_pos = ui + // .layout() + // .align_size_within_rect(image_size, rect.shrink2(button_padding)) + // .min; + // if galley.is_some() || right_galley.is_some() { + // image_pos.x = cursor_x; + // } + // let image_rect = Rect::from_min_size(image_pos, image_size); + // cursor_x += image_size.x; + // let tlr = image.load_for_size(ui.ctx(), image_size); + // let mut image_options = image.image_options().clone(); + // if image_tint_follows_text_color { + // image_options.tint = image_options.tint * visuals.text_color(); + // } + // widgets::image::paint_texture_load_result( + // ui, + // &tlr, + // image_rect, + // image.show_loading_spinner, + // &image_options, + // None, + // ); + // response = widgets::image::texture_load_result_response( + // &image.source(ui.ctx()), + // &tlr, + // response, + // ); + // } + // + // if image.is_some() && galley.is_some() { + // cursor_x += ui.spacing().icon_spacing; + // } + // + // if let Some(galley) = galley { + // let mut text_pos = ui + // .layout() + // .align_size_within_rect(galley.size(), rect.shrink2(button_padding)) + // .min; + // if image.is_some() || right_galley.is_some() { + // text_pos.x = cursor_x; + // } + // ui.painter().galley(text_pos, galley, visuals.text_color()); + // } + // + // if let Some(right_galley) = right_galley { + // // Always align to the right + // let layout = if ui.layout().is_horizontal() { + // ui.layout().with_main_align(Align::Max) + // } else { + // ui.layout().with_cross_align(Align::Max) + // }; + // let right_text_pos = layout + // .align_size_within_rect(right_galley.size(), rect.shrink2(button_padding)) + // .min; + // + // ui.painter() + // .galley(right_text_pos, right_galley, visuals.text_color()); + // } + // } - if ui.is_rect_visible(rect) { - let visuals = ui.style().interact(&response); - - let (frame_expansion, frame_cr, frame_fill, frame_stroke) = if selected { - let selection = ui.visuals().selection; - ( - Vec2::ZERO, - CornerRadius::ZERO, - selection.bg_fill, - selection.stroke, - ) - } else if frame { - let expansion = Vec2::splat(visuals.expansion); - ( - expansion, - visuals.corner_radius, - visuals.weak_bg_fill, - visuals.bg_stroke, - ) - } else { - Default::default() - }; - let frame_cr = corner_radius.unwrap_or(frame_cr); - let frame_fill = fill.unwrap_or(frame_fill); - let frame_stroke = stroke.unwrap_or(frame_stroke); - ui.painter().rect( - rect.expand2(frame_expansion), - frame_cr, - frame_fill, - frame_stroke, - epaint::StrokeKind::Inside, - ); - - let mut cursor_x = rect.min.x + button_padding.x; - - if let Some(image) = &image { - let mut image_pos = ui - .layout() - .align_size_within_rect(image_size, rect.shrink2(button_padding)) - .min; - if galley.is_some() || right_galley.is_some() { - image_pos.x = cursor_x; - } - let image_rect = Rect::from_min_size(image_pos, image_size); - cursor_x += image_size.x; - let tlr = image.load_for_size(ui.ctx(), image_size); - let mut image_options = image.image_options().clone(); - if image_tint_follows_text_color { - image_options.tint = image_options.tint * visuals.text_color(); - } - widgets::image::paint_texture_load_result( - ui, - &tlr, - image_rect, - image.show_loading_spinner, - &image_options, - None, - ); - response = widgets::image::texture_load_result_response( - &image.source(ui.ctx()), - &tlr, - response, - ); - } - - if image.is_some() && galley.is_some() { - cursor_x += ui.spacing().icon_spacing; - } - - if let Some(galley) = galley { - let mut text_pos = ui - .layout() - .align_size_within_rect(galley.size(), rect.shrink2(button_padding)) - .min; - if image.is_some() || right_galley.is_some() { - text_pos.x = cursor_x; - } - ui.painter().galley(text_pos, galley, visuals.text_color()); - } - - if let Some(right_galley) = right_galley { - // Always align to the right - let layout = if ui.layout().is_horizontal() { - ui.layout().with_main_align(Align::Max) - } else { - ui.layout().with_cross_align(Align::Max) - }; - let right_text_pos = layout - .align_size_within_rect(right_galley.size(), rect.shrink2(button_padding)) - .min; - - ui.painter() - .galley(right_text_pos, right_galley, visuals.text_color()); - } - } - - if let Some(cursor) = ui.visuals().interact_cursor { - if response.hovered() { - ui.ctx().set_cursor_icon(cursor); - } - } + // if let Some(cursor) = ui.visuals().interact_cursor { + // if response.hovered() { + // ui.ctx().set_cursor_icon(cursor); + // } + // } response } diff --git a/examples/hello_world_simple/src/main.rs b/examples/hello_world_simple/src/main.rs index 72174f051..b0d62a848 100644 --- a/examples/hello_world_simple/src/main.rs +++ b/examples/hello_world_simple/src/main.rs @@ -3,8 +3,8 @@ use eframe::egui; use eframe::egui::{ - include_image, Image, Key, KeyboardShortcut, ModifierNames, Modifiers, Popup, RichText, - WLButton, Widget, + include_image, Button, Image, Key, KeyboardShortcut, ModifierNames, Modifiers, Popup, RichText, + Widget, }; fn main() -> eframe::Result { @@ -34,15 +34,17 @@ fn main() -> eframe::Result { } ui.label(format!("Hello '{name}', age {age}")); - if WLButton::new("WL Button").ui(ui).clicked() { + if Button::new("WL Button").ui(ui).clicked() { age += 1; }; let source = include_image!("../../../crates/eframe/data/icon.png"); - let response = WLButton::image_and_text(source, "Hello World").ui(ui); + let response = Button::image_and_text(source.clone(), "Hello World").ui(ui); + + Button::new((Image::new(source).tint(egui::Color32::RED), "Tuple Button")).ui(ui); Popup::menu(&response).show(|ui| { - WLButton::new("Print") + Button::new("Print") .right_text( RichText::new( KeyboardShortcut::new(Modifiers::COMMAND, Key::P) @@ -51,7 +53,7 @@ fn main() -> eframe::Result { .weak(), ) .ui(ui); - WLButton::new("A very long button") + Button::new("A very long button") .right_text( RichText::new( KeyboardShortcut::new(Modifiers::COMMAND, Key::O)