Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/locales/en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,7 @@ help_confirm = "Confirm selected item"
help_confirm_plain_text = "Confirm selected item as plain text"
help_delete = "Delete selected"
help_favorite = "Toggle favorite"
help_cycle_filter = "Cycle category filter"
help_toggle_favorites_filter = "Toggle favorites filter"
help_hide = "Hide window"
help_pin = "Toggle window pin"
2 changes: 2 additions & 0 deletions assets/locales/ja.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,7 @@ help_confirm = "選択項目を確認"
help_confirm_plain_text = "選択項目をプレーンテキストとして確認"
help_delete = "選択項目を削除"
help_favorite = "お気に入りを切り替え"
help_cycle_filter = "カテゴリーフィルターを循環"
help_toggle_favorites_filter = "お気に入りフィルターを切り替え"
help_hide = "ウィンドウを隠す"
help_pin = "ウィンドウのピン留めを切り替え"
2 changes: 2 additions & 0 deletions assets/locales/zh-CN.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,7 @@ help_confirm = "确认选中项"
help_confirm_plain_text = "以纯文本确认选中项"
help_delete = "删除选中项"
help_favorite = "切换收藏"
help_cycle_filter = "循环切换分类过滤"
help_toggle_favorites_filter = "切换收藏过滤"
help_hide = "隐藏窗口"
help_pin = "切换窗口置顶"
8 changes: 6 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ use crate::{
config::{AutoStartManager, Settings},
constants::APP_NAME,
gui::board::{
Active, ConfirmSelection, ConfirmSelectionPlainText, Hide, Quit, RopyBoard, SelectLeft,
SelectNext, SelectPrev, SelectRight,
Active, ConfirmSelection, ConfirmSelectionPlainText, CycleFilterNext, CycleFilterPrev,
Hide, Quit, RopyBoard, SelectLeft, SelectNext, SelectPrev, SelectRight,
ToggleFavoritesFilter,
},
i18n::I18n,
repository::{ClipboardRecord, ClipboardRepository, GlobalRepository, backend::StorageBackend},
Expand Down Expand Up @@ -235,6 +236,9 @@ fn bind_application_keys(cx: &mut App) {
KeyBinding::new("down", SelectNext, None),
KeyBinding::new("enter", ConfirmSelection, None),
KeyBinding::new("shift-enter", ConfirmSelectionPlainText, None),
KeyBinding::new("alt-right", CycleFilterNext, None),
KeyBinding::new("alt-left", CycleFilterPrev, None),
KeyBinding::new("alt-f", ToggleFavoritesFilter, None),
]);
}

Expand Down
4 changes: 2 additions & 2 deletions src/gui/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use std::{

// Re-export utilities for external use
pub(crate) use actions::{
Active, ConfirmSelection, ConfirmSelectionPlainText, Hide, Quit, SelectLeft, SelectNext,
SelectPrev, SelectRight,
Active, ConfirmSelection, ConfirmSelectionPlainText, CycleFilterNext, CycleFilterPrev, Hide,
Quit, SelectLeft, SelectNext, SelectPrev, SelectRight, ToggleFavoritesFilter,
};
use filtering::{ClearConfirmAction, filter_and_sort_record_indices};
use gpui::{
Expand Down
42 changes: 40 additions & 2 deletions src/gui/board/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
config::LayoutMode,
gui::{
active_window,
board::{ActivePanel, RopyBoard},
board::{ActivePanel, RopyBoard, search::next_content_filter},
constants::default_window_size,
hide_window,
panel::settings,
Expand Down Expand Up @@ -40,7 +40,10 @@ mod generated_actions {
SelectNext,
ConfirmSelection,
ConfirmSelectionPlainText,
DeleteRecord
DeleteRecord,
CycleFilterNext,
CycleFilterPrev,
ToggleFavoritesFilter
]
);
}
Expand Down Expand Up @@ -225,6 +228,41 @@ impl RopyBoard {
self.confirm_record_as_plain_text(window, cx, self.selected_index);
}

pub(crate) fn on_cycle_filter_next(
&mut self,
_: &CycleFilterNext,
_window: &mut Window,
cx: &mut Context<'_, Self>,
) {
let next = next_content_filter(self.filter_state.content_filter, true);
self.filter_state.content_filter = next;
self.sync_filtered_records_and_reveal(cx);
cx.notify();
}

pub(crate) fn on_cycle_filter_prev(
&mut self,
_: &CycleFilterPrev,
_window: &mut Window,
cx: &mut Context<'_, Self>,
) {
let next = next_content_filter(self.filter_state.content_filter, false);
self.filter_state.content_filter = next;
self.sync_filtered_records_and_reveal(cx);
cx.notify();
}

pub(crate) fn on_toggle_favorites_filter(
&mut self,
_: &ToggleFavoritesFilter,
_window: &mut Window,
cx: &mut Context<'_, Self>,
) {
self.toggle_favorites_only();
self.sync_filtered_records_and_reveal(cx);
cx.notify();
}

pub(crate) fn on_delete_record(
&mut self,
_: &DeleteRecord,
Expand Down
3 changes: 3 additions & 0 deletions src/gui/board/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ impl Render for RopyBoard {
.on_action(cx.listener(Self::on_confirm_selection))
.on_action(cx.listener(Self::on_confirm_selection_plain_text))
.on_action(cx.listener(Self::on_delete_record))
.on_action(cx.listener(Self::on_cycle_filter_next))
.on_action(cx.listener(Self::on_cycle_filter_prev))
.on_action(cx.listener(Self::on_toggle_favorites_filter))
.on_key_down(cx.listener(Self::on_key_down))
.on_key_up(cx.listener(Self::on_key_up))
.child(render_header(self, cx))
Expand Down
66 changes: 66 additions & 0 deletions src/gui/board/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,32 @@ pub(crate) enum ContentFilter {
Files,
}

/// Cycle to the adjacent `ContentFilter` in display order.
///
/// Order matches the filter buttons in the search bar header: `All → Text →
/// Image → Files → All`. `forward = true` advances; `false` walks backward.
pub(crate) const fn next_content_filter(current: ContentFilter, forward: bool) -> ContentFilter {
const CYCLE: [ContentFilter; 4] = [
ContentFilter::All,
ContentFilter::Text,
ContentFilter::Image,
ContentFilter::Files,
];
let len = CYCLE.len();
let index = match current {
ContentFilter::All => 0,
ContentFilter::Text => 1,
ContentFilter::Image => 2,
ContentFilter::Files => 3,
};
let next = if forward {
(index + 1) % len
} else {
(index + len - 1) % len
};
CYCLE[next]
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) struct SearchOptions {
pub(crate) case_sensitive: bool,
Expand Down Expand Up @@ -391,6 +417,46 @@ mod tests {
HashSet::new()
}

#[test]
fn next_content_filter_cycles_forward() {
assert_eq!(
next_content_filter(ContentFilter::All, true),
ContentFilter::Text
);
assert_eq!(
next_content_filter(ContentFilter::Text, true),
ContentFilter::Image
);
assert_eq!(
next_content_filter(ContentFilter::Image, true),
ContentFilter::Files
);
assert_eq!(
next_content_filter(ContentFilter::Files, true),
ContentFilter::All
);
}

#[test]
fn next_content_filter_cycles_backward() {
assert_eq!(
next_content_filter(ContentFilter::All, false),
ContentFilter::Files
);
assert_eq!(
next_content_filter(ContentFilter::Files, false),
ContentFilter::Image
);
assert_eq!(
next_content_filter(ContentFilter::Image, false),
ContentFilter::Text
);
assert_eq!(
next_content_filter(ContentFilter::Text, false),
ContentFilter::All
);
}

/// Helper: build a mixed set of test records (2 text + 1 image)
fn mixed_records() -> Vec<ClipboardRecord> {
vec![
Expand Down
10 changes: 10 additions & 0 deletions src/gui/panel/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ const SHORTCUTS: &[ShortcutRow] = &[
label_key: "help_favorite",
grid_only: false,
},
ShortcutRow {
key: "Alt+← / Alt+→",
label_key: "help_cycle_filter",
grid_only: false,
},
ShortcutRow {
key: "Alt+F",
label_key: "help_toggle_favorites_filter",
grid_only: false,
},
ShortcutRow {
key: "P",
label_key: "help_pin",
Expand Down
Loading