perspective_viewer/components/
number_column_style.rs1use yew::prelude::*;
14use yew::*;
15
16use super::form::color_range_selector::*;
17use super::form::number_field::NumberFieldProps;
18use super::modal::*;
19use super::style::LocalStyle;
20use crate::components::form::number_field::NumberField;
21use crate::components::form::select_field::SelectEnumField;
22use crate::config::*;
23use crate::session::Session;
24use crate::utils::WeakScope;
25use crate::*;
26
27#[derive(PartialEq, Eq, Copy, Clone, Debug)]
28pub enum Side {
29 Fg,
30 Bg,
31}
32
33use Side::*;
34
35#[derive(Debug)]
36pub enum NumberColumnStyleMsg {
37 PosColorChanged(Side, String),
38 NegColorChanged(Side, String),
39 NumberForeModeChanged(NumberForegroundMode),
40 NumberBackModeChanged(NumberBackgroundMode),
41 GradientChanged(Side, Option<f64>),
42 DefaultGradientChanged(f64),
43}
44
45#[derive(Properties)]
51pub struct NumberColumnStyleProps {
52 #[cfg_attr(test, prop_or_default)]
53 pub config: Option<NumberColumnStyleConfig>,
54
55 #[cfg_attr(test, prop_or_default)]
56 pub default_config: NumberColumnStyleDefaultConfig,
57
58 #[prop_or_default]
59 pub on_change: Callback<ColumnConfigValueUpdate>,
60
61 #[prop_or_default]
62 pub weak_link: WeakScope<NumberColumnStyle>,
63
64 #[prop_or_default]
65 pub session: Option<Session>,
66
67 #[prop_or_default]
68 pub column_name: Option<String>,
69}
70
71impl ModalLink<NumberColumnStyle> for NumberColumnStyleProps {
72 fn weak_link(&self) -> &'_ WeakScope<NumberColumnStyle> {
73 &self.weak_link
74 }
75}
76
77impl PartialEq for NumberColumnStyleProps {
78 fn eq(&self, _other: &Self) -> bool {
79 false
80 }
81}
82
83impl NumberColumnStyleProps {
84 fn set_default_gradient(&self, ctx: &Context<NumberColumnStyle>) {
85 if let Some(session) = self.session.clone()
86 && let Some(column_name) = self.column_name.clone()
87 {
88 ctx.link().send_future(async move {
89 let view = session.get_view().unwrap();
90 let min_max = view.get_min_max(column_name).await.unwrap();
91 let abs_max = max!(
92 min_max.0.parse::<f64>().unwrap_or_default().abs(),
93 min_max.1.parse::<f64>().unwrap_or_default().abs()
94 );
95 let gradient = (abs_max * 100.).round() / 100.;
96 NumberColumnStyleMsg::DefaultGradientChanged(gradient)
97 });
98 }
99 }
100}
101
102pub struct NumberColumnStyle {
104 config: NumberColumnStyleConfig,
105 default_config: NumberColumnStyleDefaultConfig,
106 fg_mode: NumberForegroundMode,
107 bg_mode: NumberBackgroundMode,
108 pos_fg_color: String,
109 neg_fg_color: String,
110 pos_bg_color: String,
111 neg_bg_color: String,
112 fg_gradient: Option<f64>,
113 bg_gradient: Option<f64>,
114}
115
116impl Component for NumberColumnStyle {
117 type Message = NumberColumnStyleMsg;
118 type Properties = NumberColumnStyleProps;
119
120 fn create(ctx: &Context<Self>) -> Self {
121 ctx.set_modal_link();
122 ctx.props().set_default_gradient(ctx);
123 Self::reset(
124 &ctx.props().config.clone().unwrap_or_default(),
125 &ctx.props().default_config.clone(),
126 )
127 }
128
129 fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
130 let mut new = Self::reset(
131 &ctx.props().config.clone().unwrap_or_default(),
132 &ctx.props().default_config.clone(),
133 );
134 ctx.props().set_default_gradient(ctx);
135 std::mem::swap(self, &mut new);
136 true
137 }
138
139 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
140 match msg {
141 NumberColumnStyleMsg::PosColorChanged(side, val) => {
197 if side == Fg {
198 self.pos_fg_color = val;
199 self.config.pos_fg_color = Some(self.pos_fg_color.to_owned());
200 } else {
201 self.pos_bg_color = val;
202 self.config.pos_bg_color = Some(self.pos_bg_color.to_owned());
203 }
204
205 self.dispatch_config(ctx);
206 true
207 },
208 NumberColumnStyleMsg::NegColorChanged(side, val) => {
209 if side == Fg {
210 self.neg_fg_color = val;
211 self.config.neg_fg_color = Some(self.neg_fg_color.to_owned());
212 } else {
213 self.neg_bg_color = val;
214 self.config.neg_bg_color = Some(self.neg_bg_color.to_owned());
215 }
216
217 self.dispatch_config(ctx);
218 true
219 },
220 NumberColumnStyleMsg::NumberForeModeChanged(val) => {
221 self.fg_mode = val;
222 self.config.number_fg_mode = val;
223 if self.fg_mode.needs_gradient() {
224 self.config.fg_gradient = Some(self.fg_gradient.unwrap());
225 } else {
226 self.config.fg_gradient = None;
227 }
228
229 self.dispatch_config(ctx);
230 true
231 },
232 NumberColumnStyleMsg::NumberBackModeChanged(val) => {
233 self.bg_mode = val;
234 self.config.number_bg_mode = val;
235 if self.bg_mode.needs_gradient() {
236 self.config.bg_gradient = Some(self.bg_gradient.unwrap());
237 } else {
238 self.config.bg_gradient = None;
239 }
240
241 self.dispatch_config(ctx);
242 true
243 },
244 NumberColumnStyleMsg::GradientChanged(side, gradient) => {
245 match (side, gradient) {
246 (Fg, Some(x)) => {
247 self.fg_gradient = Some(x);
248 self.config.fg_gradient = Some(x);
249 },
250 (Fg, None) => {
251 self.fg_gradient = Some(self.default_config.fg_gradient);
252 self.config.fg_gradient = None;
253 },
254 (Bg, Some(x)) => {
255 self.bg_gradient = Some(x);
256 self.config.bg_gradient = Some(x);
257 },
258 (Bg, None) => {
259 self.bg_gradient = Some(self.default_config.bg_gradient);
260 self.config.bg_gradient = None;
261 },
262 };
263
264 self.dispatch_config(ctx);
265 false
266 },
267 NumberColumnStyleMsg::DefaultGradientChanged(gradient) => {
268 self.fg_gradient.get_or_insert(gradient);
269 self.bg_gradient.get_or_insert(gradient);
270 self.default_config.fg_gradient = gradient;
271 self.default_config.bg_gradient = gradient;
272 true
273 },
274 }
275 }
276
277 fn view(&self, ctx: &Context<Self>) -> Html {
278 let fg_mode_changed = ctx.link().callback(|x: Option<_>| {
279 NumberColumnStyleMsg::NumberForeModeChanged(x.unwrap_or_default())
280 });
281
282 let bg_mode_changed = ctx.link().callback(|x: Option<_>| {
283 NumberColumnStyleMsg::NumberBackModeChanged(x.unwrap_or_default())
284 });
285
286 let fg_controls = match self.fg_mode {
287 NumberForegroundMode::Disabled => html! {},
288 NumberForegroundMode::Color => html! {
289 <div class="row">
290 <ColorRangeSelector ..self.color_props("fg-color", Fg, false, ctx) />
291 </div>
292 },
293 NumberForegroundMode::Bar => html! {
294 <>
295 <div class="row">
296 <ColorRangeSelector ..self.color_props("bar-color", Fg, false, ctx) />
297 </div>
298 <NumberField ..self.max_value_props(Fg, ctx) />
299 </>
300 },
301 };
302
303 let bg_controls = match self.bg_mode {
304 NumberBackgroundMode::Disabled => html! {},
305 NumberBackgroundMode::Color => html! {
306 <div class="row">
307 <ColorRangeSelector ..self.color_props("bg-color", Bg,false, ctx) />
308 </div>
309 },
310 NumberBackgroundMode::Gradient => html! {
311 <>
312 <div class="row">
313 <ColorRangeSelector ..self.color_props("gradient-color", Bg, true, ctx) />
314 </div>
315 <NumberField ..self.max_value_props(Bg, ctx) />
316 </>
317 },
318 NumberBackgroundMode::Pulse => html! {
319 <div class="row">
320 <ColorRangeSelector ..self.color_props("pulse-color", Bg, true, ctx) />
321 </div>
322 },
323 };
324
325 html! {
326 <>
327 <LocalStyle href={css!("column-style")} />
328 <div id="column-style-container" class="number-column-style-container">
329 <SelectEnumField<NumberForegroundMode>
330 label="foreground"
331 on_change={fg_mode_changed}
332 current_value={self.fg_mode}
333 />
334 { fg_controls }
335 <SelectEnumField<NumberBackgroundMode>
336 label="background"
337 on_change={bg_mode_changed}
338 current_value={self.bg_mode}
339 />
340 { bg_controls }
341 </div>
342 </>
343 }
344 }
345}
346
347impl NumberColumnStyle {
348 fn dispatch_config(&self, ctx: &Context<Self>) {
350 let mut config = self.config.clone();
351 match &self.config {
352 NumberColumnStyleConfig {
353 pos_fg_color: Some(pos_color),
354 neg_fg_color: Some(neg_color),
355 ..
356 } if *pos_color == self.default_config.pos_fg_color
357 && *neg_color == self.default_config.neg_fg_color =>
358 {
359 config.pos_fg_color = None;
360 config.neg_fg_color = None;
361 },
362 _ => {},
363 };
364
365 match &self.config {
366 NumberColumnStyleConfig {
367 pos_bg_color: Some(pos_color),
368 neg_bg_color: Some(neg_color),
369 ..
370 } if *pos_color == self.default_config.pos_bg_color
371 && *neg_color == self.default_config.neg_bg_color =>
372 {
373 config.pos_bg_color = None;
374 config.neg_bg_color = None;
375 },
376 _ => {},
377 };
378
379 let update = Some(config).filter(|config| config != &NumberColumnStyleConfig::default());
380
381 ctx.props()
382 .on_change
383 .emit(ColumnConfigValueUpdate::DatagridNumberStyle(update));
384 }
385
386 fn color_props(
387 &self,
388 id: &str,
389 side: Side,
390 is_gradient: bool,
391 ctx: &Context<Self>,
392 ) -> ColorRangeProps {
393 let on_pos_color = ctx
394 .link()
395 .callback(move |x| NumberColumnStyleMsg::PosColorChanged(side, x));
396 let on_neg_color = ctx
397 .link()
398 .callback(move |x| NumberColumnStyleMsg::NegColorChanged(side, x));
399
400 let default_config = self.default_config.clone();
401
402 props!(ColorRangeProps {
403 id: id.to_string(),
404 is_gradient,
405 pos_color: if side == Fg {
406 &self.pos_fg_color
407 } else {
408 &self.pos_bg_color
409 }
410 .to_owned(),
411 neg_color: if side == Fg {
412 &self.neg_fg_color
413 } else {
414 &self.neg_bg_color
415 }
416 .to_owned(),
417 on_pos_color,
418 on_neg_color,
419 on_reset: ctx.link().batch_callback(move |_| if side == Fg {
420 vec![
421 NumberColumnStyleMsg::PosColorChanged(
422 side,
423 default_config.pos_fg_color.clone(),
424 ),
425 NumberColumnStyleMsg::NegColorChanged(
426 side,
427 default_config.neg_fg_color.clone(),
428 ),
429 ]
430 } else {
431 vec![
432 NumberColumnStyleMsg::PosColorChanged(
433 side,
434 default_config.pos_bg_color.clone(),
435 ),
436 NumberColumnStyleMsg::NegColorChanged(
437 side,
438 default_config.neg_bg_color.clone(),
439 ),
440 ]
441 }),
442 is_modified: if side == Fg {
443 self.pos_fg_color != self.default_config.pos_fg_color
444 || self.neg_fg_color != self.default_config.neg_fg_color
445 } else {
446 self.pos_bg_color != self.default_config.pos_bg_color
447 || self.neg_bg_color != self.default_config.neg_bg_color
448 },
449 })
450 }
451
452 fn max_value_props(&self, side: Side, ctx: &Context<Self>) -> NumberFieldProps {
453 let on_change = ctx
454 .link()
455 .callback(move |x| NumberColumnStyleMsg::GradientChanged(side, x));
456
457 let value = if side == Fg {
458 self.fg_gradient.unwrap_or_default()
459 } else {
460 self.bg_gradient.unwrap_or_default()
461 };
462
463 props!(NumberFieldProps {
464 default: value,
465 current_value: value,
466 label: "max-value",
467 on_change
468 })
469 }
470
471 fn reset(
472 config: &NumberColumnStyleConfig,
473 default_config: &NumberColumnStyleDefaultConfig,
474 ) -> Self {
475 let mut config = config.clone();
476 let fg_gradient = config.fg_gradient;
477 let bg_gradient = config.bg_gradient;
478
479 let pos_fg_color = config
480 .pos_fg_color
481 .as_ref()
482 .unwrap_or(&default_config.pos_fg_color)
483 .to_owned();
484
485 let neg_fg_color = config
486 .neg_fg_color
487 .as_ref()
488 .unwrap_or(&default_config.neg_fg_color)
489 .to_owned();
490
491 let pos_bg_color = config
492 .pos_bg_color
493 .as_ref()
494 .unwrap_or(&default_config.pos_bg_color)
495 .to_owned();
496
497 let neg_bg_color = config
498 .neg_bg_color
499 .as_ref()
500 .unwrap_or(&default_config.neg_bg_color)
501 .to_owned();
502
503 config.pos_fg_color = Some(pos_fg_color.to_owned());
504 config.neg_fg_color = Some(neg_fg_color.to_owned());
505 let fg_mode = config.number_fg_mode;
506 config.pos_bg_color = Some(pos_bg_color.to_owned());
507 config.neg_bg_color = Some(neg_bg_color.to_owned());
508 let bg_mode = config.number_bg_mode;
509 Self {
510 config,
511 default_config: default_config.clone(),
512 fg_mode,
513 bg_mode,
514 pos_fg_color,
515 neg_fg_color,
516 pos_bg_color,
517 neg_bg_color,
518 fg_gradient,
519 bg_gradient,
520 }
521 }
522}