Skip to main content

egui_elements/
visuals.rs

1//! Per-widget visuals stored on [`Theme`](crate::theme::Theme) and optionally
2//! overridden on a single widget via `.visuals(...)`.
3//!
4//! Custom widgets resolve visuals in this order: the override → values
5//! written by [`Theme::install`](crate::theme::Theme::install) → stock
6//! [`egui::Style`].
7
8use egui::{Color32, CornerRadius, Response, Shadow, Stroke};
9
10/// Alias: labels use the same visuals as buttons.
11pub type LabelVisuals = ButtonVisuals;
12
13/// Visuals for a button
14#[derive(Copy, Clone, Debug, Default)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct ButtonVisuals {
17   /// Glyph / label color.
18   pub text: Color32,
19   /// Idle fill.
20   pub bg: Color32,
21   /// Fill while hovered.
22   pub bg_hover: Color32,
23   /// Fill while the pointer is down.
24   pub bg_click: Color32,
25   /// Fill when [`crate::widgets::Button::selected`] is set.
26   pub bg_selected: Color32,
27   /// Idle border.
28   pub border: Stroke,
29   /// Border while hovered.
30   pub border_hover: Stroke,
31   /// Border while the pointer is down.
32   pub border_click: Stroke,
33   pub corner_radius: CornerRadius,
34   pub shadow: Shadow,
35}
36
37impl PartialEq for ButtonVisuals {
38   fn eq(&self, other: &Self) -> bool {
39      self.text == other.text
40         && self.bg == other.bg
41         && self.bg_hover == other.bg_hover
42         && self.bg_click == other.bg_click
43         && self.bg_selected == other.bg_selected
44         && self.border == other.border
45         && self.border_hover == other.border_hover
46         && self.border_click == other.border_click
47         && self.corner_radius == other.corner_radius
48         && self.shadow == other.shadow
49   }
50}
51
52impl Eq for ButtonVisuals {}
53
54impl ButtonVisuals {
55   /// Fill for the current interaction state (click > hover > idle).
56   pub fn bg_from_res(&self, res: &Response) -> Color32 {
57      if res.is_pointer_button_down_on() || res.has_focus() || res.clicked() {
58         self.bg_click
59      } else if res.hovered() || res.highlighted() {
60         self.bg_hover
61      } else {
62         self.bg
63      }
64   }
65
66   /// Border for the current interaction state (click > hover > idle).
67   pub fn border_from_res(&self, res: &Response) -> Stroke {
68      if res.is_pointer_button_down_on() || res.has_focus() || res.clicked() {
69         self.border_click
70      } else if res.hovered() || res.highlighted() {
71         self.border_hover
72      } else {
73         self.border
74      }
75   }
76}
77
78/// Visuals for a TextEdit
79#[derive(Copy, Clone, Debug, Default)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub struct TextEditVisuals {
82   /// Typed text color.
83   pub text: Color32,
84   /// Field fill.
85   pub bg: Color32,
86   /// Idle border.
87   pub border: Stroke,
88   /// Border while hovered.
89   pub border_hover: Stroke,
90   /// Border while focused / open.
91   pub border_open: Stroke,
92   pub corner_radius: CornerRadius,
93   pub shadow: Shadow,
94}
95
96impl PartialEq for TextEditVisuals {
97   fn eq(&self, other: &Self) -> bool {
98      self.text == other.text
99         && self.bg == other.bg
100         && self.border == other.border
101         && self.border_hover == other.border_hover
102         && self.border_open == other.border_open
103         && self.corner_radius == other.corner_radius
104         && self.shadow == other.shadow
105   }
106}
107
108impl Eq for TextEditVisuals {}
109
110impl TextEditVisuals {
111   /// Border for the current interaction state (open > hover > idle).
112   pub fn border_from_res(&self, res: &Response) -> Stroke {
113      if res.is_pointer_button_down_on() || res.has_focus() || res.clicked() {
114         self.border_open
115      } else if res.hovered() || res.highlighted() {
116         self.border_hover
117      } else {
118         self.border
119      }
120   }
121}
122
123/// Visuals for a ComboBox
124#[derive(Copy, Clone, Debug, Default)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct ComboBoxVisuals {
127   /// Idle fill of the closed button.
128   pub bg: Color32,
129   /// Disclosure-icon color.
130   pub icon: Color32,
131   /// Fill while hovered.
132   pub bg_hover: Color32,
133   /// Fill while the popup is open.
134   pub bg_open: Color32,
135   /// Idle border.
136   pub border: Stroke,
137   /// Border while hovered.
138   pub border_hover: Stroke,
139   /// Border while the popup is open.
140   pub border_open: Stroke,
141   pub corner_radius: CornerRadius,
142   pub shadow: Shadow,
143}
144
145impl PartialEq for ComboBoxVisuals {
146   fn eq(&self, other: &Self) -> bool {
147      self.bg == other.bg
148         && self.icon == other.icon
149         && self.bg_hover == other.bg_hover
150         && self.bg_open == other.bg_open
151         && self.border == other.border
152         && self.border_hover == other.border_hover
153         && self.border_open == other.border_open
154         && self.corner_radius == other.corner_radius
155         && self.shadow == other.shadow
156   }
157}
158
159impl Eq for ComboBoxVisuals {}
160
161impl ComboBoxVisuals {
162   /// Fill for the current interaction state (open > hover > idle).
163   pub fn bg_from_res(&self, res: &Response) -> Color32 {
164      if res.is_pointer_button_down_on() || res.has_focus() || res.clicked() {
165         self.bg_open
166      } else if res.hovered() || res.highlighted() {
167         self.bg_hover
168      } else {
169         self.bg
170      }
171   }
172
173   /// Border for the current interaction state (open > hover > idle).
174   pub fn border_from_res(&self, res: &Response) -> Stroke {
175      if res.is_pointer_button_down_on() || res.has_focus() || res.clicked() {
176         self.border_open
177      } else if res.hovered() || res.highlighted() {
178         self.border_hover
179      } else {
180         self.border
181      }
182   }
183}
184
185/// Hover / click overrides used by [`crate::utils::frame`].
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187#[derive(Clone, Copy, Debug, Default)]
188pub struct FrameVisuals {
189   /// Fill while hovered.
190   pub bg_on_hover: Color32,
191   /// Fill while the pointer is down.
192   pub bg_on_click: Color32,
193   /// `(width, color)` border while hovered.
194   pub border_on_hover: (f32, Color32),
195   /// `(width, color)` border while the pointer is down.
196   pub border_on_click: (f32, Color32),
197}
198
199impl PartialEq for FrameVisuals {
200   fn eq(&self, other: &Self) -> bool {
201      self.bg_on_hover == other.bg_on_hover
202         && self.bg_on_click == other.bg_on_click
203         && self.border_on_hover == other.border_on_hover
204         && self.border_on_click == other.border_on_click
205   }
206}
207
208impl Eq for FrameVisuals {}