mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 22:00:03 -04:00
style by widget
This commit is contained in:
@@ -434,6 +434,7 @@ mod plugin;
|
|||||||
pub mod response;
|
pub mod response;
|
||||||
mod sense;
|
mod sense;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
|
pub mod style_trait;
|
||||||
pub mod text_selection;
|
pub mod text_selection;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod ui_builder;
|
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::{
|
use crate::{
|
||||||
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,
|
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,
|
||||||
Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2,
|
Image, IntoAtoms, NumExt as _, Response, RichText, Sense, Stroke, TextStyle, TextWrapMode, Ui,
|
||||||
Widget, WidgetInfo, WidgetText, WidgetType,
|
Vec2, Widget, WidgetInfo, WidgetText, WidgetType,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Clickable button with text.
|
/// Clickable button with text.
|
||||||
@@ -281,25 +283,30 @@ impl<'a> Button<'a> {
|
|||||||
|
|
||||||
let text = layout.text().map(String::from);
|
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 has_frame_margin = frame.unwrap_or_else(|| ui.visuals().button_frame);
|
||||||
|
|
||||||
let mut button_padding = if has_frame_margin {
|
layout.map_texts(|t| match t {
|
||||||
ui.spacing().button_padding
|
WidgetText::RichText(mut text) => {
|
||||||
} else {
|
let text_mut = Arc::make_mut(&mut text);
|
||||||
Vec2::ZERO
|
*text_mut = mem::take(text_mut).font(style.text.font_id.clone());
|
||||||
};
|
WidgetText::RichText(text)
|
||||||
if small {
|
}
|
||||||
button_padding.y = 0.0;
|
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
|
let mut prepared = layout.frame(style.frame).min_size(min_size).allocate(ui);
|
||||||
.frame(Frame::new().inner_margin(button_padding))
|
|
||||||
.min_size(min_size)
|
|
||||||
.allocate(ui);
|
|
||||||
|
|
||||||
let response = if ui.is_rect_visible(prepared.response.rect) {
|
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 {
|
let visible_frame = if frame_when_inactive {
|
||||||
has_frame_margin
|
has_frame_margin
|
||||||
} else {
|
} else {
|
||||||
@@ -310,23 +317,13 @@ impl<'a> Button<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if image_tint_follows_text_color {
|
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 {
|
if visible_frame {
|
||||||
let stroke = stroke.unwrap_or(visuals.bg_stroke);
|
prepared.frame = style.frame;
|
||||||
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.paint(ui)
|
prepared.paint(ui)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{cmp::Ordering, ops::RangeInclusive};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Button, CursorIcon, Id, Key, MINUS_CHAR_STR, Modifiers, NumExt as _, Response, RichText, Sense,
|
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 id = ui.next_auto_id();
|
||||||
let is_slow_speed = shift && ui.ctx().is_being_dragged(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,
|
// The following ensures that when a `DragValue` receives focus,
|
||||||
// it is immediately rendered in edit mode, rather than being rendered
|
// it is immediately rendered in edit mode, rather than being rendered
|
||||||
// in button mode for just one frame. This is important for
|
// in button mode for just one frame. This is important for
|
||||||
@@ -560,13 +564,14 @@ impl Widget for DragValue<'_> {
|
|||||||
.clip_text(false)
|
.clip_text(false)
|
||||||
.horizontal_align(ui.layout().horizontal_align())
|
.horizontal_align(ui.layout().horizontal_align())
|
||||||
.vertical_align(ui.layout().vertical_align())
|
.vertical_align(ui.layout().vertical_align())
|
||||||
.margin(ui.spacing().button_padding)
|
.margin(style.frame.inner_margin)
|
||||||
.min_size(ui.spacing().interact_size)
|
.min_size(style.min_size)
|
||||||
.id(id)
|
.id(id)
|
||||||
.desired_width(
|
.desired_width(
|
||||||
ui.spacing().interact_size.x - 2.0 * ui.spacing().button_padding.x,
|
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.
|
// Select all text when the edit gains focus.
|
||||||
|
|||||||
Reference in New Issue
Block a user