1use makepad_render::*;
2use crate::buttonlogic::*;
3use crate::tabclose::*;
4use crate::widgetstyle::*;
5
6#[derive(Clone)]
7pub struct Tab {
8 pub bg: Quad,
9 pub text: Text,
10 pub tab_close: TabClose,
11 pub label: String,
12 pub is_closeable: bool,
13 pub animator: Animator,
14 pub z: f32,
15 pub abs_origin: Option<Vec2>,
16 pub _is_selected: bool,
17 pub _is_focussed: bool,
18 pub _bg_area: Area,
19 pub _bg_inst: Option<InstanceArea>,
20 pub _text_area: Area,
21 pub _close_anim_rect: Rect,
22 pub _is_down: bool,
23 pub _is_drag: bool
24}
25
26#[derive(Clone, PartialEq)]
27pub enum TabEvent {
28 None,
29 DragMove(FingerMoveEvent),
30 DragEnd(FingerUpEvent),
31 Closing,
32 Close,
33 Select,
34}
35
36impl Tab {
37 pub fn proto(cx: &mut Cx) -> Self {
38 let mut tab = Self {
39 label: "Tab".to_string(),
40 is_closeable: true,
41 z: 0.,
42 bg: Quad ::proto(cx),
43 tab_close: TabClose::proto(cx),
44 text: Text::proto(cx),
45 animator: Animator::default(),
46 abs_origin: None,
47 _is_selected: false,
48 _is_focussed: false,
49 _is_down: false,
50 _is_drag: false,
51 _close_anim_rect: Rect::default(),
52 _text_area: Area::Empty,
53 _bg_area: Area::Empty,
54 _bg_inst: None,
55 };
56 tab.animator.set_anim_as_last_values(&tab.anim_default(cx));
57 tab
58 }
59
60 pub fn layout_bg() -> LayoutId {uid!()}
61 pub fn text_style_title() -> TextStyleId {uid!()}
62 pub fn instance_border_color() -> InstanceColor {uid!()}
63 pub fn tab_closing() -> InstanceFloat {uid!()}
64 pub fn shader_bg() -> ShaderId {uid!()}
65
66 pub fn style(cx: &mut Cx, opt: &StyleOptions) {
67
68 Self::layout_bg().set(cx, Layout {
69 align: Align::left_center(),
70 walk: Walk::wh(Width::Compute, Height::Fix(40. * opt.scale.powf(0.5))),
71 padding: Padding {l: 16.0, t: 1.0, r: 16.0, b: 0.0},
72 ..Default::default()
73 });
74
75 Self::text_style_title().set(cx, Theme::text_style_normal().get(cx));
76
77 Self::shader_bg().set(cx, Quad::def_quad_shader().compose(shader_ast!({
78
79 let border_color: Self::instance_border_color();
80 const border_width: float = 1.0;
81
82 fn pixel() -> vec4 {
83 df_viewport(pos * vec2(w, h));
84 df_rect(-1., -1., w + 2., h + 2.);
85 df_fill(color);
86 df_move_to(w, 0.);
87 df_line_to(w, h);
88 df_move_to(0., 0.);
89 df_line_to(0., h);
90 return df_stroke(border_color, 1.);
91 }
92 })));
93 }
94
95 pub fn get_bg_color(&self, cx: &Cx) -> Color {
96 if self._is_selected {
97 Theme::color_bg_selected().get(cx)
98 }
99 else {
100 Theme::color_bg_normal().get(cx)
101 }
102 }
103
104 pub fn get_text_color(&self, cx: &Cx) -> Color {
105 if self._is_selected {
106 if self._is_focussed {
107 Theme::color_text_selected_focus().get(cx)
108 }
109 else {
110 Theme::color_text_selected_defocus().get(cx)
111 }
112 }
113 else {
114 if self._is_focussed {
115 Theme::color_text_deselected_focus().get(cx)
116 }
117 else {
118 Theme::color_text_deselected_defocus().get(cx)
119 }
120 }
121 }
122
123 pub fn anim_default(&self, cx: &Cx) -> Anim {
124 Anim::new(Play::Cut {duration: 0.05}, vec![
125 Track::color(Quad::instance_color(), Ease::Lin, vec![(1.0, self.get_bg_color(cx))]),
126 Track::color(Self::instance_border_color(), Ease::Lin, vec![(1.0, Theme::color_bg_selected().get(cx))]),
127 Track::color(Text::instance_color(), Ease::Lin, vec![(1.0, self.get_text_color(cx))]),
128 ])
130 }
131
132 pub fn anim_over(&self, cx: &Cx) -> Anim {
133 Anim::new(Play::Cut {duration: 0.01}, vec![
134 Track::color(Quad::instance_color(), Ease::Lin, vec![(1.0, self.get_bg_color(cx))]),
135 Track::color(Self::instance_border_color(), Ease::Lin, vec![(1.0, Theme::color_bg_selected().get(cx))]),
136 Track::color(Text::instance_color(), Ease::Lin, vec![(1.0, self.get_text_color(cx))]),
137 ])
139 }
140
141 pub fn anim_down(&self, cx: &Cx) -> Anim {
142 Anim::new(Play::Cut {duration: 0.01}, vec![
143 Track::color(Quad::instance_color(), Ease::Lin, vec![(1.0, self.get_bg_color(cx))]),
144 Track::color(Self::instance_border_color(), Ease::Lin, vec![(1.0, Theme::color_bg_selected().get(cx))]),
145 Track::color(Text::instance_color(), Ease::Lin, vec![(1.0, self.get_text_color(cx))]),
146 ])
148 }
149
150 pub fn anim_close(&self, _cx: &Cx) -> Anim {
151 Anim::new(Play::Single {duration: 0.1, cut: true, term: true, end: 1.0}, vec![
152 Track::float(Self::tab_closing(), Ease::OutExp, vec![(0.0, 1.0), (1.0, 0.0)]),
153 ])
154 }
155
156 pub fn set_tab_focus(&mut self, cx: &mut Cx, focus: bool) {
157 if focus != self._is_focussed {
158 self._is_focussed = focus;
159 self.animator.play_anim(cx, self.anim_default(cx));
160 }
161 }
162
163 pub fn set_tab_selected(&mut self, cx: &mut Cx, selected: bool) {
164 if selected != self._is_selected {
165 self._is_selected = selected;
166 self.animator.play_anim(cx, self.anim_default(cx));
167 }
168 }
169
170 pub fn set_tab_state(&mut self, cx: &mut Cx, selected: bool, focus: bool) {
171 self._is_selected = selected;
172 self._is_focussed = focus;
173 self.animator.set_anim_as_last_values(&self.anim_default(cx));
174 }
175
176 pub fn handle_tab(&mut self, cx: &mut Cx, event: &mut Event) -> TabEvent {
177
178 if !self.animator.term_anim_playing() {
179 match self.tab_close.handle_tab_close(cx, event) {
180 ButtonEvent::Down => {
181 self._close_anim_rect = self._bg_area.get_rect(cx);
182 self.animator.play_anim(cx, self.anim_close(cx));
183 return TabEvent::Closing;
184 },
185 _ => ()
186 }
187 }
188
189 match event.hits(cx, self._bg_area, HitOpt::default()) {
190 Event::Animate(ae) => {
191 if self.animator.term_anim_playing() {
193 self.animator.calc_float(cx, Self::tab_closing(), ae.time);
194 cx.redraw_child_area(self._bg_area);
195 }
196 else {
197 self.animator.calc_area(cx, self._bg_area, ae.time);
198 self.animator.calc_area(cx, self._text_area, ae.time);
199 }
200 },
201 Event::AnimEnded(_ae) => {
202 if self.animator.term_anim_playing() {
203 return TabEvent::Close;
204 }
205 else {
206 self.animator.end();
207 }
208 },
209 Event::FingerDown(_fe) => {
210 if self.animator.term_anim_playing() {
211 return TabEvent::None
212 }
213 cx.set_down_mouse_cursor(MouseCursor::Hand);
214 self._is_down = true;
215 self._is_drag = false;
216 self._is_selected = true;
217 self._is_focussed = true;
218 self.animator.play_anim(cx, self.anim_down(cx));
219 return TabEvent::Select;
220 },
221 Event::FingerHover(fe) => {
222 cx.set_hover_mouse_cursor(MouseCursor::Hand);
223 match fe.hover_state {
224 HoverState::In => {
225 if self._is_down {
226 self.animator.play_anim(cx, self.anim_down(cx));
227 }
228 else {
229 self.animator.play_anim(cx, self.anim_over(cx));
230 }
231 },
232 HoverState::Out => {
233 self.animator.play_anim(cx, self.anim_default(cx));
234 },
235 _ => ()
236 }
237 },
238 Event::FingerUp(fe) => {
239 self._is_down = false;
240
241 if fe.is_over {
242 if !fe.is_touch {
243 self.animator.play_anim(cx, self.anim_over(cx));
244 }
245 else {
246 self.animator.play_anim(cx, self.anim_default(cx));
247 }
248 }
249 else {
250 self.animator.play_anim(cx, self.anim_default(cx));
251 }
252 if self._is_drag {
253 self._is_drag = false;
254 return TabEvent::DragEnd(fe);
255 }
256 },
257 Event::FingerMove(fe) => {
258 if !self._is_drag {
259 if fe.move_distance() > 10. {
260 self._is_drag = true;
262 }
263 }
264 if self._is_drag {
265 return TabEvent::DragMove(fe);
266 }
267 },
269 _ => ()
270 };
271 TabEvent::None
272 }
273
274 pub fn get_tab_rect(&mut self, cx: &Cx) -> Rect {
275 self._bg_area.get_rect(cx)
276 }
277
278 pub fn begin_tab(&mut self, cx: &mut Cx) -> Result<(), ()> {
279 self.bg.shader = Self::shader_bg().get(cx);
281 self.bg.z = self.z;
282 self.bg.color = self.animator.last_color(cx, Quad::instance_color());
283
284 if self.animator.term_anim_playing() {
286 let bg_inst = self.bg.draw_quad(
288 cx,
289 Walk::wh(
290 Width::Fix(self._close_anim_rect.w * self.animator.last_float(cx, Self::tab_closing())),
291 Height::Fix(self._close_anim_rect.h),
292 )
293 );
294 bg_inst.push_last_color(cx, &self.animator, Self::instance_border_color());
295 self._bg_area = bg_inst.into();
296 self.animator.set_area(cx, self._bg_area);
297 return Err(())
298 }
299 else {
300 let layout = if let Some(abs_origin) = self.abs_origin {
301 Layout {abs_origin: Some(abs_origin), ..Self::layout_bg().get(cx)}
302 }
303 else {
304 Self::layout_bg().get(cx)
305 };
306 let bg_inst = self.bg.begin_quad(cx, layout);
307 bg_inst.push_last_color(cx, &self.animator, Self::instance_border_color());
308 if self.is_closeable {
309 self.tab_close.draw_tab_close(cx);
310 cx.turtle_align_y();
311 }
312 self.text.z = self.z;
314 self.text.text_style = Self::text_style_title().get(cx);
315 self.text.color = self.animator.last_color(cx, Text::instance_color());
316 self._text_area = self.text.draw_text(cx, &self.label);
317 cx.turtle_align_y();
318 self._bg_inst = Some(bg_inst);
319
320 return Ok(())
321 }
322 }
323
324 pub fn end_tab(&mut self, cx: &mut Cx) {
325 if let Some(bg_inst) = self._bg_inst.take() {
326 self._bg_area = self.bg.end_quad(cx, &bg_inst);
327 self.animator.set_area(cx, self._bg_area); }
329 }
330
331 pub fn draw_tab(&mut self, cx: &mut Cx) {
332 if self.begin_tab(cx).is_err() {return};
333 self.end_tab(cx);
334 }
335
336}