mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-14 09:27:04 +01:00
feat(gui,core): larger icon sizes in My files
The icon grid's size slider stops at 256 px instead of 144 px, which is big enough to tell photos apart by their thumbnail. Thumbnails are 512 px, so they stay sharp at that size on a 2x screen. Names may wrap wider on the larger tiles. Ctrl+plus, Ctrl+minus and Ctrl+0 step and reset the size while the grid is shown, as they do in Photos. The size is saved in the config as files_view.icon_size once the slider rests; configs without it keep the 72 px default.
This commit is contained in:
1 parent
1c50797772
commit
e571f22d5d
15 files changed
+3561
-3487
No files matched your search
@@ -224,6 +224,10 @@ pub struct FilesView {
|
||||
pub descending: bool,
|
||||
/// Folders ahead of files, whatever the sort.
|
||||
pub folders_first: bool,
|
||||
/// The icon grid's thumbnail edge in logical pixels. `None` means the
|
||||
/// app's default.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon_size: Option<i32>,
|
||||
}
|
||||
|
||||
impl Default for FilesView {
|
||||
@@ -233,6 +237,7 @@ impl Default for FilesView {
|
||||
sort: FileSort::Name,
|
||||
descending: false,
|
||||
folders_first: true,
|
||||
icon_size: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,6 +782,7 @@ mod tests {
|
||||
sort: FileSort::Modified,
|
||||
descending: true,
|
||||
folders_first: false,
|
||||
icon_size: Some(192),
|
||||
},
|
||||
photos_view: PhotosView {
|
||||
row_height: Some(240),
|
||||
@@ -856,6 +862,7 @@ mod tests {
|
||||
let partial: FilesView = serde_json::from_str(r#"{"sort":"size"}"#).unwrap();
|
||||
assert_eq!(partial.sort, FileSort::Size);
|
||||
assert!(partial.folders_first);
|
||||
assert_eq!(partial.icon_size, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -53,7 +53,7 @@ button.sidebar-account { padding: 4px 8px; font-weight: normal; }
|
||||
/* The folder on screen, last in the breadcrumb trail: a flat button's side
|
||||
* padding, so the "›" before it is spaced like the ones between buttons. */
|
||||
.crumb-current { padding: 0 10px; }
|
||||
scale.browser-status-meter trough { min-width: 104px; min-height: 6px; }
|
||||
scale.browser-status-meter trough { min-width: 140px; min-height: 6px; }
|
||||
.bulk-bar { padding: 6px 8px; border-radius: 12px; background: color-mix(in srgb, var(--accent-bg-color) 12%, transparent); }
|
||||
.dropzone { border: 2px dashed alpha(currentColor, 0.25); border-radius: 16px; padding: 28px 18px; transition: background 160ms ease, border-color 160ms ease; }
|
||||
.dropzone-active { border-color: var(--accent-bg-color); background: color-mix(in srgb, var(--accent-bg-color) 10%, transparent); }
|
||||
|
||||
@@ -488,6 +488,7 @@ fn build_window(app: &adw::Application) {
|
||||
empty_actions: browser_widgets.empty_actions.clone(),
|
||||
summary: browser_widgets.summary.clone(),
|
||||
zoom: browser_widgets.zoom.clone(),
|
||||
zoom_save: RefCell::new(None),
|
||||
},
|
||||
details: DetailsState {
|
||||
details: browser_widgets.details,
|
||||
@@ -1523,6 +1524,9 @@ fn show_shortcuts(window: &adw::ApplicationWindow) {
|
||||
("<Alt>Left <Alt>Right", gettext_noop("Back / forward")),
|
||||
("<Alt>Up", gettext_noop("Parent folder")),
|
||||
("<Control>1 <Control>2", gettext_noop("Grid / list")),
|
||||
("<Control>plus", gettext_noop("Larger thumbnails")),
|
||||
("<Control>minus", gettext_noop("Smaller thumbnails")),
|
||||
("<Control>0", gettext_noop("Reset thumbnail size")),
|
||||
("F2", gettext_noop("Rename")),
|
||||
("Delete", gettext_noop("Move to Trash")),
|
||||
("Escape", gettext_noop("Clear the selection")),
|
||||
@@ -1588,6 +1592,21 @@ fn install_shortcuts(ui: &Rc<Ui>, window: &adw::ApplicationWindow) {
|
||||
ui.browser.search.grab_focus();
|
||||
}
|
||||
gtk4::gdk::Key::n | gtk4::gdk::Key::N if ctrl && on_browser => prompt_new_folder(&ui),
|
||||
gtk4::gdk::Key::plus | gtk4::gdk::Key::equal | gtk4::gdk::Key::KP_Add
|
||||
if ctrl && on_browser && !ui.browser.files.shows_list() =>
|
||||
{
|
||||
zoom_files(&ui, 1)
|
||||
}
|
||||
gtk4::gdk::Key::minus | gtk4::gdk::Key::KP_Subtract
|
||||
if ctrl && on_browser && !ui.browser.files.shows_list() =>
|
||||
{
|
||||
zoom_files(&ui, -1)
|
||||
}
|
||||
gtk4::gdk::Key::_0 | gtk4::gdk::Key::KP_0
|
||||
if ctrl && on_browser && !ui.browser.files.shows_list() =>
|
||||
{
|
||||
zoom_files(&ui, 0)
|
||||
}
|
||||
gtk4::gdk::Key::_1 | gtk4::gdk::Key::_2 if ctrl && on_browser => set_files_view(
|
||||
&ui,
|
||||
FilesView {
|
||||
|
||||
@@ -77,6 +77,8 @@ pub(crate) struct BrowserState {
|
||||
/// zoom value, and Proton account storage usage.
|
||||
pub(crate) summary: gtk4::Label,
|
||||
pub(crate) zoom: gtk4::Scale,
|
||||
/// Saves the zoom once the slider rests, not on every step.
|
||||
pub(crate) zoom_save: RefCell<Option<glib::SourceId>>,
|
||||
}
|
||||
|
||||
/// Idle pause after the last keystroke before a search query is sent, so typing
|
||||
@@ -87,9 +89,11 @@ pub(crate) const SEARCH_DEBOUNCE: Duration = Duration::from_millis(250);
|
||||
pub(crate) const SEARCH_LIMIT: usize = 200;
|
||||
|
||||
/// Browser grid thumbnail size controlled by the bottom zoom slider. The
|
||||
/// default preserves the former fixed 72 px presentation.
|
||||
/// default preserves the former fixed 72 px presentation. The largest size is
|
||||
/// big enough to tell photos apart by their thumbnail, and thumbnails are
|
||||
/// 512 px, so they stay sharp up to it on a 2× screen.
|
||||
pub(crate) const GRID_THUMB_MIN: i32 = 48;
|
||||
pub(crate) const GRID_THUMB_MAX: i32 = 144;
|
||||
pub(crate) const GRID_THUMB_MAX: i32 = 256;
|
||||
pub(crate) const GRID_THUMB_DEFAULT: i32 = 72;
|
||||
pub(crate) const GRID_THUMB_STEP: i32 = 8;
|
||||
|
||||
@@ -801,6 +805,7 @@ pub(crate) fn wire_browser(ui: &Rc<Ui>) {
|
||||
&[],
|
||||
)));
|
||||
ui_zoom.browser.files.set_thumbnail_size(size);
|
||||
save_icon_size(&ui_zoom, size);
|
||||
});
|
||||
|
||||
files
|
||||
@@ -1306,6 +1311,11 @@ pub(crate) fn wire_browser_actions(ui: &Rc<Ui>, build_thumbnails: >k4::Button)
|
||||
|
||||
let view = ui.dirs.load_config().files_view;
|
||||
ui.browser.view.set(view);
|
||||
if let Some(size) = view.icon_size {
|
||||
ui.browser
|
||||
.zoom
|
||||
.set_value(f64::from(size.clamp(GRID_THUMB_MIN, GRID_THUMB_MAX)));
|
||||
}
|
||||
let layout = gio::SimpleAction::new_stateful(
|
||||
"view",
|
||||
Some(glib::VariantTy::STRING),
|
||||
@@ -1407,6 +1417,41 @@ pub(crate) fn wire_browser_actions(ui: &Rc<Ui>, build_thumbnails: >k4::Button)
|
||||
ui.browser.content.add_controller(drop);
|
||||
}
|
||||
|
||||
/// Remember the grid's icon size, once the slider has rested for a moment.
|
||||
fn save_icon_size(ui: &Rc<Ui>, size: i32) {
|
||||
let mut view = ui.browser.view.get();
|
||||
if view.icon_size == Some(size) {
|
||||
return;
|
||||
}
|
||||
view.icon_size = Some(size);
|
||||
ui.browser.view.set(view);
|
||||
if let Some(source) = ui.browser.zoom_save.borrow_mut().take() {
|
||||
source.remove();
|
||||
}
|
||||
let ui_save = ui.clone();
|
||||
let source = glib::timeout_add_local_once(Duration::from_millis(500), move || {
|
||||
ui_save.browser.zoom_save.borrow_mut().take();
|
||||
let mut config = ui_save.dirs.load_config();
|
||||
config.files_view.icon_size = ui_save.browser.view.get().icon_size;
|
||||
if let Err(e) = ui_save.dirs.save_config(&config) {
|
||||
tracing::warn!("cannot save the icon size: {e}");
|
||||
}
|
||||
});
|
||||
*ui.browser.zoom_save.borrow_mut() = Some(source);
|
||||
}
|
||||
|
||||
/// Step the grid's icon size by `steps` slider notches; `0` resets it.
|
||||
pub(crate) fn zoom_files(ui: &Rc<Ui>, steps: i32) {
|
||||
let zoom = &ui.browser.zoom;
|
||||
let size = if steps == 0 {
|
||||
GRID_THUMB_DEFAULT
|
||||
} else {
|
||||
(zoom.value().round() as i32 + steps * GRID_THUMB_STEP)
|
||||
.clamp(GRID_THUMB_MIN, GRID_THUMB_MAX)
|
||||
};
|
||||
zoom.set_value(f64::from(size));
|
||||
}
|
||||
|
||||
fn layout_name(view: FilesView) -> &'static str {
|
||||
if view.list { "list" } else { "grid" }
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ pub(crate) fn text_column(
|
||||
|
||||
fn resize_grid_tile(thumbnail: >k4::Overlay, label: >k4::Label, size: i32) {
|
||||
resize_file_thumbnail(thumbnail, size, grid_fallback_size(size));
|
||||
let name_width = (size / 6 + 1).clamp(8, 24);
|
||||
let name_width = (size / 6 + 1).clamp(8, 40);
|
||||
label.set_width_chars(name_width);
|
||||
label.set_max_width_chars(name_width);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ Existing folders remain device backups; older builds cannot open the upgraded da
|
||||
- The zoom level and the tile shape are remembered between runs.
|
||||
- In the photo viewer, the details panel lies over the photo when the window is
|
||||
narrow instead of squeezing it.
|
||||
- **Larger icons in My files.** The icon size slider goes up to 256 px, big enough to
|
||||
tell photos apart by their thumbnail. Ctrl+plus, Ctrl+minus and Ctrl+0 change it from
|
||||
the keyboard, and the size is remembered between runs.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
+387
-387
File diff suppressed because it is too large.
Load diff
+387
-387
File diff suppressed because it is too large.
Load diff
+387
-387
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user