Skip to content

Commit dc94ce6

Browse files
committed
Custom scale slider
1 parent d853267 commit dc94ce6

File tree

2 files changed

+120
-67
lines changed

2 files changed

+120
-67
lines changed

cosmic-settings/src/pages/display/mod.rs

+118-66
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod arrangement;
77
use crate::{app, pages};
88
use arrangement::Arrangement;
99
use cosmic::iced::{time, Alignment, Length};
10+
use cosmic::iced_widget::row;
1011
use cosmic::iced_widget::scrollable::{Direction, RelativeOffset, Scrollbar};
1112
use cosmic::widget::{
1213
self, column, container, dropdown, list_column, segmented_button, tab_bar, text, toggler,
@@ -18,18 +19,32 @@ use cosmic_randr_shell::{
1819
};
1920
use cosmic_settings_page::{self as page, section, Section};
2021
use futures::pin_mut;
21-
use once_cell::sync::Lazy;
2222
use slab::Slab;
2323
use slotmap::{Key, SecondaryMap, SlotMap};
2424
use std::sync::atomic::{AtomicBool, Ordering};
2525
use std::{collections::BTreeMap, process::ExitStatus, sync::Arc};
2626
use tokio::sync::oneshot;
2727
use tracing::error;
2828

29-
static DPI_SCALES: &[u32] = &[50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300];
30-
31-
static DPI_SCALE_LABELS: Lazy<Vec<String>> =
32-
Lazy::new(|| DPI_SCALES.iter().map(|scale| format!("{scale}%")).collect());
29+
static DPI_SCALES: &[ScaleValue] = &[
30+
ScaleValue::Custom,
31+
ScaleValue::Preset(50),
32+
ScaleValue::Preset(75),
33+
ScaleValue::Preset(100),
34+
ScaleValue::Preset(125),
35+
ScaleValue::Preset(150),
36+
ScaleValue::Preset(175),
37+
ScaleValue::Preset(200),
38+
ScaleValue::Preset(225),
39+
ScaleValue::Preset(250),
40+
ScaleValue::Preset(275),
41+
ScaleValue::Preset(300),
42+
];
43+
fn scale_is_custom(idx: usize) -> bool {
44+
idx == 0
45+
}
46+
static DPI_CUSTOM_SCALE_BREAKPOINTS: &[u32] = &[50, 100, 150, 200, 250, 300];
47+
static DPI_CUSTOM_SCALE_RANGE: std::ops::RangeInclusive<u32> = 50..=300;
3348

3449
/// Display color depth options
3550
#[allow(dead_code)]
@@ -50,6 +65,11 @@ pub enum Mirroring {
5065
Mirror(OutputKey),
5166
}
5267

68+
pub enum ScaleValue {
69+
Custom,
70+
Preset(u32),
71+
}
72+
5373
/// Night light preferences
5474
// #[derive(Clone, Copy, Debug)]
5575
// pub enum NightLight {
@@ -101,8 +121,10 @@ pub enum Message {
101121
Resolution(usize),
102122
/// Set the preferred scale for a display.
103123
Scale(usize),
104-
/// Adjust the display scale.
105-
AdjustScale(u32),
124+
/// Set the displayed value for the custom scale.
125+
CustomScaleDrag(u32),
126+
/// Confirm the custom scale change for a display.
127+
CustomScale,
106128
/// Refreshes display outputs.
107129
Update {
108130
/// Available outputs from cosmic-randr.
@@ -153,7 +175,7 @@ pub struct Page {
153175
show_display_options: bool,
154176
comp_config: cosmic_config::Config,
155177
comp_config_descale_xwayland: bool,
156-
adjusted_scale: u32,
178+
custom_scale: Option<u32>,
157179
}
158180

159181
impl Default for Page {
@@ -184,9 +206,9 @@ impl Default for Page {
184206
dialog: None,
185207
dialog_countdown: 0,
186208
show_display_options: true,
187-
adjusted_scale: 0,
188209
comp_config,
189210
comp_config_descale_xwayland,
211+
custom_scale: None,
190212
}
191213
}
192214
}
@@ -214,6 +236,7 @@ struct ViewCache {
214236
vrr_selected: Option<usize>,
215237
resolution_selected: Option<usize>,
216238
scale_selected: Option<usize>,
239+
scales: Vec<String>,
217240
}
218241

219242
impl page::AutoBind<crate::pages::Message> for Page {}
@@ -515,18 +538,15 @@ impl Page {
515538
Message::Resolution(option) => return self.set_resolution(option),
516539

517540
Message::Scale(option) => {
518-
self.adjusted_scale = 0;
519-
return self.set_scale(option);
541+
return self.set_scale_preset(option);
520542
}
521543

522-
Message::AdjustScale(scale) => {
523-
if self.adjusted_scale != scale {
524-
self.adjusted_scale = scale;
544+
Message::CustomScaleDrag(scale) => {
545+
self.custom_scale = Some(scale);
546+
}
525547

526-
if let Some(option) = self.cache.scale_selected {
527-
return self.set_scale(option);
528-
}
529-
}
548+
Message::CustomScale => {
549+
return self.set_scale(self.custom_scale.unwrap());
530550
}
531551

532552
Message::Update { randr } => {
@@ -673,18 +693,28 @@ impl Page {
673693
self.cache.refresh_rate_selected = None;
674694
self.cache.vrr_selected = None;
675695

696+
self.cache.scales = DPI_SCALES
697+
.iter()
698+
.map(|s| match s {
699+
ScaleValue::Custom => fl!("display", "custom-scale-option"),
700+
ScaleValue::Preset(v) => format!("{v}%"),
701+
})
702+
.collect();
676703
let selected_scale = DPI_SCALES
677704
.iter()
678-
.position(|scale| self.config.scale <= *scale)
679-
.unwrap_or(DPI_SCALES.len() - 1);
680-
681-
self.adjusted_scale = ((self.config.scale % 25).min(20) as f32 / 5.0).round() as u32 * 5;
682-
self.cache.scale_selected = Some(if self.adjusted_scale != 0 && selected_scale > 0 {
683-
selected_scale - 1
684-
} else {
685-
selected_scale
686-
});
705+
.position(|s| matches!(s, ScaleValue::Preset(s) if *s == self.config.scale))
706+
.unwrap_or(0);
687707

708+
// If we are using a non-preset scale
709+
if scale_is_custom(selected_scale) {
710+
self.custom_scale = Some(self.config.scale);
711+
}
712+
// If the previous state of the page had a custom scale but it coincides with a preset scale, we are still using a custom scale
713+
if let Some(v) = &mut self.custom_scale {
714+
*v = self.config.scale;
715+
}
716+
717+
self.cache.scale_selected = Some(selected_scale);
688718
if let Some(current_mode_id) = output.current {
689719
for (mode_id, mode) in output
690720
.modes
@@ -927,20 +957,30 @@ impl Page {
927957
Task::batch(tasks)
928958
}
929959

960+
/// Set the scale preset of the active display.
961+
pub fn set_scale_preset(&mut self, option: usize) -> Task<app::Message> {
962+
self.custom_scale = scale_is_custom(option).then_some(self.config.scale);
963+
let scale = match DPI_SCALES[option] {
964+
ScaleValue::Custom => self.custom_scale.unwrap(),
965+
ScaleValue::Preset(value) => value,
966+
};
967+
968+
self.cache.scale_selected = Some(option);
969+
self.set_scale(scale)
970+
}
971+
930972
/// Set the scale of the active display.
931-
pub fn set_scale(&mut self, option: usize) -> Task<app::Message> {
973+
pub fn set_scale(&mut self, scale: u32) -> Task<app::Message> {
932974
let Some(output) = self.list.outputs.get(self.active_display) else {
933975
return Task::none();
934976
};
935977

936-
let mut tasks = Vec::with_capacity(2);
937-
938-
let scale = (option * 25 + 50) as u32 + self.adjusted_scale.min(20);
939-
940-
self.cache.scale_selected = Some(option);
941978
self.config.scale = scale;
942-
tasks.push(self.exec_randr(output, Randr::Scale(scale)));
943-
Task::batch(tasks)
979+
if let Some(s) = &mut self.custom_scale {
980+
*s = scale;
981+
}
982+
983+
self.exec_randr(output, Randr::Scale(scale))
944984
}
945985

946986
/// Enables or disables the active display.
@@ -1132,7 +1172,7 @@ pub fn display_configuration() -> Section<crate::pages::Message> {
11321172
let vrr = descriptions.insert(fl!("vrr"));
11331173
let resolution = descriptions.insert(fl!("display", "resolution"));
11341174
let scale = descriptions.insert(fl!("display", "scale"));
1135-
let additional_scale_options = descriptions.insert(fl!("display", "additional-scale-options"));
1175+
let custom_scale = descriptions.insert(fl!("display", "custom-scale"));
11361176
let orientation = descriptions.insert(fl!("orientation"));
11371177
let enable_label = descriptions.insert(fl!("display", "enable"));
11381178
let options_label = descriptions.insert(fl!("display", "options"));
@@ -1181,38 +1221,50 @@ pub fn display_configuration() -> Section<crate::pages::Message> {
11811221
));
11821222
}
11831223

1184-
items.extend(vec![
1185-
widget::settings::item(
1186-
&descriptions[scale],
1187-
dropdown(&DPI_SCALE_LABELS, page.cache.scale_selected, Message::Scale),
1188-
),
1189-
widget::settings::item(
1190-
&descriptions[additional_scale_options],
1191-
widget::spin_button(
1192-
format!("{}%", page.adjusted_scale),
1193-
page.adjusted_scale,
1194-
5,
1195-
0,
1196-
20,
1197-
Message::AdjustScale,
1198-
),
1224+
items.push(widget::settings::item(
1225+
&descriptions[scale],
1226+
dropdown(
1227+
&page.cache.scales,
1228+
page.cache.scale_selected,
1229+
Message::Scale,
11991230
),
1200-
widget::settings::item(
1201-
&descriptions[orientation],
1202-
dropdown(
1203-
&page.cache.orientations,
1204-
page.cache.orientation_selected,
1205-
|id| {
1206-
Message::Orientation(match id {
1207-
0 => Transform::Normal,
1208-
1 => Transform::Rotate90,
1209-
2 => Transform::Rotate180,
1210-
_ => Transform::Rotate270,
1211-
})
1212-
},
1213-
),
1231+
));
1232+
1233+
if let Some(s) = page.custom_scale {
1234+
items.push(widget::settings::item(
1235+
&descriptions[custom_scale],
1236+
row![
1237+
widget::text::body(format!("{}%", s))
1238+
.width(Length::Fixed(22.0))
1239+
.align_x(Alignment::Center),
1240+
widget::horizontal_space().width(12),
1241+
widget::slider(
1242+
DPI_CUSTOM_SCALE_RANGE.clone(),
1243+
s,
1244+
Message::CustomScaleDrag
1245+
)
1246+
.on_release(Message::CustomScale)
1247+
.breakpoints(DPI_CUSTOM_SCALE_BREAKPOINTS)
1248+
.step(5u32)
1249+
],
1250+
));
1251+
}
1252+
1253+
items.push(widget::settings::item(
1254+
&descriptions[orientation],
1255+
dropdown(
1256+
&page.cache.orientations,
1257+
page.cache.orientation_selected,
1258+
|id| {
1259+
Message::Orientation(match id {
1260+
0 => Transform::Normal,
1261+
1 => Transform::Rotate90,
1262+
2 => Transform::Rotate180,
1263+
_ => Transform::Rotate270,
1264+
})
1265+
},
12141266
),
1215-
]);
1267+
));
12161268

12171269
items
12181270
});

i18n/en/cosmic_settings.ftl

+2-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,8 @@ display = Displays
382382
.refresh-rate = Refresh rate
383383
.resolution = Resolution
384384
.scale = Scale
385-
.additional-scale-options = Additional scale options
385+
.custom-scale-option = Custom
386+
.custom-scale = Custom Scale
386387
387388
mirroring = Mirroring
388389
.id = Mirroring { $id }

0 commit comments

Comments
 (0)