1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Theme style components

use crate::dir::Direction;

/// Margin size
///
/// Default value: [`MarginStyle::Large`].
#[crate::impl_default(MarginStyle::Large)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MarginStyle {
    /// No margins
    None,
    /// Inner margin, used to draw highlight/selection boxes
    ///
    /// Guide size: 1px at 100%, 2px at 125%, 2px at 150%, 3px at 200%.
    ///
    /// This is the smallest of the fixed margin sizes, and only really
    /// useful to reserve space for drawing selection boxes.
    Inner,
    /// A small margin for inner layout
    ///
    /// Guide size: 2px at 100%, 3px at 125%, 4px at 150%, 5px at 200%.
    Tiny,
    /// Small external margin size
    ///
    /// Guide size: 4px at 100%, 5px at 125%, 7px at 150%, 9px at 200%.
    Small,
    /// Large margin, used between elements such as buttons
    ///
    /// Guide size: 7px at 100%, 9px at 125%, 11px at 150%, 15px at 200%.
    Large,
    /// Text margins
    ///
    /// Margins for use around standard text elements (may be asymmetric).
    Text,
    /// Specify in pixels (scaled)
    Px(f32),
    /// Specify in Em (font size)
    Em(f32),
}

/// Style of marks
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum MarkStyle {
    /// An arrowhead/angle-bracket/triangle pointing in the given direction
    Point(Direction),
    /// A cross rotated 45°
    X,
}

/// Various features which may be sized and drawn
///
/// Includes most types of features excepting text and frames.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum Feature {
    Separator,
    Mark(MarkStyle),
    CheckBox,
    RadioBox,
    ScrollBar(Direction),
    Slider(Direction),
    ProgressBar(Direction),
}

impl From<MarkStyle> for Feature {
    fn from(style: MarkStyle) -> Self {
        Feature::Mark(style)
    }
}

/// Style of a frame
///
/// A "frame" is an element surrounding another element.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FrameStyle {
    /// No frame, just draw the background
    #[default]
    None,
    /// A frame for grouping content
    Frame,
    /// A frame around pop-ups
    Popup,
    /// Border around a pop-up menu entry
    MenuEntry,
    /// Frame used to indicate navigation focus
    NavFocus,
    /// Border of a button
    Button,
    /// Border of a tab
    Tab,
    /// Box used to contain editable text
    EditBox,
    /// Window decoration (excludes top buttons)
    Window,
}

/// Selection style hint
///
/// How to draw selections
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum SelectionStyle {
    /// Adjust background color
    Highlight,
    /// Draw a frame around the selection
    Frame,
    /// Both
    Both,
}

impl SelectionStyle {
    /// True if an external margin is required
    ///
    /// Margin size is [`SizeCx::inner_margins`](super::SizeCx::inner_margins)
    pub fn is_external(self) -> bool {
        matches!(self, SelectionStyle::Frame | SelectionStyle::Both)
    }
}

/// Class of text drawn
///
/// Themes choose font, font size, colour, and alignment based on this.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TextClass {
    /// Label text is drawn over the background colour
    ///
    /// This takes one parameter: `multi_line`. Text is wrapped only if true.
    Label(bool),
    /// Scrollable label
    ///
    /// This is similar to `Label(true)`, but may occupy less vertical space.
    /// Usually it also implies that the text is both scrollable and selectable,
    /// but these are characteristics of the widget, not the text object.
    LabelScroll,
    /// Label with access keys
    ///
    /// This takes one parameter: `multi_line`. Text is wrapped only if true.
    ///
    /// This is identical to `Label` except that effects are only drawn if
    /// access key mode is activated (usually the `Alt` key).
    AccessLabel(bool),
    /// Button text is drawn over a button
    ///
    /// Same as `AccessLabel(false)`, though theme may differentiate.
    Button,
    /// Menu label (single line, does not stretch)
    ///
    /// Similar to `AccessLabel(false)`, but with horizontal stretching disabled.
    MenuLabel,
    /// Editable text, usually encapsulated in some type of box
    ///
    /// This takes one parameter: `multi_line`. Text is wrapped only if true.
    Edit(bool),
}

impl TextClass {
    /// True if text is single-line only
    #[inline]
    pub fn single_line(self) -> bool {
        !self.multi_line()
    }

    /// True if text is multi-line and should automatically line-wrap
    #[inline]
    pub fn multi_line(self) -> bool {
        use TextClass::*;
        matches!(
            self,
            Label(true) | LabelScroll | AccessLabel(true) | Edit(true)
        )
    }

    /// True if text effects should only be shown dependant on access key
    /// mode being active
    #[inline]
    pub fn is_access_key(self) -> bool {
        use TextClass::*;
        matches!(self, AccessLabel(_) | Button | MenuLabel)
    }
}