1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 07:03:14 -04:00

Add Atomics, IntoAtomics, Atomic, AtomicKind, implement Button with new Atomics

This commit is contained in:
lucasmerlin
2025-03-31 18:32:54 +02:00
parent f017e226e4
commit 471a91966b
5 changed files with 484 additions and 337 deletions

View File

@@ -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<WidgetText>) -> 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<WidgetText>) -> 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`].

View File

@@ -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,

View File

@@ -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<Galley>),
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<WidgetLayoutItemType<'a>>) -> Self {
self.items.push((item, kind.into()));
pub fn add(mut self, atomic: impl Into<Atomic<'a>>) -> 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<WidgetText>) -> Self {
// Self {
// wl: WidgetLayout::new()
// .sense(Sense::click())
// .add(Item::default(), WidgetLayoutItemType::Text(text.into())),
// }
// }
//
// pub fn image(image: impl Into<Image<'a>>) -> 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<Image<'a>>, text: impl Into<WidgetText>) -> 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<WidgetText>) -> 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<WidgetText>) -> Self {
Self {
wl: WidgetLayout::new()
.sense(Sense::click())
.add(Item::default(), WidgetLayoutItemType::Text(text.into())),
}
}
pub struct Atomic<'a> {
size: Option<Vec2>,
grow: bool,
pub kind: AtomicKind<'a>,
}
pub fn image(image: impl Into<Image<'a>>) -> 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<Image<'a>>, text: impl Into<WidgetText>) -> 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<WidgetText>) -> 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<AtomicKind<'a>>) -> 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<Atomic<'a>> + 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<T> for Atomic<'a>
where
T: Into<AtomicKind<'a>>,
{
fn from(value: T) -> Self {
Atomic {
size: None,
grow: false,
kind: value.into(),
}
}
}
impl<'a> From<Image<'a>> 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<T> for AtomicKind<'a>
where
T: Into<WidgetText>,
{
fn from(value: T) -> Self {
AtomicKind::Text(value.into())
}
}
pub struct Atomics<'a>(Vec<Atomic<'a>>);
impl<'a> Atomics<'a> {
pub fn add(&mut self, atomic: impl Into<Atomic<'a>>) {
self.0.push(atomic.into());
}
pub fn add_front(&mut self, atomic: impl Into<Atomic<'a>>) {
self.0.insert(0, atomic.into());
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Atomic<'a>> {
self.0.iter_mut()
}
}
impl<'a, T> IntoAtomics<'a> for T
where
T: Into<Atomic<'a>>,
{
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);

View File

@@ -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<Image<'a>>,
text: Option<WidgetText>,
right_text: WidgetText,
wrap_mode: Option<TextWrapMode>,
/// None means default for interact
fill: Option<Color32>,
stroke: Option<Stroke>,
sense: Sense,
small: bool,
frame: Option<bool>,
min_size: Vec2,
corner_radius: Option<CornerRadius>,
selected: bool,
image_tint_follows_text_color: bool,
wl: WidgetLayout<'a>,
}
impl<'a> Button<'a> {
pub fn new(text: impl Into<WidgetText>) -> 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<Image<'a>>, text: Option<WidgetText>) -> 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<Color32>) -> 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<WidgetText>) -> Self {
self.right_text = shortcut_text.into().weak();
pub fn shortcut_text(mut self, shortcut_text: impl Into<Atomic<'a>>) -> 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<WidgetText>) -> Self {
self.right_text = right_text.into();
pub fn right_text(mut self, right_text: impl Into<Atomic<'a>>) -> 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
}

View File

@@ -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)