use crate::{
build_unit_selector,
layout::{LayoutGen, Position, Rect, Size},
output_tabs::OutputTabs,
util::{convert_units, friendly_ftoa, length_from_unit, PosExt, Repack, Unit},
Number::*,
RcUi, ADDED_HEIGHT, GROUP_H_PADDING, GROUP_V_PADDING, LINE_V_PADDING,
};
use fltk::{frame::Frame, input::FloatInput, menu::Choice, prelude::*};
use fpvsetup::{find_common_aspect_ratio, MonitorDimensions};
use std::{convert::TryInto, rc::Rc};
use uom::si::length::centimeter;
#[derive(Clone)]
pub struct MonitorProperties {
pub width_label: Frame,
pub width_input: FloatInput,
pub width_unit_selector: Choice,
pub height_label: Frame,
pub height_input: FloatInput,
pub height_unit_selector: Choice,
pub diagonal_label: Frame,
pub diagonal_input: FloatInput,
pub diagonal_unit_selector: Choice,
pub aspect_label: Frame,
pub aspect_n_input: FloatInput,
pub aspect_sep: Frame,
pub aspect_d_input: FloatInput,
pub distance_label: Frame,
pub distance_input: FloatInput,
pub distance_unit_selector: Choice,
}
impl MonitorProperties {
pub fn new(ui: &RcUi, monitor_dimensions: Option<MonitorDimensions>) -> Self {
let width_label = Frame::default().with_label("Monitor width:");
let mut width_input = FloatInput::default();
let r = Rc::clone(ui);
width_input.set_callback(move || Self::width_or_height_change_handler(&r));
width_input.set_trigger(CallbackTrigger::Changed);
if let Some(dim) = monitor_dimensions {
let [width, _] = dim.width_and_height();
width_input.set_value(&friendly_ftoa(width.get::<centimeter>()))
}
let width_unit_selector =
build_unit_selector(&width_input, Some(Unit::Centimeters), Plural, false);
let height_label = Frame::default().with_label(", height:");
let mut height_input = FloatInput::default();
let r = Rc::clone(ui);
height_input.set_callback(move || Self::width_or_height_change_handler(&r));
height_input.set_trigger(CallbackTrigger::Changed);
if let Some(dim) = monitor_dimensions {
let [_, height] = dim.width_and_height();
height_input.set_value(&friendly_ftoa(height.get::<centimeter>()))
}
let height_unit_selector =
build_unit_selector(&height_input, Some(Unit::Centimeters), Plural, false);
let diagonal_label = Frame::default().with_label("Monitor diagonal:");
let mut diagonal_input = FloatInput::default();
let r = Rc::clone(ui);
diagonal_input.set_callback(move || Self::diagonal_or_aspect_change_handler(&r));
diagonal_input.set_trigger(CallbackTrigger::Changed);
let diagonal_unit_selector =
build_unit_selector(&diagonal_input, Some(Unit::Inches), Plural, false);
let aspect_label = Frame::default().with_label(", aspect ratio:");
let mut aspect_n_input = FloatInput::default();
let r = Rc::clone(ui);
aspect_n_input.set_callback(move || Self::diagonal_or_aspect_change_handler(&r));
aspect_n_input.set_trigger(CallbackTrigger::Changed);
let mut aspect_sep = Frame::default().with_label(":");
aspect_sep.set_label_font(Font::HelveticaBold);
let mut aspect_d_input = FloatInput::default();
let r = Rc::clone(ui);
aspect_d_input.set_callback(move || Self::diagonal_or_aspect_change_handler(&r));
aspect_d_input.set_trigger(CallbackTrigger::Changed);
let distance_label = Frame::default().with_label("Viewing distance:");
let mut distance_input = FloatInput::default();
let r = Rc::clone(ui);
distance_input.set_callback(move || OutputTabs::update(&r));
distance_input.set_trigger(CallbackTrigger::Changed);
let distance_unit_selector =
build_unit_selector(&distance_input, Some(Unit::Centimeters), Plural, false);
Self {
width_label,
width_input,
width_unit_selector,
height_label,
height_input,
height_unit_selector,
diagonal_label,
diagonal_input,
diagonal_unit_selector,
aspect_label,
aspect_n_input,
aspect_sep,
aspect_d_input,
distance_label,
distance_input,
distance_unit_selector,
}
}
pub fn apply_layout(&mut self, layout: &MonitorPropertiesLayout, pos: Position) {
self.width_label
.set_rect(layout.width_label.with_added_pos(pos));
self.width_input
.set_rect(layout.width_input.with_added_pos(pos));
self.width_unit_selector
.set_rect(layout.width_unit_selector.with_added_pos(pos));
self.height_label
.set_rect(layout.height_label.with_added_pos(pos));
self.height_input
.set_rect(layout.height_input.with_added_pos(pos));
self.height_unit_selector
.set_rect(layout.height_unit_selector.with_added_pos(pos));
self.diagonal_label
.set_rect(layout.diagonal_label.with_added_pos(pos));
self.diagonal_input
.set_rect(layout.diagonal_input.with_added_pos(pos));
self.diagonal_unit_selector
.set_rect(layout.diagonal_unit_selector.with_added_pos(pos));
self.aspect_label
.set_rect(layout.aspect_label.with_added_pos(pos));
self.aspect_n_input
.set_rect(layout.aspect_n_input.with_added_pos(pos));
self.aspect_sep
.set_rect(layout.aspect_sep.with_added_pos(pos));
self.aspect_d_input
.set_rect(layout.aspect_d_input.with_added_pos(pos));
self.distance_label
.set_rect(layout.distance_label.with_added_pos(pos));
self.distance_input
.set_rect(layout.distance_input.with_added_pos(pos));
self.distance_unit_selector
.set_rect(layout.distance_unit_selector.with_added_pos(pos));
}
pub fn width_or_height_change_handler(ui: &RcUi) {
let mut _p = ui.borrow_mut();
let p = &mut _p.as_mut().unwrap().monitor_properties;
let width = p.width_input.value().parse::<f64>();
let height = p.height_input.value().parse::<f64>();
if let (Ok(width), Ok(height)) = (width, height) {
let width_unit = p.width_unit_selector.value().try_into().unwrap();
let height_unit = p.height_unit_selector.value().try_into().unwrap();
let dimensions = MonitorDimensions::WidthAndHeight {
width: length_from_unit(width, width_unit),
height: length_from_unit(height, height_unit),
};
let diagonal = convert_units(
dimensions.diagonal(),
p.diagonal_unit_selector.value().try_into().unwrap(),
);
let aspect = dimensions.aspect();
p.diagonal_input.set_value(&friendly_ftoa(diagonal));
let [n, d] = find_common_aspect_ratio(aspect, 0.1).unwrap_or([aspect, 1.0]);
p.aspect_n_input.set_value(&friendly_ftoa(n));
p.aspect_d_input.set_value(&friendly_ftoa(d));
drop(_p);
OutputTabs::update(ui);
}
}
fn diagonal_or_aspect_change_handler(ui: &RcUi) {
let mut _p = ui.borrow_mut();
let p = &mut _p.as_mut().unwrap().monitor_properties;
let diagonal = p.diagonal_input.value().parse::<f64>();
let aspect_n = p.aspect_n_input.value().parse::<f64>();
let aspect_d = p.aspect_d_input.value().parse::<f64>();
if let (Ok(diagonal), [Ok(n), Ok(d)]) = (diagonal, [aspect_n, aspect_d]) {
let diagonal_unit = p.diagonal_unit_selector.value().try_into().unwrap();
let dimensions = MonitorDimensions::DiagonalAndAspect {
diagonal: length_from_unit(diagonal, diagonal_unit),
aspect: n / d,
};
let [width, height] = dimensions.width_and_height();
let width = convert_units(width, p.width_unit_selector.value().try_into().unwrap());
let height = convert_units(height, p.height_unit_selector.value().try_into().unwrap());
p.width_input.set_value(&friendly_ftoa(width));
p.height_input.set_value(&friendly_ftoa(height));
drop(_p);
OutputTabs::update(ui);
}
}
}
impl LayoutGen<'_> for MonitorProperties {
type Arguments = ();
type Layout = MonitorPropertiesLayout;
fn generate_layout(&self, _: Self::Arguments) -> Self::Layout {
const NUM_LINES: i32 = 3;
let height_l1;
let mut width_l1 = GROUP_H_PADDING * 2;
let width_label = Rect(
Position(GROUP_H_PADDING, GROUP_V_PADDING),
self.width_label.measure_label().repack(),
);
height_l1 = width_label.h() + ADDED_HEIGHT;
width_l1 += width_label.w();
let width_input = Rect(width_label.to_right(5), Size(70, height_l1));
width_l1 += width_input.w() + 5;
let width_unit_selector = Rect(width_input.to_right(5), Size(105, height_l1));
width_l1 += self.width_unit_selector.w() + 5;
let height_label = Rect(
width_unit_selector.to_right(0),
self.height_label.measure_label().repack(),
);
width_l1 += height_label.w();
let height_input = Rect(height_label.to_right(5), Size(70, height_l1));
width_l1 += height_input.w() + 5;
let height_unit_selector = Rect(height_input.to_right(5), Size(105, height_l1));
width_l1 += height_unit_selector.w() + 5;
let height_l2;
let mut width_l2 = GROUP_H_PADDING * 2;
let diagonal_label = Rect(
width_label.to_bottom(LINE_V_PADDING),
self.diagonal_label.measure_label().repack(),
);
height_l2 = diagonal_label.h() + ADDED_HEIGHT;
width_l2 += diagonal_label.w();
let diagonal_input = Rect(diagonal_label.to_right(5), Size(70, height_l2));
width_l2 += diagonal_input.w() + 5;
let diagonal_unit_selector = Rect(diagonal_input.to_right(5), Size(105, height_l2));
width_l2 += diagonal_unit_selector.w() + 5;
let aspect_label = Rect(
diagonal_unit_selector.to_right(0),
self.aspect_label.measure_label().repack(),
);
width_l2 += aspect_label.w();
let aspect_n_input = Rect(aspect_label.to_right(5), Size(65, height_l2));
width_l2 += aspect_n_input.w() + 5;
let aspect_sep = Rect(
aspect_n_input.to_right(1),
self.aspect_sep.measure_label().repack(),
);
width_l2 += aspect_sep.w() + 1;
let aspect_d_input = Rect(aspect_sep.to_right(1), Size(65, height_l2));
width_l2 += aspect_d_input.w() + 1;
let height_l3;
let mut width_l3 = GROUP_H_PADDING * 2;
let distance_label = Rect(
diagonal_label.to_bottom(LINE_V_PADDING),
self.distance_label.measure_label().repack(),
);
height_l3 = distance_label.h() + ADDED_HEIGHT;
width_l3 += distance_label.w();
let distance_input = Rect(distance_label.to_right(5), Size(70, height_l3));
width_l3 += distance_input.w() + 5;
let distance_unit_selector = Rect(distance_input.to_right(5), Size(105, height_l3));
width_l3 += distance_unit_selector.w() + 5;
let total_width = [width_l1, width_l2, width_l3]
.iter()
.copied()
.max()
.unwrap();
let total_height = height_l1
+ height_l2
+ height_l3
+ LINE_V_PADDING * (NUM_LINES - 1)
+ GROUP_V_PADDING * 2;
MonitorPropertiesLayout {
total_size: Size(total_width, total_height),
width_label,
width_input,
width_unit_selector,
height_label,
height_input,
height_unit_selector,
diagonal_label,
diagonal_input,
diagonal_unit_selector,
aspect_label,
aspect_n_input,
aspect_sep,
aspect_d_input,
distance_label,
distance_input,
distance_unit_selector,
}
}
}
make_layout!(pub MonitorPropertiesLayout, has
width_label, width_input, width_unit_selector,
height_label, height_input, height_unit_selector,
diagonal_label, diagonal_input, diagonal_unit_selector,
aspect_label, aspect_n_input, aspect_sep, aspect_d_input,
distance_label, distance_input, distance_unit_selector,
);