1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 22:53:14 -04:00

CheckboxStyle

This commit is contained in:
Adrien Zianne
2025-10-16 17:11:51 +02:00
parent e39d8c4345
commit 1e346da874
4 changed files with 76 additions and 28 deletions

View File

@@ -36,8 +36,10 @@ pub struct CheckboxStyle {
pub frame: Frame,
/// Text next to it
pub text: TextVisuals,
/// Size
pub size: Vec2,
/// Box size
pub size: f32,
/// Check size
pub check_size: f32,
/// Frame of the checkbox itself
pub checkbox_frame: Frame,
/// Checkmark stroke
@@ -149,6 +151,7 @@ impl Style {
fill: visuals.bg_fill,
stroke: visuals.bg_stroke,
corner_radius: visuals.corner_radius,
inner_margin: self.spacing.button_padding.into(),
..Default::default()
},
stroke: visuals.fg_stroke,
@@ -169,7 +172,23 @@ impl Style {
}
}
// pub fn checkbox_style(&self, state: WidgetState) -> CheckboxStylee {}
pub fn checkbox_style(&self, state: WidgetState) -> CheckboxStyle {
let visuals = self.visuals.widgets.state(state);
let ws = self.widget_style(state);
CheckboxStyle {
frame: ws.frame.fill(Color32::TRANSPARENT),
size: self.spacing.icon_width,
check_size: self.spacing.icon_width_inner,
checkbox_frame: Frame {
fill: visuals.weak_bg_fill,
corner_radius: visuals.corner_radius,
stroke: visuals.bg_stroke,
..Default::default()
},
text: ws.text,
stroke: ws.stroke,
}
}
pub fn label_style(&self, state: WidgetState) -> LabelStyle {
let ws = self.widget_style(state);

View File

@@ -55,10 +55,17 @@ impl Widget for Checkbox<'_> {
indeterminate,
} = self;
let spacing = &ui.spacing();
let icon_width = spacing.icon_width;
// Get the widget style by reading the rect from the previous pass
let id = ui.next_auto_id();
let response: Option<Response> = ui.ctx().read_response(id);
let state = response.map(|r| r.widget_state()).unwrap_or_default();
let style = ui.style().checkbox_style(state);
let mut min_size = Vec2::splat(spacing.interact_size.y);
let icon_width = style.size;
// interact_size or size ?
// let mut min_size = Vec2::splat(ui.spacing().interact_size.y);
let mut min_size = Vec2::splat(style.size);
min_size.y = min_size.y.at_least(icon_width);
// In order to center the checkbox based on min_size we set the icon height to at least min_size.y
@@ -72,6 +79,7 @@ impl Widget for Checkbox<'_> {
let mut prepared = AtomLayout::new(atoms)
.sense(Sense::click())
.min_size(min_size)
.frame(style.frame)
.allocate(ui);
if prepared.response.clicked() {
@@ -97,17 +105,16 @@ impl Widget for Checkbox<'_> {
if ui.is_rect_visible(prepared.response.rect) {
// let visuals = ui.style().interact_selectable(&response, *checked); // too colorful
let visuals = *ui.style().interact(&prepared.response);
prepared.fallback_text_color = visuals.text_color();
prepared.fallback_text_color = style.text.color;
let response = prepared.paint(ui);
if let Some(rect) = response.rect(rect_id) {
let (small_icon_rect, big_icon_rect) = ui.spacing().icon_rectangles(rect);
ui.painter().add(epaint::RectShape::new(
big_icon_rect.expand(visuals.expansion),
visuals.corner_radius,
visuals.bg_fill,
visuals.bg_stroke,
big_icon_rect,
style.checkbox_frame.corner_radius,
style.checkbox_frame.fill,
style.checkbox_frame.stroke,
epaint::StrokeKind::Inside,
));
@@ -116,7 +123,7 @@ impl Widget for Checkbox<'_> {
ui.painter().add(Shape::hline(
small_icon_rect.x_range(),
small_icon_rect.center().y,
visuals.fg_stroke,
style.stroke,
));
} else if *checked {
// Check mark:
@@ -126,7 +133,7 @@ impl Widget for Checkbox<'_> {
pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
pos2(small_icon_rect.right(), small_icon_rect.top()),
],
visuals.fg_stroke,
style.stroke,
));
}
}

View File

@@ -1,3 +1,7 @@
## General
- Interact size is great, but it SHOULD or MUST be at least this size ? Maybe add a parameter for this choice ?
## Button
- If hovering a button add a stroke, the ui shift. Maybe add the option to avoid this resize by making the frame smaller ?
@@ -7,3 +11,9 @@
- I understand checking if a sense has been set by the user, but why check if it's different than hover ? Is it for technical purpose or purely to avoid confusion with a link ?
- Selecting the text while being underlined move the underline
## Checkbox
- Checkbox are in fact a label and a check box. To propagate the CheckBoxStyle to the label we need to use a scope or something similar and change the global style. Why bind label to the check box, the user can add it himself (To keep the great prototyping speed we could keep this behavior in a Ui method, that would just merge)
- Propose different type of checkmark ? (eg. small square, dot, filled, custom, etc)

View File

@@ -1,9 +1,10 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#![allow(rustdoc::missing_crate_level_docs)] // it's an example
use std::process::exit;
use eframe::egui::{self, Color32, FontId, Label, RichText, Sense, Stroke, style::WidgetVisuals};
use eframe::egui::{
self, Atom, Checkbox, Color32, FontId, Label, RichText, Sense, Stroke, style::WidgetVisuals,
vec2,
};
fn main() -> eframe::Result {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
@@ -16,12 +17,18 @@ fn main() -> eframe::Result {
// Our application state:
let mut name = "Arthur".to_owned();
let mut age = 42;
let mut is_adult = age >= 18;
eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
egui::CentralPanel::default().show(ctx, |ui| {
ui.ctx().style_mut(|s| {
s.spacing.icon_width_inner = 8.0;
s.spacing.icon_width = 15.0;
s.spacing.button_padding = vec2(5.0, 5.0);
// s.spacing.interact_size.y = 30.0;
s.visuals.widgets.inactive = WidgetVisuals {
fg_stroke: Stroke::new(1.0, Color32::LIGHT_GRAY),
bg_stroke: Stroke::new(1.0, Color32::LIGHT_GRAY),
..s.visuals.widgets.inactive
};
s.visuals.widgets.active = WidgetVisuals {
@@ -29,7 +36,7 @@ fn main() -> eframe::Result {
..s.visuals.widgets.inactive
};
s.visuals.widgets.hovered = WidgetVisuals {
fg_stroke: Stroke::new(1.0, Color32::YELLOW),
fg_stroke: Stroke::new(3.0, Color32::LIGHT_YELLOW),
..s.visuals.widgets.inactive
};
s.visuals.widgets.noninteractive = WidgetVisuals {
@@ -55,17 +62,22 @@ fn main() -> eframe::Result {
// ui.add_enabled(false, Button::new("disabled"));
// ui.add(Button::new("no frame inactive").frame_when_inactive(false));
ui.label("Normal text");
// Should not be affected by WidgetStyle
ui.label(
RichText::new("Unaffected by style")
.font(FontId::monospace(15.0))
.color(Color32::KHAKI),
);
// ui.label("Normal text");
// // Should not be affected by WidgetStyle
// ui.label(
// RichText::new("Unaffected by style")
// .font(FontId::monospace(15.0))
// .color(Color32::KHAKI),
// );
ui.add(Label::new("interaction click").sense(Sense::click()));
ui.add(Label::new("focusable").sense(Sense::focusable_noninteractive()))
.request_focus();
// ui.add(Label::new("interaction click").sense(Sense::click()));
// ui.add(Label::new("focusable").sense(Sense::focusable_noninteractive()))
// .request_focus();
ui.add(Checkbox::new(&mut is_adult, "test"));
ui.add(Checkbox::new(&mut is_adult, "test"));
ui.add(Checkbox::new(&mut is_adult, Atom::default()));
ui.add(Checkbox::new(&mut is_adult, Atom::default()).indeterminate(true));
});
})
}