egui_dock/
style.rs

1use egui::{ecolor::*, CornerRadius, Margin, Stroke};
2
3/// Left or right alignment for tab add button.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
6#[allow(missing_docs)]
7pub enum TabAddAlign {
8    Left,
9    Right,
10}
11
12/// Lets you change how tabs and the [`DockArea`](crate::DockArea) should look and feel.
13/// [`Style`] is divided into several, more specialized structs that handle individual
14/// elements of the UI.
15///
16/// Your [`Style`] can inherit all its properties from an [`egui::Style`] through the
17/// [`Style::from_egui`] function.
18///
19/// Example:
20///
21/// ```rust
22/// # use egui_dock::{DockArea, DockState, OverlayType, Style, TabAddAlign, TabViewer};
23/// # use egui::{Ui, WidgetText};
24/// # struct MyTabViewer;
25/// # impl TabViewer for MyTabViewer {
26/// #     type Tab = ();
27/// #     fn title(&mut self, tab: &mut Self::Tab) -> WidgetText { WidgetText::default() }
28/// #     fn ui(&mut self, ui: &mut Ui, tab: &mut Self::Tab) {}
29/// # }
30/// # egui::__run_test_ctx(|ctx| {
31/// # egui::CentralPanel::default().show(ctx, |ui| {
32/// # let mut dock_state = DockState::new(vec![]);
33/// // Inherit the look and feel from egui.
34/// let mut style = Style::from_egui(ui.style());
35///
36/// // Modify a few fields.
37/// style.overlay.overlay_type = OverlayType::HighlightedAreas;
38/// style.buttons.add_tab_align = TabAddAlign::Left;
39///
40/// // Use the style with the `DockArea`.
41/// DockArea::new(&mut dock_state)
42///     .style(style)
43///     .show_inside(ui, &mut MyTabViewer);
44/// # });
45/// # });
46/// #
47/// ```
48#[derive(Clone, Debug)]
49#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
50#[allow(missing_docs)]
51pub struct Style {
52    /// Sets padding to indent from the edges of the window. By `Default` it's `None`.
53    pub dock_area_padding: Option<Margin>,
54
55    pub main_surface_border_stroke: Stroke,
56    pub main_surface_border_rounding: CornerRadius,
57
58    pub buttons: ButtonsStyle,
59    pub separator: SeparatorStyle,
60    pub tab_bar: TabBarStyle,
61    pub tab: TabStyle,
62    pub overlay: OverlayStyle,
63}
64
65/// Specifies the look and feel of buttons.
66#[derive(Clone, Debug)]
67#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
68pub struct ButtonsStyle {
69    /// Color of the close tab button.
70    pub close_tab_color: Color32,
71
72    /// Color of the active close tab button.
73    pub close_tab_active_color: Color32,
74
75    /// Color of the background close tab button.
76    pub close_tab_bg_fill: Color32,
77
78    /// Left or right aligning of the add tab button.
79    pub add_tab_align: TabAddAlign,
80
81    /// Color of the add tab button.
82    pub add_tab_color: Color32,
83
84    /// Color of the active add tab button.
85    pub add_tab_active_color: Color32,
86
87    /// Color of the add tab button's background.
88    pub add_tab_bg_fill: Color32,
89
90    /// Color of the add tab button's left border.
91    pub add_tab_border_color: Color32,
92
93    /// Color of the close all tabs button.
94    pub close_all_tabs_color: Color32,
95
96    /// Color of the active close all tabs button.
97    pub close_all_tabs_active_color: Color32,
98
99    /// Color of the close all tabs button's background.
100    pub close_all_tabs_bg_fill: Color32,
101
102    /// Color of the close all tabs button's left border.
103    pub close_all_tabs_border_color: Color32,
104
105    /// Color of disabled close all tabs button.
106    pub close_all_tabs_disabled_color: Color32,
107
108    /// Color of the collapse tabs button.
109    pub collapse_tabs_color: Color32,
110
111    /// Color of the active collapse tabs button.
112    pub collapse_tabs_active_color: Color32,
113
114    /// Color of the collapse tabs button's background.
115    pub collapse_tabs_bg_fill: Color32,
116
117    /// Color of the collapse tabs button's left border.
118    pub collapse_tabs_border_color: Color32,
119
120    /// Color of the minimize window button.
121    pub minimize_window_color: Color32,
122
123    /// Color of the active minimize window button.
124    pub minimize_window_active_color: Color32,
125
126    /// Color of the minimize window button's background.
127    pub minimize_window_bg_fill: Color32,
128
129    /// Color of the minimize window button's left border.
130    pub minimize_window_border_color: Color32,
131}
132
133/// Specifies the look and feel of node separators.
134#[derive(Clone, Debug)]
135#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
136pub struct SeparatorStyle {
137    /// Width of the rectangle separator between nodes. By `Default` it's `1.0`.
138    pub width: f32,
139
140    /// Extra width added to the "logical thickness" of the rectangle so it's
141    /// easier to grab. By `Default` it's `4.0`.
142    pub extra_interact_width: f32,
143
144    /// Limit for the allowed area for the separator offset. By `Default` it's `175.0`.
145    /// `bigger value > less allowed offset` for the current window size.
146    pub extra: f32,
147
148    /// Idle color of the rectangle separator. By `Default` it's [`Color32::BLACK`].
149    pub color_idle: Color32,
150
151    /// Hovered color of the rectangle separator. By `Default` it's [`Color32::GRAY`].
152    pub color_hovered: Color32,
153
154    /// Dragged color of the rectangle separator. By `Default` it's [`Color32::WHITE`].
155    pub color_dragged: Color32,
156}
157
158/// Specifies the look and feel of tab bars.
159#[derive(Clone, Debug)]
160#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
161pub struct TabBarStyle {
162    /// Background color of tab bar. By `Default` it's [`Color32::WHITE`].
163    pub bg_fill: Color32,
164
165    /// Height of the tab bar. By `Default` it's `24.0`.
166    pub height: f32,
167
168    /// Inner margin of tab bar. By `Default` it's `Margin::ZERO`.
169    pub inner_margin: Margin,
170
171    /// Show a scroll bar when tab bar overflows. By `Default` it's `true`.
172    pub show_scroll_bar_on_overflow: bool,
173
174    /// Tab corner_radius. By `Default` it's [`CornerRadius::default`].
175    pub corner_radius: CornerRadius,
176
177    /// Color of the line separating the tab name area from the tab content area.
178    /// By `Default` it's [`Color32::BLACK`].
179    pub hline_color: Color32,
180
181    /// Whether tab titles expand to fill the width of their tab bars.
182    /// By `Default` it's `false`.
183    pub fill_tab_bar: bool,
184}
185
186/// Specifies the look and feel of an individual tab.
187#[derive(Clone, Debug)]
188#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
189pub struct TabStyle {
190    /// Style of the tab when it is active.
191    pub active: TabInteractionStyle,
192
193    /// Style of the tab when it is inactive.
194    pub inactive: TabInteractionStyle,
195
196    /// Style of the tab when it is focused.
197    pub focused: TabInteractionStyle,
198
199    /// Style of the tab when it is hovered.
200    pub hovered: TabInteractionStyle,
201
202    /// Style of the tab when it is inactive and has keyboard focus.
203    pub inactive_with_kb_focus: TabInteractionStyle,
204
205    /// Style of the tab when it is active and has keyboard focus.
206    pub active_with_kb_focus: TabInteractionStyle,
207
208    /// Style of the tab when it is focused and has keyboard focus.
209    pub focused_with_kb_focus: TabInteractionStyle,
210
211    /// Style for the tab body.
212    pub tab_body: TabBodyStyle,
213
214    /// If `true`, show the hline below the active tabs name.
215    /// If `false`, show the active tab as merged with the tab ui area.
216    /// By `Default` it's `false`.
217    pub hline_below_active_tab_name: bool,
218
219    /// Spacing between tabs.
220    pub spacing: f32,
221
222    /// The minimum width of the tab.
223    ///
224    /// The tab title or [`TabBarStyle::fill_tab_bar`] may make the tab
225    /// wider than this but never shorter.
226    pub minimum_width: Option<f32>,
227}
228
229/// Specifies the look and feel of individual tabs while they are being interacted with.
230#[derive(Clone, Debug)]
231#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
232pub struct TabInteractionStyle {
233    /// Color of the outline around tabs. By `Default` it's [`Color32::BLACK`].
234    pub outline_color: Color32,
235
236    /// Tab corner radius. By `Default` it's [`CornerRadius::default`].
237    pub corner_radius: CornerRadius,
238
239    /// Colour of the tab's background. By `Default` it's [`Color32::WHITE`].
240    pub bg_fill: Color32,
241
242    /// Color of the title text.
243    pub text_color: Color32,
244}
245
246/// Specifies the look and feel of the tab body.
247#[derive(Clone, Debug)]
248#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
249pub struct TabBodyStyle {
250    /// Inner margin of tab body. By `Default` it's `Margin::same(4.0)`.
251    pub inner_margin: Margin,
252
253    /// The stroke of the tabs border. By `Default` it's ['Stroke::default'].
254    pub stroke: Stroke,
255
256    /// Tab corner radius. By `Default` it's [`CornerRadius::default`].
257    pub corner_radius: CornerRadius,
258
259    /// Colour of the tab's background. By `Default` it's [`Color32::WHITE`].
260    pub bg_fill: Color32,
261}
262
263/// Specifies the look and feel of the tab drop overlay.
264#[derive(Clone, Debug)]
265#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
266pub struct OverlayStyle {
267    /// Sets selection color for the placing area of the tab where this tab targeted on it.
268    /// By `Default` it's `(0, 191, 255)` (light blue) with `0.5` capacity.
269    pub selection_color: Color32,
270
271    /// Width of stroke when a selection uses an outline instead of filled rectangle.
272    pub selection_stroke_width: f32,
273
274    /// Units of padding between each button.
275    pub button_spacing: f32,
276
277    /// Max side length of a button on the overlay.
278    pub max_button_size: f32,
279
280    /// Style of the additional highlighting rectangle drawn on the surface which you're attempting to drop a tab in.
281    ///
282    /// By default this value shows no highlighting.
283    pub hovered_leaf_highlight: LeafHighlighting,
284
285    /// Opacity which surfaces will fade to in a range of `0.0..=1.0`.
286    pub surface_fade_opacity: f32,
287
288    /// The color of the overlay buttons.
289    pub button_color: Color32,
290
291    /// The stroke of the button border.
292    pub button_border_stroke: Stroke,
293
294    /// The type of overlay used.
295    pub overlay_type: OverlayType,
296
297    /// The feel of the overlay, timings, detection, etc.
298    pub feel: OverlayFeel,
299}
300
301/// Specifies the feel of the tab drop overlay, i.e anything non visual about the overlay.
302#[derive(Clone, Debug)]
303#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
304pub struct OverlayFeel {
305    /// range is `0.0..=1.0`.
306    pub window_drop_coverage: f32,
307
308    /// range is `0.0..=1.0`.
309    pub center_drop_coverage: f32,
310
311    /// The amount of time windows should stay faded despite not needing to, prevents quick mouse movements from causing flashing.
312    pub fade_hold_time: f32,
313
314    /// Amount of time the overlay waits before dropping a preference it may have for a node.
315    pub max_preference_time: f32,
316
317    /// Units which the buttons interact area will be expanded by.
318    pub interact_expansion: f32,
319}
320
321/// Specifies the type of overlay used.
322#[derive(Copy, Clone, Debug, PartialEq)]
323#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
324pub enum OverlayType {
325    /// Shows highlighted areas predicting where a dropped tab would land were it to be dropped this frame.
326    ///
327    /// Always used when hovering over tabs and tab head.
328    HighlightedAreas,
329
330    /// Shows icons indicating the possible drop positions which the user may hover over to drop a tab at that given location.
331    ///
332    /// This is the default type of overlay for leaves.
333    Widgets,
334}
335
336/// Highlighting on the currently hovered leaf.
337#[derive(Clone, Debug)]
338#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
339pub struct LeafHighlighting {
340    /// Fill color.
341    pub color: Color32,
342
343    /// Rounding of the resulting rectangle.
344    pub corner_radius: CornerRadius,
345
346    /// Stroke.
347    pub stroke: Stroke,
348
349    /// Amount of egui units which each side should expand.
350    pub expansion: f32,
351}
352
353impl Default for Style {
354    fn default() -> Self {
355        Self {
356            dock_area_padding: None,
357            main_surface_border_stroke: Stroke::new(f32::default(), Color32::BLACK),
358            main_surface_border_rounding: CornerRadius::default(),
359            buttons: ButtonsStyle::default(),
360            separator: SeparatorStyle::default(),
361            tab_bar: TabBarStyle::default(),
362            tab: TabStyle::default(),
363            overlay: OverlayStyle::default(),
364        }
365    }
366}
367
368impl Default for ButtonsStyle {
369    fn default() -> Self {
370        Self {
371            close_tab_color: Color32::WHITE,
372            close_tab_active_color: Color32::WHITE,
373            close_tab_bg_fill: Color32::GRAY,
374
375            add_tab_align: TabAddAlign::Right,
376            add_tab_color: Color32::WHITE,
377            add_tab_active_color: Color32::WHITE,
378            add_tab_bg_fill: Color32::GRAY,
379            add_tab_border_color: Color32::BLACK,
380
381            close_all_tabs_color: Color32::WHITE,
382            close_all_tabs_active_color: Color32::WHITE,
383            close_all_tabs_bg_fill: Color32::GRAY,
384            close_all_tabs_border_color: Color32::BLACK,
385            close_all_tabs_disabled_color: Color32::LIGHT_GRAY,
386
387            collapse_tabs_color: Color32::WHITE,
388            collapse_tabs_active_color: Color32::WHITE,
389            collapse_tabs_bg_fill: Color32::GRAY,
390            collapse_tabs_border_color: Color32::BLACK,
391
392            minimize_window_color: Color32::WHITE,
393            minimize_window_active_color: Color32::WHITE,
394            minimize_window_bg_fill: Color32::GRAY,
395            minimize_window_border_color: Color32::BLACK,
396        }
397    }
398}
399
400impl Default for SeparatorStyle {
401    fn default() -> Self {
402        Self {
403            width: 1.0,
404            extra_interact_width: 2.0,
405            extra: 175.0,
406            color_idle: Color32::BLACK,
407            color_hovered: Color32::GRAY,
408            color_dragged: Color32::WHITE,
409        }
410    }
411}
412
413impl Default for TabBarStyle {
414    fn default() -> Self {
415        Self {
416            bg_fill: Color32::WHITE,
417            height: 24.0,
418            inner_margin: Margin::ZERO,
419            show_scroll_bar_on_overflow: true,
420            corner_radius: CornerRadius::default(),
421            hline_color: Color32::BLACK,
422            fill_tab_bar: false,
423        }
424    }
425}
426
427impl Default for TabStyle {
428    fn default() -> Self {
429        Self {
430            active: TabInteractionStyle::default(),
431            inactive: TabInteractionStyle {
432                text_color: Color32::DARK_GRAY,
433                ..Default::default()
434            },
435            focused: TabInteractionStyle {
436                text_color: Color32::BLACK,
437                ..Default::default()
438            },
439            hovered: TabInteractionStyle {
440                text_color: Color32::BLACK,
441                ..Default::default()
442            },
443            active_with_kb_focus: TabInteractionStyle::default(),
444            inactive_with_kb_focus: TabInteractionStyle {
445                text_color: Color32::DARK_GRAY,
446                ..Default::default()
447            },
448            focused_with_kb_focus: TabInteractionStyle {
449                text_color: Color32::BLACK,
450                ..Default::default()
451            },
452            spacing: 0.0,
453            tab_body: TabBodyStyle::default(),
454            hline_below_active_tab_name: false,
455            minimum_width: None,
456        }
457    }
458}
459
460impl Default for TabInteractionStyle {
461    fn default() -> Self {
462        Self {
463            bg_fill: Color32::WHITE,
464            outline_color: Color32::BLACK,
465            corner_radius: CornerRadius::default(),
466            text_color: Color32::DARK_GRAY,
467        }
468    }
469}
470
471impl Default for TabBodyStyle {
472    fn default() -> Self {
473        Self {
474            inner_margin: Margin::same(4),
475            stroke: Stroke::default(),
476            corner_radius: CornerRadius::default(),
477            bg_fill: Color32::WHITE,
478        }
479    }
480}
481
482impl Default for OverlayStyle {
483    fn default() -> Self {
484        Self {
485            selection_color: Color32::from_rgb(0, 191, 255).linear_multiply(0.5),
486            selection_stroke_width: 1.0,
487            button_spacing: 10.0,
488            max_button_size: 100.0,
489
490            surface_fade_opacity: 0.1,
491
492            hovered_leaf_highlight: Default::default(),
493            button_color: Color32::from_gray(140),
494            button_border_stroke: Stroke::new(1.0, Color32::from_gray(60)),
495            overlay_type: OverlayType::Widgets,
496            feel: Default::default(),
497        }
498    }
499}
500
501impl Default for OverlayFeel {
502    fn default() -> Self {
503        Self {
504            max_preference_time: 0.3,
505            window_drop_coverage: 0.5,
506            center_drop_coverage: 0.25,
507            fade_hold_time: 0.2,
508            interact_expansion: 20.0,
509        }
510    }
511}
512
513impl Default for LeafHighlighting {
514    fn default() -> Self {
515        Self {
516            color: Color32::TRANSPARENT,
517            corner_radius: CornerRadius::same(0),
518            stroke: Stroke::NONE,
519            expansion: 0.0,
520        }
521    }
522}
523
524impl Style {
525    pub(crate) const TAB_ADD_BUTTON_SIZE: f32 = 24.0;
526    pub(crate) const TAB_ADD_PLUS_SIZE: f32 = 12.0;
527    pub(crate) const TAB_CLOSE_BUTTON_SIZE: f32 = 24.0;
528    pub(crate) const TAB_CLOSE_X_SIZE: f32 = 9.0;
529    pub(crate) const TAB_CLOSE_ALL_BUTTON_SIZE: f32 = 24.0;
530    pub(crate) const TAB_CLOSE_ALL_SIZE: f32 = 10.0;
531    pub(crate) const TAB_COLLAPSE_BUTTON_SIZE: f32 = 24.0;
532    pub(crate) const TAB_COLLAPSE_ARROW_SIZE: f32 = 10.0;
533    pub(crate) const TAB_EXPAND_BUTTON_SIZE: f32 = 24.0;
534    pub(crate) const TAB_EXPAND_ARROW_SIZE: f32 = 10.0;
535}
536
537impl Style {
538    /// Derives relevant fields from `egui::Style` and sets the remaining fields to their default values.
539    ///
540    /// Fields overwritten by [`egui::Style`] are:
541    /// - [`Style::main_surface_border_stroke`]
542    ///
543    /// See also: [`ButtonsStyle::from_egui`], [`SeparatorStyle::from_egui`], [`TabBarStyle::from_egui`],
544    /// [`TabStyle::from_egui`]
545    pub fn from_egui(style: &egui::Style) -> Self {
546        Self {
547            main_surface_border_stroke: Stroke::NONE,
548            main_surface_border_rounding: CornerRadius::ZERO,
549            buttons: ButtonsStyle::from_egui(style),
550            separator: SeparatorStyle::from_egui(style),
551            tab_bar: TabBarStyle::from_egui(style),
552            tab: TabStyle::from_egui(style),
553            overlay: OverlayStyle::from_egui(style),
554            ..Self::default()
555        }
556    }
557}
558
559impl ButtonsStyle {
560    /// Derives relevant fields from `egui::Style` and sets the remaining fields to their default values.
561    ///
562    /// Fields overwritten by [`egui::Style`] are:
563    /// - [`ButtonsStyle::close_tab_bg_fill`]
564    /// - [`ButtonsStyle::close_tab_color`]
565    /// - [`ButtonsStyle::close_tab_active_color`]
566    /// - [`ButtonsStyle::add_tab_bg_fill`]
567    /// - [`ButtonsStyle::add_tab_color`]
568    /// - [`ButtonsStyle::add_tab_active_color`]
569    /// - [`ButtonsStyle::add_tab_border_color`]
570    /// - [`ButtonsStyle::close_all_tabs_bg_fill`]
571    /// - [`ButtonsStyle::close_all_tabs_color`]
572    /// - [`ButtonsStyle::close_all_tabs_active_color`]
573    /// - [`ButtonsStyle::close_all_tabs_border_color`]
574    /// - [`ButtonsStyle::collapse_tabs_bg_fill`]
575    /// - [`ButtonsStyle::collapse_tabs_color`]
576    /// - [`ButtonsStyle::collapse_tabs_active_color`]
577    /// - [`ButtonsStyle::collapse_tabs_border_color`]
578    pub fn from_egui(style: &egui::Style) -> Self {
579        Self {
580            close_tab_bg_fill: style.visuals.widgets.hovered.bg_fill,
581            close_tab_color: style.visuals.text_color(),
582            close_tab_active_color: style.visuals.strong_text_color(),
583            add_tab_bg_fill: style.visuals.widgets.hovered.bg_fill,
584            add_tab_color: style.visuals.text_color(),
585            add_tab_active_color: style.visuals.strong_text_color(),
586            add_tab_border_color: style.visuals.widgets.noninteractive.bg_fill,
587            close_all_tabs_bg_fill: style.visuals.widgets.hovered.bg_fill,
588            close_all_tabs_color: style.visuals.text_color(),
589            close_all_tabs_active_color: style.visuals.strong_text_color(),
590            close_all_tabs_border_color: style.visuals.widgets.noninteractive.bg_fill,
591            close_all_tabs_disabled_color: style.visuals.widgets.inactive.bg_fill,
592            collapse_tabs_bg_fill: style.visuals.widgets.hovered.bg_fill,
593            collapse_tabs_color: style.visuals.text_color(),
594            collapse_tabs_active_color: style.visuals.strong_text_color(),
595            collapse_tabs_border_color: style.visuals.widgets.noninteractive.bg_fill,
596            minimize_window_bg_fill: style.visuals.widgets.hovered.bg_fill,
597            minimize_window_color: style.visuals.text_color(),
598            minimize_window_active_color: style.visuals.strong_text_color(),
599            minimize_window_border_color: style.visuals.widgets.noninteractive.bg_fill,
600            ..ButtonsStyle::default()
601        }
602    }
603}
604
605impl SeparatorStyle {
606    /// Derives relevant fields from `egui::Style` and sets the remaining fields to their default values.
607    ///
608    /// Fields overwritten by [`egui::Style`] are:
609    /// - [`SeparatorStyle::color_idle`]
610    /// - [`SeparatorStyle::color_hovered`]
611    /// - [`SeparatorStyle::color_dragged`]
612    pub fn from_egui(style: &egui::Style) -> Self {
613        Self {
614            // Same as egui panel resize colors:
615            color_idle: style.visuals.widgets.noninteractive.bg_stroke.color, // dim
616            color_hovered: style.visuals.widgets.hovered.fg_stroke.color,     // bright
617            color_dragged: style.visuals.widgets.active.fg_stroke.color,      // bright
618            ..SeparatorStyle::default()
619        }
620    }
621}
622
623impl TabBarStyle {
624    /// Derives relevant fields from `egui::Style` and sets the remaining fields to their default values.
625    ///
626    /// Fields overwritten by [`egui::Style`] are:
627    /// - [`TabBarStyle::bg_fill`]
628    /// - [`TabBarStyle::hline_color`]
629    pub fn from_egui(style: &egui::Style) -> Self {
630        Self {
631            bg_fill: style.visuals.extreme_bg_color,
632            corner_radius: CornerRadius {
633                nw: style.visuals.widgets.inactive.corner_radius.nw + 2,
634                ne: style.visuals.widgets.inactive.corner_radius.ne + 2,
635                sw: 0,
636                se: 0,
637            },
638            hline_color: style.visuals.widgets.noninteractive.bg_stroke.color,
639            ..TabBarStyle::default()
640        }
641    }
642}
643
644impl TabStyle {
645    /// Derives tab styles from `egui::Style`.
646    ///
647    /// See also: [`TabInteractionStyle::from_egui_active`], [`TabInteractionStyle::from_egui_inactive`],
648    /// [`TabInteractionStyle::from_egui_focused`], [`TabInteractionStyle::from_egui_hovered`], [`TabBodyStyle::from_egui`],
649    pub fn from_egui(style: &egui::Style) -> TabStyle {
650        Self {
651            active: TabInteractionStyle::from_egui_active(style),
652            inactive: TabInteractionStyle::from_egui_inactive(style),
653            focused: TabInteractionStyle::from_egui_focused(style),
654            hovered: TabInteractionStyle::from_egui_hovered(style),
655            active_with_kb_focus: TabInteractionStyle::from_egui_active_with_kb_focus(style),
656            inactive_with_kb_focus: TabInteractionStyle::from_egui_inactive_with_kb_focus(style),
657            focused_with_kb_focus: TabInteractionStyle::from_egui_focused_with_kb_focus(style),
658            tab_body: TabBodyStyle::from_egui(style),
659            ..Default::default()
660        }
661    }
662}
663
664impl TabInteractionStyle {
665    /// Derives relevant fields from `egui::Style` for an active tab and sets the remaining fields to their default values.
666    ///
667    /// Fields overwritten by [`egui::Style`] are:
668    /// - [`TabInteractionStyle::outline_color`]
669    /// - [`TabInteractionStyle::bg_fill`]
670    /// - [`TabInteractionStyle::text_color`]
671    pub fn from_egui_active(style: &egui::Style) -> Self {
672        Self {
673            outline_color: style.visuals.widgets.noninteractive.bg_stroke.color,
674            bg_fill: style.visuals.window_fill(),
675            text_color: style.visuals.text_color(),
676            corner_radius: CornerRadius {
677                sw: 0,
678                se: 0,
679                ..style.visuals.widgets.active.corner_radius
680            },
681        }
682    }
683
684    /// Derives relevant fields from `egui::Style` for an inactive tab and sets the remaining fields to their default values.
685    ///
686    /// Fields overwritten by [`egui::Style`] are:
687    /// - [`TabInteractionStyle::outline_color`]
688    /// - [`TabInteractionStyle::bg_fill`]
689    /// - [`TabInteractionStyle::text_color`]
690    pub fn from_egui_inactive(style: &egui::Style) -> Self {
691        Self {
692            text_color: style.visuals.text_color(),
693            bg_fill: tint_color_towards(style.visuals.window_fill, style.visuals.extreme_bg_color),
694            outline_color: tint_color_towards(
695                style.visuals.widgets.noninteractive.bg_stroke.color,
696                style.visuals.extreme_bg_color,
697            ),
698            ..TabInteractionStyle::from_egui_active(style)
699        }
700    }
701
702    /// Derives relevant fields from `egui::Style` for a focused tab and sets the remaining fields to their default values.
703    ///
704    /// Fields overwritten by [`egui::Style`] are:
705    /// - [`TabInteractionStyle::outline_color`]
706    /// - [`TabInteractionStyle::bg_fill`]
707    /// - [`TabInteractionStyle::text_color`]
708    pub fn from_egui_focused(style: &egui::Style) -> Self {
709        Self {
710            text_color: style.visuals.strong_text_color(),
711            ..TabInteractionStyle::from_egui_active(style)
712        }
713    }
714
715    /// Derives relevant fields from `egui::Style` for a hovered tab and sets the remaining fields to their default values.
716    ///
717    /// Fields overwritten by [`egui::Style`] are:
718    /// - [`TabInteractionStyle::outline_color`]
719    /// - [`TabInteractionStyle::bg_fill`]
720    /// - [`TabInteractionStyle::text_color`]
721    pub fn from_egui_hovered(style: &egui::Style) -> Self {
722        Self {
723            text_color: style.visuals.strong_text_color(),
724            outline_color: style.visuals.widgets.hovered.bg_stroke.color,
725            ..TabInteractionStyle::from_egui_inactive(style)
726        }
727    }
728
729    /// Derives relevant fields from `egui::Style` for an active tab with keyboard focus and sets the remaining fields to their default values.
730    ///
731    /// Fields overwritten by [`egui::Style`] are:
732    /// - [`TabInteractionStyle::outline_color`]
733    /// - [`TabInteractionStyle::bg_fill`]
734    /// - [`TabInteractionStyle::text_color`]
735    pub fn from_egui_active_with_kb_focus(style: &egui::Style) -> Self {
736        Self {
737            text_color: style.visuals.strong_text_color(),
738            outline_color: style.visuals.widgets.hovered.bg_stroke.color,
739            ..TabInteractionStyle::from_egui_active(style)
740        }
741    }
742
743    /// Derives relevant fields from `egui::Style` for an inactive tab with keyboard focus and sets the remaining fields to their default values.
744    ///
745    /// Fields overwritten by [`egui::Style`] are:
746    /// - [`TabInteractionStyle::outline_color`]
747    /// - [`TabInteractionStyle::bg_fill`]
748    /// - [`TabInteractionStyle::text_color`]
749    pub fn from_egui_inactive_with_kb_focus(style: &egui::Style) -> Self {
750        Self {
751            text_color: style.visuals.strong_text_color(),
752            outline_color: style.visuals.widgets.hovered.bg_stroke.color,
753            ..TabInteractionStyle::from_egui_inactive(style)
754        }
755    }
756
757    /// Derives relevant fields from `egui::Style` for a focused tab with keyboard focus and sets the remaining fields to their default values.
758    ///
759    /// Fields overwritten by [`egui::Style`] are:
760    /// - [`TabInteractionStyle::outline_color`]
761    /// - [`TabInteractionStyle::bg_fill`]
762    /// - [`TabInteractionStyle::text_color`]
763    pub fn from_egui_focused_with_kb_focus(style: &egui::Style) -> Self {
764        Self {
765            text_color: style.visuals.strong_text_color(),
766            outline_color: style.visuals.widgets.hovered.bg_stroke.color,
767            ..TabInteractionStyle::from_egui_focused(style)
768        }
769    }
770}
771
772impl TabBodyStyle {
773    /// Derives relevant fields from `egui::Style` and sets the remaining fields to their default values.
774    ///
775    /// Fields overwritten by [`egui::Style`] are:
776    /// - [`TabBodyStyle::inner_margin`]
777    /// - [`TabBodyStyle::stroke]
778    /// - [`TabBodyStyle::bg_fill`]
779    pub fn from_egui(style: &egui::Style) -> Self {
780        Self {
781            inner_margin: style.spacing.window_margin,
782            stroke: style.visuals.widgets.noninteractive.bg_stroke,
783            corner_radius: style.visuals.widgets.active.corner_radius,
784            bg_fill: style.visuals.window_fill(),
785        }
786    }
787}
788
789impl OverlayStyle {
790    /// Derives relevant fields from `egui::Style` and sets the remaining fields to their default values.
791    ///
792    /// Fields overwritten by [`egui::Style`] are:
793    /// - [`OverlayStyle::selection_color`]
794    /// - [`OverlayStyle::button_spacing]
795    /// - [`OverlayStyle::button_color`]
796    /// - [`OverlayStyle::button_border_stroke`]
797    pub fn from_egui(style: &egui::Style) -> Self {
798        Self {
799            selection_color: style.visuals.selection.bg_fill.linear_multiply(0.5),
800            button_spacing: style.spacing.icon_spacing,
801            button_color: style.visuals.widgets.noninteractive.fg_stroke.color,
802            button_border_stroke: style.visuals.widgets.noninteractive.bg_stroke,
803            ..Default::default()
804        }
805    }
806}