Files
feresaria/shell/ferese-settings/src/displays_ui.rs
T

174 lines
8.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use cosmic::Element;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{column, container, row};
use crate::displays::{self, Refresh};
use crate::{App, Message, schema, visuals};
impl App {
pub(super) fn displays_view(&self) -> Element<'_, Message> {
let palette = visuals::Palette::from_resolved(&self.resolved.presented);
let targets = displays::targets(&self.draft, &self.displays);
let selected = displays::selected(&targets, self.display_selection.as_deref()).cloned();
let mut panel = column([]).spacing(6);
panel = panel.push(
row([])
.spacing(8)
.align_y(Alignment::Center)
.push(visuals::action_icon("M3 4h18v13H3z M12 17v4 M8 21h8", palette.accent))
.push(
ferese_theme::controls::select(
cosmic::iced::widget::pick_list(targets.clone(), selected.clone(), |target| {
Message::SelectDisplay(target.key)
})
.placeholder("Select display")
.font(self.font)
.text_size(12)
.padding([4, 8])
.width(Length::Fill),
palette,
)
.width(Length::Fill),
)
.push(self.settings_icon_button(
"Refresh displays",
"M20 7V3l-3 3a8 8 0 1 0 3 6 M20 3h-5",
Some(Message::RefreshDisplays),
)),
);
if let Some(error) = &self.display_error {
panel = panel.push(cosmic::widget::tooltip(
self.note("Live display information unavailable"),
self.label(error.clone(), 11.),
cosmic::widget::tooltip::Position::Bottom,
));
}
if let Some(target) = selected {
let live = self
.displays
.iter()
.find(|d| d.connector == target.matcher || d.identity == target.matcher);
if let Some(display) = live {
let status = display
.current
.map(|mode| format!("{} × {} · {}", mode.width, mode.height, mode.label()))
.unwrap_or_else(|| "Off".into());
panel = panel.push(self.note(&status));
}
if let Some(prefix) = target.prefix {
let configured = self.draft.string(&format!("{prefix}.mode"), "");
let automatic = self.draft.boolean(&format!("{prefix}.auto_refresh"), false);
if let Some(display) = live {
let resolutions = displays::resolutions(display);
if !resolutions.is_empty() {
let path = prefix.clone();
panel = panel.push(
self.display_control(
"Resolution",
ferese_theme::controls::select(
cosmic::iced::widget::pick_list(
resolutions,
displays::resolution(&configured, display),
move |size| Message::DisplayResolution(path.clone(), size),
)
.font(self.font)
.text_size(12)
.padding([4, 8])
.width(225),
palette,
)
.width(225)
.into(),
),
);
} else {
panel = panel.push(self.display_mode_field(&prefix));
}
let modes = displays::choices(display, &configured);
let mut refresh: Vec<_> = modes.iter().copied().map(Refresh::Manual).collect();
if modes.iter().any(|mode| (59_000..=61_000).contains(&mode.refresh))
&& let Some(high) = modes.last()
{
refresh.push(Refresh::Automatic(*high));
}
if !refresh.is_empty() {
let configured_rate = configured
.split('@')
.nth(1)
.and_then(|r| r.parse::<f64>().ok())
.map(|r| r * 1000.)
.or_else(|| display.current.map(|m| f64::from(m.refresh)));
let selected = refresh.iter().copied().find(|value| match value {
Refresh::Automatic(_) => automatic,
Refresh::Manual(mode) => {
!automatic && configured_rate.is_some_and(|r| (r - f64::from(mode.refresh)).abs() < 1.)
}
});
let path = prefix.clone();
panel = panel.push(
self.display_control(
"Refresh rate",
ferese_theme::controls::select(
cosmic::iced::widget::pick_list(refresh, selected, move |value| match value {
Refresh::Manual(mode) => Message::RefreshRate(path.clone(), mode, false),
Refresh::Automatic(mode) => Message::RefreshRate(path.clone(), mode, true),
})
.font(self.font)
.text_size(12)
.padding([4, 8])
.width(225),
palette,
)
.width(225)
.into(),
),
);
}
} else {
panel = panel.push(self.display_mode_field(&prefix));
}
panel = panel.push(self.field(schema::range(
format!("{prefix}.scale"),
"Scale",
"",
schema::RangeSpec {
default: 1.,
min: 0.75,
max: 3.,
step: 0.25,
suffix: "×",
integer: false,
},
)));
panel = panel.push(row([]).spacing(5).align_y(Alignment::Center)
.push(cosmic::widget::tooltip(visuals::action_icon("M12 3a9 9 0 1 0 0 18a9 9 0 0 0 0-18 M12 11v6 M12 7h.01", palette.muted),
self.label("Auto uses 60 Hz below 30% battery and returns to normal on AC or at 35%. Applying a display mode can briefly blank the screen.", 11.), cosmic::widget::tooltip::Position::Top))
.push(self.note(if target.active { "Changes apply live" } else { "Saved profile · applies when active" })));
} else {
panel = panel.push(self.note("Managed automatically · no saved display profile"));
}
} else {
panel = panel.push(self.note("No displays or saved profiles found"));
}
container(panel)
.padding([7, 10])
.width(Length::Fill)
.class(visuals::surface(palette.card, 14.))
.into()
}
fn display_control<'a>(&self, label: &str, control: Element<'a, Message>) -> Element<'a, Message> {
row([])
.spacing(10)
.align_y(Alignment::Center)
.padding([7, 10])
.push(self.label(label.to_owned(), 13.).width(Length::Fill))
.push(control)
.into()
}
fn display_mode_field(&self, prefix: &str) -> Element<'static, Message> {
self.field(schema::text(format!("{prefix}.mode"), "Resolution / refresh", "", ""))
}
}