mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
style by widget
This commit is contained in:
@@ -434,6 +434,7 @@ mod plugin;
|
||||
pub mod response;
|
||||
mod sense;
|
||||
pub mod style;
|
||||
pub mod style_trait;
|
||||
pub mod text_selection;
|
||||
mod ui;
|
||||
mod ui_builder;
|
||||
|
||||
188
crates/egui/src/style_trait.rs
Normal file
188
crates/egui/src/style_trait.rs
Normal file
@@ -0,0 +1,188 @@
|
||||
use emath::Vec2;
|
||||
use epaint::{Color32, FontId, Stroke};
|
||||
|
||||
use crate::{
|
||||
Frame, Response, Style,
|
||||
style::{WidgetVisuals, Widgets},
|
||||
};
|
||||
|
||||
/// General text style
|
||||
pub struct TextVisuals {
|
||||
/// Font used
|
||||
pub font_id: FontId,
|
||||
/// Font color
|
||||
pub color: Color32,
|
||||
}
|
||||
|
||||
/// General widget style
|
||||
pub struct WidgetStyle {
|
||||
pub frame: Frame,
|
||||
|
||||
pub text: TextVisuals,
|
||||
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct ButtonStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
}
|
||||
|
||||
pub struct CheckboxStyle {
|
||||
/// Frame around
|
||||
pub frame: Frame,
|
||||
/// Text next to it
|
||||
pub text: TextVisuals,
|
||||
/// Size
|
||||
pub size: Vec2,
|
||||
/// Frame of the checkbox itself
|
||||
pub checkbox_frame: Frame,
|
||||
/// Checkmark stroke
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct DragValueStyle {
|
||||
/// Frame around
|
||||
pub frame: Frame,
|
||||
/// Text of the value
|
||||
pub text: TextVisuals,
|
||||
pub min_size: Vec2,
|
||||
}
|
||||
|
||||
pub struct HyperlinkStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
pub size: Vec2,
|
||||
pub checkbox_frame: Frame,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct ImageStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
pub size: Vec2,
|
||||
pub checkbox_frame: Frame,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct LabelStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
pub size: Vec2,
|
||||
pub checkbox_frame: Frame,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct RadioButtonStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
pub size: Vec2,
|
||||
pub checkbox_frame: Frame,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct SeparatorStyle {
|
||||
pub size: f32,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct SliderStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
pub size: Vec2,
|
||||
pub checkbox_frame: Frame,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
pub struct SpinnerStyle {
|
||||
pub frame: Frame,
|
||||
pub text: TextVisuals,
|
||||
pub size: Vec2,
|
||||
pub checkbox_frame: Frame,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum WidgetState {
|
||||
Noninteractive,
|
||||
#[default]
|
||||
Inactive,
|
||||
Hovered,
|
||||
Active,
|
||||
}
|
||||
|
||||
impl Widgets {
|
||||
pub fn state(&self, state: WidgetState) -> &WidgetVisuals {
|
||||
match state {
|
||||
WidgetState::Noninteractive => &self.noninteractive,
|
||||
WidgetState::Inactive => &self.inactive,
|
||||
WidgetState::Hovered => &self.hovered,
|
||||
WidgetState::Active => &self.active,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn widget_state(&self) -> WidgetState {
|
||||
if !self.sense.interactive() {
|
||||
WidgetState::Noninteractive
|
||||
} else if self.is_pointer_button_down_on() || self.has_focus() || self.clicked() {
|
||||
WidgetState::Active
|
||||
} else if self.hovered() || self.highlighted() {
|
||||
WidgetState::Hovered
|
||||
} else {
|
||||
WidgetState::Inactive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn widget_style(&self, state: WidgetState) -> WidgetStyle {
|
||||
let visuals = self.visuals.widgets.state(state);
|
||||
let font_id = self.override_font_id.clone();
|
||||
WidgetStyle {
|
||||
frame: Frame {
|
||||
fill: visuals.bg_fill,
|
||||
stroke: visuals.bg_stroke,
|
||||
corner_radius: visuals.corner_radius,
|
||||
..Default::default()
|
||||
},
|
||||
stroke: visuals.fg_stroke,
|
||||
text: TextVisuals {
|
||||
color: visuals.fg_stroke.color,
|
||||
font_id: font_id.unwrap_or(FontId::new(13.0, epaint::FontFamily::Proportional)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn button_style(&self, state: WidgetState) -> ButtonStyle {
|
||||
let ws = self.widget_style(state);
|
||||
ButtonStyle {
|
||||
frame: ws.frame.inner_margin(self.spacing.button_padding),
|
||||
text: ws.text,
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn checkbox_style(&self, state: WidgetState) -> CheckboxStylee {}
|
||||
|
||||
// pub fn label_style(&self, state: WidgetState) -> LabelStyle {}
|
||||
|
||||
pub fn drag_value_style(&self, state: WidgetState) -> DragValueStyle {
|
||||
let ws = self.widget_style(state);
|
||||
DragValueStyle {
|
||||
frame: ws.frame.inner_margin(self.spacing.button_padding),
|
||||
min_size: self.spacing.interact_size,
|
||||
text: ws.text,
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn hyperlink_style(&self, state: WidgetState) -> HyperlinkStyle {}
|
||||
|
||||
// pub fn image_style(&self, state: WidgetState) -> ImageStyle {}
|
||||
|
||||
// pub fn slider_style(&self, state: WidgetState) -> SliderStyle {}
|
||||
|
||||
// pub fn separator_style(&self, state: WidgetState) -> SeparatorStyle {}
|
||||
|
||||
// pub fn spinner_style(&self, state: WidgetState) -> SpinnerStyle {}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::{mem, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,
|
||||
Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2,
|
||||
Widget, WidgetInfo, WidgetText, WidgetType,
|
||||
Image, IntoAtoms, NumExt as _, Response, RichText, Sense, Stroke, TextStyle, TextWrapMode, Ui,
|
||||
Vec2, Widget, WidgetInfo, WidgetText, WidgetType,
|
||||
};
|
||||
|
||||
/// Clickable button with text.
|
||||
@@ -281,25 +283,30 @@ impl<'a> Button<'a> {
|
||||
|
||||
let text = layout.text().map(String::from);
|
||||
|
||||
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().button_style(state);
|
||||
|
||||
let has_frame_margin = frame.unwrap_or_else(|| ui.visuals().button_frame);
|
||||
|
||||
let mut button_padding = if has_frame_margin {
|
||||
ui.spacing().button_padding
|
||||
} else {
|
||||
Vec2::ZERO
|
||||
};
|
||||
if small {
|
||||
button_padding.y = 0.0;
|
||||
}
|
||||
layout.map_texts(|t| match t {
|
||||
WidgetText::RichText(mut text) => {
|
||||
let text_mut = Arc::make_mut(&mut text);
|
||||
*text_mut = mem::take(text_mut).font(style.text.font_id.clone());
|
||||
WidgetText::RichText(text)
|
||||
}
|
||||
WidgetText::Text(text) => {
|
||||
let rich_text = RichText::new(text.clone()).font(style.text.font_id.clone());
|
||||
WidgetText::RichText(Arc::new(rich_text))
|
||||
}
|
||||
w => w,
|
||||
});
|
||||
|
||||
let mut prepared = layout
|
||||
.frame(Frame::new().inner_margin(button_padding))
|
||||
.min_size(min_size)
|
||||
.allocate(ui);
|
||||
let mut prepared = layout.frame(style.frame).min_size(min_size).allocate(ui);
|
||||
|
||||
let response = if ui.is_rect_visible(prepared.response.rect) {
|
||||
let visuals = ui.style().interact_selectable(&prepared.response, selected);
|
||||
|
||||
let visible_frame = if frame_when_inactive {
|
||||
has_frame_margin
|
||||
} else {
|
||||
@@ -310,23 +317,13 @@ impl<'a> Button<'a> {
|
||||
};
|
||||
|
||||
if image_tint_follows_text_color {
|
||||
prepared.map_images(|image| image.tint(visuals.text_color()));
|
||||
prepared.map_images(|image| image.tint(style.text.color));
|
||||
}
|
||||
|
||||
prepared.fallback_text_color = visuals.text_color();
|
||||
prepared.fallback_text_color = style.text.color;
|
||||
|
||||
if visible_frame {
|
||||
let stroke = stroke.unwrap_or(visuals.bg_stroke);
|
||||
let fill = fill.unwrap_or(visuals.weak_bg_fill);
|
||||
prepared.frame = prepared
|
||||
.frame
|
||||
.inner_margin(
|
||||
button_padding + Vec2::splat(visuals.expansion) - Vec2::splat(stroke.width),
|
||||
)
|
||||
.outer_margin(-Vec2::splat(visuals.expansion))
|
||||
.fill(fill)
|
||||
.stroke(stroke)
|
||||
.corner_radius(corner_radius.unwrap_or(visuals.corner_radius));
|
||||
prepared.frame = style.frame;
|
||||
}
|
||||
|
||||
prepared.paint(ui)
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{cmp::Ordering, ops::RangeInclusive};
|
||||
|
||||
use crate::{
|
||||
Button, CursorIcon, Id, Key, MINUS_CHAR_STR, Modifiers, NumExt as _, Response, RichText, Sense,
|
||||
TextEdit, TextWrapMode, Ui, Widget, WidgetInfo, emath, text,
|
||||
TextEdit, TextWrapMode, Ui, Widget, WidgetInfo, emath, grid::State, text,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -447,6 +447,10 @@ impl Widget for DragValue<'_> {
|
||||
let id = ui.next_auto_id();
|
||||
let is_slow_speed = shift && ui.ctx().is_being_dragged(id);
|
||||
|
||||
let response = ui.ctx().read_response(id);
|
||||
let state = response.map(|r| r.widget_state()).unwrap_or_default();
|
||||
let style = ui.style().drag_value_style(state);
|
||||
|
||||
// The following ensures that when a `DragValue` receives focus,
|
||||
// it is immediately rendered in edit mode, rather than being rendered
|
||||
// in button mode for just one frame. This is important for
|
||||
@@ -560,13 +564,14 @@ impl Widget for DragValue<'_> {
|
||||
.clip_text(false)
|
||||
.horizontal_align(ui.layout().horizontal_align())
|
||||
.vertical_align(ui.layout().vertical_align())
|
||||
.margin(ui.spacing().button_padding)
|
||||
.min_size(ui.spacing().interact_size)
|
||||
.margin(style.frame.inner_margin)
|
||||
.min_size(style.min_size)
|
||||
.id(id)
|
||||
.desired_width(
|
||||
ui.spacing().interact_size.x - 2.0 * ui.spacing().button_padding.x,
|
||||
)
|
||||
.font(text_style),
|
||||
.text_color(style.text.color)
|
||||
.font(style.text.font_id),
|
||||
);
|
||||
|
||||
// Select all text when the edit gains focus.
|
||||
|
||||
Reference in New Issue
Block a user