Skip to content
Closed
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 migrations/db_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ CREATE TABLE clipboard_entries (
content_type TEXT NOT NULL CHECK(content_type IN ('text', 'url', 'image')),
text_content TEXT,
blob_content BLOB,
image_width INTEGER,
image_height INTEGER,
created_at INTEGER NOT NULL,
size_bytes INTEGER NOT NULL
);
Expand Down
1 change: 1 addition & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub enum Message {
UpdateApps,
SetSender(ExtSender),
SwitchToPage(Page),
ClipboardHistoryLoaded(u64, Result<Vec<ClipBoardContentType>, String>),
EditClipboardHistory(Editable<ClipBoardContentType>),
ClearClipboardHistory,
ChangeFocus(ArrowKey, u32),
Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
/// - the iced Element to render
pub fn clipboard_view(
query: String,
clipboard_content: Vec<ClipBoardContentType>,
clipboard_content: &[ClipBoardContentType],
focussed_id: u32,
theme: Theme,
) -> Element<'static, Message> {
Expand Down
1 change: 1 addition & 0 deletions src/app/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ pub struct Tile {
pub config: Config,
hotkeys: Hotkeys,
clipboard_content: Vec<ClipBoardContentType>,
clipboard_generation: u64,
tray_icon: Option<TrayIcon>,
sender: Option<ExtSender>,
page: Page,
Expand Down
10 changes: 5 additions & 5 deletions src/app/tile/elm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use crate::app::pages::settings::settings_page;
use crate::app::tile::{AppIndex, Hotkeys};
use crate::app::{DEFAULT_WINDOW_HEIGHT, SettingsTab, ToApp, ToApps};
use crate::config::Theme;
use crate::database::load_clipboard;
use crate::debounce::Debouncer;
use crate::platform::macos::events::Event;
use crate::styles::{
Expand Down Expand Up @@ -81,7 +80,7 @@ pub fn new(hotkeys: Hotkeys, config: &Config) -> (Tile, Task<Message>) {

let conn = database::initialise_database();

let clipboard_content = load_clipboard(&conn);
let load_clipboard = crate::app::tile::update::load_clipboard_task(0);

(
Tile {
Expand All @@ -101,7 +100,8 @@ pub fn new(hotkeys: Hotkeys, config: &Config) -> (Tile, Task<Message>) {
config: config.clone(),
ranking,
theme: config.theme.to_owned().clone().into(),
clipboard_content,
clipboard_content: vec![],
clipboard_generation: 0,
tray_icon: None,
sender: None,
page: Page::Main,
Expand All @@ -114,7 +114,7 @@ pub fn new(hotkeys: Hotkeys, config: &Config) -> (Tile, Task<Message>) {
previous_input_source: None,
conn,
},
Task::batch([open.map(|_| Message::OpenWindow)]),
Task::batch([open.map(|_| Message::OpenWindow), load_clipboard]),
)
}

Expand Down Expand Up @@ -150,7 +150,7 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> {
let results = match tile.page {
Page::ClipboardHistory => clipboard_view(
tile.query_lc.clone(),
tile.clipboard_content.clone(),
&tile.clipboard_content,
tile.focus_id,
tile.config.theme.clone(),
),
Expand Down
87 changes: 50 additions & 37 deletions src/app/tile/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use iced::window::Id;
use log::info;
use objc2::MainThreadMarker;
use objc2_app_kit::NSApplication;
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
use url::Url;
Expand Down Expand Up @@ -44,7 +43,10 @@ use crate::config::Config;
use crate::config::MainPage;
use crate::config::Position;
use crate::config::ThemeMode;
use crate::database::store_clipboard_content;
use crate::database::{
clear_clipboard, delete_clipboard_content, load_clipboard_in_background,
store_clipboard_content, update_clipboard_content,
};
use crate::debounce::DebouncePolicy;
use crate::platform::macos::events::Event;
use crate::platform::macos::launching::Shortcut;
Expand Down Expand Up @@ -111,6 +113,12 @@ fn message_for_open_command(command: &AppCommand) -> Message {
}
}

pub fn load_clipboard_task(generation: u64) -> Task<Message> {
Task::perform(load_clipboard_in_background(), move |result| {
Message::ClipboardHistoryLoaded(generation, result)
})
}

/// Handle the "elm" update
pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
match message {
Expand Down Expand Up @@ -652,57 +660,56 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
Task::none()
}
}
Message::ClipboardHistoryLoaded(generation, result) => {
if generation != tile.clipboard_generation {
return load_clipboard_task(tile.clipboard_generation);
}

match result {
Ok(content) => tile.clipboard_content = content,
Err(error) => log::error!("Failed to load clipboard history: {error}"),
}
Task::none()
}
Message::EditClipboardHistory(action) => {
if !tile.config.cbhist {
return Task::none();
}
match action {
Editable::Create(content) => {
if tile.clipboard_content.len() >= 300 {
// hard limit of 300 items at once
tile.clipboard_content.pop();
if let Err(error) = store_clipboard_content(&tile.conn, &content) {
log::error!("Failed to store clipboard content: {error}");
return Task::none();
}

if !tile.clipboard_content.contains(&content) {
store_clipboard_content(&tile.conn, &content);
tile.clipboard_content.insert(0, content);
return Task::none();
if let Some(index) = tile.clipboard_content.iter().position(|x| x == &content) {
tile.clipboard_content.remove(index);
}

let new_content_vec = tile
.clipboard_content
.par_iter()
.filter_map(|x| {
if *x == content {
None
} else {
Some(x.to_owned())
}
})
.collect();
if tile.clipboard_content.len() >= 300 {
// hard limit of 300 items at once
tile.clipboard_content.pop();
}

tile.clipboard_content = new_content_vec;
tile.clipboard_content.insert(0, content);
tile.clipboard_generation += 1;
}
Editable::Delete(content) => {
tile.clipboard_content = tile
.clipboard_content
.iter()
.filter_map(|x| {
if *x == content {
None
} else {
Some(x.to_owned())
}
})
.collect();
if let Err(error) = delete_clipboard_content(&tile.conn, &content) {
log::error!("Failed to delete clipboard content: {error}");
return Task::none();
}
tile.clipboard_content.retain(|x| x != &content);
tile.clipboard_generation += 1;
}
Editable::Update { old, new } => {
tile.clipboard_content = tile
.clipboard_content
.iter()
.map(|x| if x == &old { new.clone() } else { x.to_owned() })
.collect();
if let Err(error) = update_clipboard_content(&tile.conn, &old, &new) {
log::error!("Failed to update clipboard content: {error}");
return Task::none();
}
tile.clipboard_content.retain(|x| x != &old && x != &new);
tile.clipboard_content.insert(0, new);
tile.clipboard_generation += 1;
}
}
Task::none()
Expand Down Expand Up @@ -1072,7 +1079,12 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
Task::batch([Task::done(Message::ReloadConfig), Task::none()])
}
Message::ClearClipboardHistory => {
if let Err(error) = clear_clipboard(&tile.conn) {
log::error!("Failed to clear clipboard history: {error}");
return Task::none();
}
tile.clipboard_content.clear();
tile.clipboard_generation += 1;
Task::none()
}
Message::SimulatePaste(pid) => {
Expand Down Expand Up @@ -1455,6 +1467,7 @@ mod tests {
handle: None,
},
clipboard_content: Vec::new(),
clipboard_generation: 0,
tray_icon: None,
sender: None,
page: Page::Main,
Expand Down
22 changes: 20 additions & 2 deletions src/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ impl PartialEq for ClipBoardContentType {
} else if let Self::Image(image_data) = self
&& let Self::Image(other_image_data) = other
{
return image_data.bytes == other_image_data.bytes;
return image_data.width == other_image_data.width
&& image_data.height == other_image_data.height
&& image_data.bytes == other_image_data.bytes;
} else if let Self::Url(a) = self
&& let Self::Url(b) = other
{
Expand Down Expand Up @@ -112,7 +114,7 @@ mod tests {

#[test]
fn clipboard_to_app_truncates_and_uses_first_line_for_display() {
let item =
let _item =
ClipBoardContentType::Text("abcdefghijklmnopqrstuvwxyz\nsecond line".to_string());

// let app = item.to_app();
Expand All @@ -121,4 +123,20 @@ mod tests {
// assert_eq!(app.search_name, "abcdefghijklmnopqrstuvwxy");
// assert_eq!(app.desc, "Clipboard Item");
}

#[test]
fn clipboard_image_equality_includes_dimensions() {
let first = ClipBoardContentType::Image(ImageData {
width: 1,
height: 2,
bytes: vec![0; 8].into(),
});
let second = ClipBoardContentType::Image(ImageData {
width: 2,
height: 1,
bytes: vec![0; 8].into(),
});

assert_ne!(first, second);
}
}
Loading
Loading