mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(gui): open the details pane on request, not on selection
Revealing the pane on the first click of a double-click reflowed the files under the pointer, so the second click missed and the folder did not open. The pane now follows a header toggle, Alt+Enter and a Details menu item.
This commit is contained in:
1 parent
be8464f4a0
commit
f457fa68fc
4 files changed
+54
-10
No files matched your search
@@ -1338,6 +1338,9 @@ fn install_shortcuts(ui: &Rc<Ui>, window: &adw::ApplicationWindow) {
|
|||||||
}
|
}
|
||||||
gtk4::gdk::Key::u | gtk4::gdk::Key::U if ctrl && on_browser => prompt_upload(&ui),
|
gtk4::gdk::Key::u | gtk4::gdk::Key::U if ctrl && on_browser => prompt_upload(&ui),
|
||||||
gtk4::gdk::Key::Menu if on_browser => popup_keyboard_context_menu(&ui),
|
gtk4::gdk::Key::Menu if on_browser => popup_keyboard_context_menu(&ui),
|
||||||
|
gtk4::gdk::Key::Return | gtk4::gdk::Key::KP_Enter if alt && on_browser => {
|
||||||
|
toggle_details(&ui)
|
||||||
|
}
|
||||||
gtk4::gdk::Key::F10
|
gtk4::gdk::Key::F10
|
||||||
if on_browser && state.contains(gtk4::gdk::ModifierType::SHIFT_MASK) =>
|
if on_browser && state.contains(gtk4::gdk::ModifierType::SHIFT_MASK) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ pub(crate) struct BrowserWidgets {
|
|||||||
pub(crate) quota: gtk4::ProgressBar,
|
pub(crate) quota: gtk4::ProgressBar,
|
||||||
pub(crate) quota_text: gtk4::Label,
|
pub(crate) quota_text: gtk4::Label,
|
||||||
pub(crate) refresh: gtk4::Button,
|
pub(crate) refresh: gtk4::Button,
|
||||||
/// Wraps the views + the details pane; the pane slides in on selection.
|
/// Wraps the views + the details pane; the pane shows the selection while its header toggle is on.
|
||||||
pub(crate) split: adw::OverlaySplitView,
|
pub(crate) split: adw::OverlaySplitView,
|
||||||
pub(crate) details: DetailsWidgets,
|
pub(crate) details: DetailsWidgets,
|
||||||
/// The selection shared by both views, so a selection change can drive the
|
/// The selection shared by both views, so a selection change can drive the
|
||||||
@@ -362,8 +362,9 @@ pub(crate) fn build_browser_page() -> (gtk4::Widget, BrowserWidgets) {
|
|||||||
content.add_named(&view_stack, Some("views"));
|
content.add_named(&view_stack, Some("views"));
|
||||||
content.add_named(&status, Some("status"));
|
content.add_named(&status, Some("status"));
|
||||||
|
|
||||||
// The details pane slides in from the right when an entry is selected.
|
// The details pane slides in from the right while its header toggle is on.
|
||||||
let (details_pane, details) = build_details_pane();
|
let (details_pane, details) = build_details_pane();
|
||||||
|
let details_toggle = details.toggle.clone();
|
||||||
let split = adw::OverlaySplitView::builder()
|
let split = adw::OverlaySplitView::builder()
|
||||||
.sidebar_position(gtk4::PackType::End)
|
.sidebar_position(gtk4::PackType::End)
|
||||||
.collapsed(true)
|
.collapsed(true)
|
||||||
@@ -507,6 +508,7 @@ pub(crate) fn build_browser_page() -> (gtk4::Widget, BrowserWidgets) {
|
|||||||
header.pack_start(&new_button);
|
header.pack_start(&new_button);
|
||||||
header.pack_end(&folder_button);
|
header.pack_end(&folder_button);
|
||||||
header.pack_end(&refresh);
|
header.pack_end(&refresh);
|
||||||
|
header.pack_end(&details_toggle);
|
||||||
header.pack_end(&view_button);
|
header.pack_end(&view_button);
|
||||||
header.pack_end(&search);
|
header.pack_end(&search);
|
||||||
header.pack_end(&search_scope);
|
header.pack_end(&search_scope);
|
||||||
@@ -1178,6 +1180,8 @@ pub(crate) fn entry_context_menu(ui: &Rc<Ui>, entry: &DirEntry) -> ActionMenu {
|
|||||||
let (ui_c, entry_c) = (ui.clone(), entry.clone());
|
let (ui_c, entry_c) = (ui.clone(), entry.clone());
|
||||||
menu.item("Versions…", move || open_versions_dialog(&ui_c, &entry_c));
|
menu.item("Versions…", move || open_versions_dialog(&ui_c, &entry_c));
|
||||||
}
|
}
|
||||||
|
let (ui_c, entry_c) = (ui.clone(), entry.clone());
|
||||||
|
menu.item("Details", move || open_details(&ui_c, &entry_c));
|
||||||
menu.section();
|
menu.section();
|
||||||
|
|
||||||
let (ui_c, entry_c) = (ui.clone(), entry.clone());
|
let (ui_c, entry_c) = (ui.clone(), entry.clone());
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ pub(crate) struct DetailsWidgets {
|
|||||||
pub(crate) versions_button: gtk4::Button,
|
pub(crate) versions_button: gtk4::Button,
|
||||||
pub(crate) trash_button: gtk4::Button,
|
pub(crate) trash_button: gtk4::Button,
|
||||||
pub(crate) close_button: gtk4::Button,
|
pub(crate) close_button: gtk4::Button,
|
||||||
|
/// The header toggle that asks for the pane. Selecting an entry only
|
||||||
|
/// fills the pane; it is shown while this is on, so a click never moves
|
||||||
|
/// the files under the pointer halfway through a double-click.
|
||||||
|
pub(crate) toggle: gtk4::ToggleButton,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The details pane shown beside the file views: a big type icon over the entry's
|
/// The details pane shown beside the file views: a big type icon over the entry's
|
||||||
@@ -42,6 +46,11 @@ pub(crate) struct DetailsWidgets {
|
|||||||
/// actions. Built empty; [`show_details`] fills it from the selected
|
/// actions. Built empty; [`show_details`] fills it from the selected
|
||||||
/// [`DirEntry`] and [`wire_details`] connects the buttons.
|
/// [`DirEntry`] and [`wire_details`] connects the buttons.
|
||||||
pub(crate) fn build_details_pane() -> (gtk4::Widget, DetailsWidgets) {
|
pub(crate) fn build_details_pane() -> (gtk4::Widget, DetailsWidgets) {
|
||||||
|
let toggle = gtk4::ToggleButton::builder()
|
||||||
|
.icon_name("sidebar-show-right-symbolic")
|
||||||
|
.tooltip_text("Details (Alt+Enter)")
|
||||||
|
.build();
|
||||||
|
|
||||||
let close_button = gtk4::Button::builder()
|
let close_button = gtk4::Button::builder()
|
||||||
.icon_name("window-close-symbolic")
|
.icon_name("window-close-symbolic")
|
||||||
.tooltip_text("Close details")
|
.tooltip_text("Close details")
|
||||||
@@ -186,6 +195,7 @@ pub(crate) fn build_details_pane() -> (gtk4::Widget, DetailsWidgets) {
|
|||||||
versions_button,
|
versions_button,
|
||||||
trash_button,
|
trash_button,
|
||||||
close_button,
|
close_button,
|
||||||
|
toggle,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -212,8 +222,13 @@ pub(crate) fn wire_details(ui: &Rc<Ui>) {
|
|||||||
|
|
||||||
let ui_close = ui.clone();
|
let ui_close = ui.clone();
|
||||||
ui.details.details.close_button.connect_clicked(move |_| {
|
ui.details.details.close_button.connect_clicked(move |_| {
|
||||||
clear_selection(&ui_close);
|
ui_close.details.details.toggle.set_active(false);
|
||||||
hide_details(&ui_close);
|
});
|
||||||
|
|
||||||
|
let ui_toggle = ui.clone();
|
||||||
|
ui.details.details.toggle.connect_toggled(move |toggle| {
|
||||||
|
let show = toggle.is_active() && ui_toggle.details.details_entry.borrow().is_some();
|
||||||
|
ui_toggle.browser.split.set_show_sidebar(show);
|
||||||
});
|
});
|
||||||
|
|
||||||
let ui_open = ui.clone();
|
let ui_open = ui.clone();
|
||||||
@@ -333,12 +348,17 @@ pub(crate) fn show_details(ui: &Rc<Ui>, entry: &DirEntry) {
|
|||||||
if mounted {
|
if mounted {
|
||||||
load_details_extras(ui, entry);
|
load_details_extras(ui, entry);
|
||||||
}
|
}
|
||||||
// Reveal the pane on idle, not inline. This runs from `selection_changed`,
|
// Only a pane the user asked for is revealed. This runs from
|
||||||
// which fires on the *first* press of a double-click; mutating the widget
|
// `selection_changed`, which fires on the *first* press of a double-click:
|
||||||
// tree here cancels GtkGridView's multi-press tracking, so the second press
|
// sliding the pane in there reflows the files, so the second press lands on
|
||||||
// restarts the count and `activate` never fires — the folder needs two
|
// a different spot (or item) and the folder never opens. With the pane
|
||||||
// double-clicks. Deferring lets the click gesture finish first. Guard on the
|
// already up, a new selection only repaints it. Deferred to idle all the
|
||||||
// entry still being present so a navigation that clears it wins the race.
|
// same, since mutating the widget tree mid-gesture cancels GtkGridView's
|
||||||
|
// multi-press tracking; the guard lets a navigation that clears the entry
|
||||||
|
// win the race.
|
||||||
|
if !ui.details.details.toggle.is_active() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let ui = ui.clone();
|
let ui = ui.clone();
|
||||||
glib::idle_add_local_once(move || {
|
glib::idle_add_local_once(move || {
|
||||||
if ui.details.details_entry.borrow().is_some() {
|
if ui.details.details_entry.borrow().is_some() {
|
||||||
@@ -431,6 +451,18 @@ pub(crate) fn versions_summary(items: &[RevisionInfo]) -> String {
|
|||||||
|
|
||||||
/// Hide the details pane and forget the entry it was showing, so a stale entry
|
/// Hide the details pane and forget the entry it was showing, so a stale entry
|
||||||
/// can't be acted on after the listing moves on.
|
/// can't be acted on after the listing moves on.
|
||||||
|
/// Show the pane for `entry`, turning the header toggle on.
|
||||||
|
pub(crate) fn open_details(ui: &Rc<Ui>, entry: &DirEntry) {
|
||||||
|
ui.details.details.toggle.set_active(true);
|
||||||
|
show_details(ui, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alt+Enter: show or hide the pane for the selected entry.
|
||||||
|
pub(crate) fn toggle_details(ui: &Rc<Ui>) {
|
||||||
|
let toggle = &ui.details.details.toggle;
|
||||||
|
toggle.set_active(!toggle.is_active());
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn hide_details(ui: &Rc<Ui>) {
|
pub(crate) fn hide_details(ui: &Rc<Ui>) {
|
||||||
ui.browser.split.set_show_sidebar(false);
|
ui.browser.split.set_show_sidebar(false);
|
||||||
*ui.details.details_entry.borrow_mut() = None;
|
*ui.details.details_entry.borrow_mut() = None;
|
||||||
|
|||||||
@@ -111,6 +111,11 @@ Schema: **31** (unchanged).
|
|||||||
Schema: **31** (unchanged).
|
Schema: **31** (unchanged).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- **Double-click opens folders reliably.** Selecting a file no longer slides the details pane in
|
||||||
|
by itself: the pane moved the files under the pointer between the two clicks of a double-click,
|
||||||
|
so the folder did not open. The pane now shows on request, from the header's Details toggle,
|
||||||
|
Alt+Enter or the item menu's Details, and then follows the selection. Closing it keeps the
|
||||||
|
selection.
|
||||||
- **An upload parked behind a temp-file rename no longer stays parked forever.** A create for a
|
- **An upload parked behind a temp-file rename no longer stays parked forever.** A create for a
|
||||||
name that looks like a writer's temporary file waits for the rename to the final name. If that
|
name that looks like a writer's temporary file waits for the rename to the final name. If that
|
||||||
rename never came — the writer crashed, the file was deleted, or the name was never temporary —
|
rename never came — the writer crashed, the file was deleted, or the name was never temporary —
|
||||||
|
|||||||
Reference in new issue
Block a user