Skip to content

Commit

Permalink
feat: implment clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
Decodetalkers committed Aug 28, 2024
1 parent ee30a86 commit 0ff3220
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 27 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ iced_core = "0.12"
iced_renderer = "0.12.1"
iced_futures = "0.12.0"
iced_graphics = "0.12.1"
window_clipboard = "0.4.1"

bitflags = "2.6.0"
log = "0.4.22"

Expand Down
2 changes: 2 additions & 0 deletions iced_layershell/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ iced_graphics.workspace = true
tracing.workspace = true
thiserror.workspace = true
layershellev.workspace = true
window_clipboard.workspace = true
log.workspace = true
futures.workspace = true
21 changes: 17 additions & 4 deletions iced_layershell/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ async fn run_instance<A, E, C>(

let mut should_exit = false;

let mut clipboard = LayerShellClipboard;
let mut clipboard = LayerShellClipboard::connect(&window);

let mut mouse_interaction = mouse::Interaction::default();
let mut messages = Vec::new();
Expand All @@ -364,6 +364,7 @@ async fn run_instance<A, E, C>(
&mut renderer,
init_command,
&mut runtime,
&mut clipboard,
&mut custom_actions,
&mut should_exit,
&mut proxy,
Expand Down Expand Up @@ -500,6 +501,7 @@ async fn run_instance<A, E, C>(
&mut state,
&mut renderer,
&mut runtime,
&mut clipboard,
&mut should_exit,
&mut proxy,
&mut debug,
Expand Down Expand Up @@ -563,6 +565,7 @@ pub(crate) fn update<A: Application, C, E: Executor>(
state: &mut State<A>,
renderer: &mut A::Renderer,
runtime: &mut Runtime<E, IcedProxy<A::Message>, A::Message>,
clipboard: &mut LayerShellClipboard,
should_exit: &mut bool,
proxy: &mut IcedProxy<A::Message>,
debug: &mut Debug,
Expand All @@ -589,6 +592,7 @@ pub(crate) fn update<A: Application, C, E: Executor>(
renderer,
command,
runtime,
clipboard,
custom_actions,
should_exit,
proxy,
Expand All @@ -612,6 +616,7 @@ pub(crate) fn run_command<A, C, E>(
renderer: &mut A::Renderer,
command: Command<A::Message>,
runtime: &mut Runtime<E, IcedProxy<A::Message>, A::Message>,
clipboard: &mut LayerShellClipboard,
custom_actions: &mut Vec<LayerShellActions<()>>,
should_exit: &mut bool,
proxy: &mut IcedProxy<A::Message>,
Expand All @@ -624,6 +629,7 @@ pub(crate) fn run_command<A, C, E>(
A::Message: 'static,
{
use iced_core::widget::operation;
use iced_runtime::clipboard;
use iced_runtime::command;
use iced_runtime::window;
use iced_runtime::window::Action as WinowAction;
Expand All @@ -636,9 +642,16 @@ pub(crate) fn run_command<A, C, E>(
command::Action::Stream(stream) => {
runtime.run(stream);
}
command::Action::Clipboard(_action) => {
// TODO:
}
command::Action::Clipboard(action) => match action {
clipboard::Action::Read(tag, kind) => {
let message = tag(clipboard.read(kind));

proxy.send(message);
}
clipboard::Action::Write(contents, kind) => {
clipboard.write(kind, contents);
}
},
command::Action::Widget(action) => {
let mut current_cache = std::mem::take(cache);
let mut current_operation = Some(action);
Expand Down
74 changes: 69 additions & 5 deletions iced_layershell/src/clipboard.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,74 @@
use iced_core::clipboard::Kind;
use iced_core::Clipboard;
pub struct LayerShellClipboard;
use layershellev::WindowWrapper;
pub struct LayerShellClipboard {
state: State,
}

enum State {
Connected(window_clipboard::Clipboard),
Unavailable,
}

impl LayerShellClipboard {
/// Creates a new [`Clipboard`] for the given window.
pub fn connect(window: &WindowWrapper) -> Self {
#[allow(unsafe_code)]
let state = unsafe { window_clipboard::Clipboard::connect(window) }
.ok()
.map(State::Connected)
.unwrap_or(State::Unavailable);

Self { state }
}

/// Creates a new [`Clipboard`] that isn't associated with a window.
/// This clipboard will never contain a copied value.
#[allow(unused)]
pub fn unconnected() -> Self {
Self {
state: State::Unavailable,
}
}

/// Reads the current content of the [`Clipboard`] as text.
pub fn read(&self, kind: Kind) -> Option<String> {
match &self.state {
State::Connected(clipboard) => match kind {
Kind::Standard => clipboard.read().ok(),
Kind::Primary => clipboard.read_primary().and_then(Result::ok),
},
State::Unavailable => None,
}
}

/// Writes the given text contents to the [`Clipboard`].
pub fn write(&mut self, kind: Kind, contents: String) {
match &mut self.state {
State::Connected(clipboard) => {
let result = match kind {
Kind::Standard => clipboard.write(contents),
Kind::Primary => clipboard.write_primary(contents).unwrap_or(Ok(())),
};

match result {
Ok(()) => {}
Err(error) => {
log::warn!("error writing to clipboard: {error}");
}
}
}
State::Unavailable => {}
}
}
}

// TODO: clipboard
impl Clipboard for LayerShellClipboard {
fn read(&self, _kind: iced_core::clipboard::Kind) -> Option<String> {
None
fn read(&self, kind: Kind) -> Option<String> {
self.read(kind)
}

fn write(&mut self, kind: Kind, contents: String) {
self.write(kind, contents);
}
fn write(&mut self, _kind: iced_core::clipboard::Kind, _contents: String) {}
}
26 changes: 21 additions & 5 deletions iced_layershell/src/multi_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use iced_futures::{Executor, Runtime, Subscription};
use layershellev::{
calloop::timer::{TimeoutAction, Timer},
reexport::zwp_virtual_keyboard_v1,
LayerEvent, NewPopUpSettings, ReturnData, WindowState,
LayerEvent, NewPopUpSettings, ReturnData, WindowState, WindowWrapper,
};

use futures::{channel::mpsc, SinkExt, StreamExt};
Expand Down Expand Up @@ -208,6 +208,7 @@ where
control_sender,
//state,
window_manager,
window,
init_command,
));

Expand Down Expand Up @@ -474,6 +475,7 @@ async fn run_instance<A, E, C>(
>,
mut control_sender: mpsc::UnboundedSender<Vec<LayerShellActions<A::WindowInfo>>>,
mut window_manager: WindowManager<A, C>,
window: Arc<WindowWrapper>,
init_command: Command<A::Message>,
) where
A: Application + 'static,
Expand All @@ -484,7 +486,7 @@ async fn run_instance<A, E, C>(
{
use iced::window;
use iced_core::Event;
let mut clipboard = LayerShellClipboard;
let mut clipboard = LayerShellClipboard::connect(&window);
let mut ui_caches: HashMap<window::Id, user_interface::Cache> = HashMap::new();

let mut user_interfaces = ManuallyDrop::new(build_user_interfaces(
Expand All @@ -505,6 +507,7 @@ async fn run_instance<A, E, C>(
&mut compositor,
init_command,
&mut runtime,
&mut clipboard,
&mut custom_actions,
&mut should_exit,
&mut proxy,
Expand Down Expand Up @@ -741,6 +744,7 @@ async fn run_instance<A, E, C>(
&mut application,
&mut compositor,
&mut runtime,
&mut clipboard,
&mut should_exit,
&mut proxy,
&mut debug,
Expand Down Expand Up @@ -882,6 +886,7 @@ pub(crate) fn update<A: Application, C, E: Executor>(
application: &mut A,
compositor: &mut C,
runtime: &mut Runtime<E, IcedProxy<A::Message>, A::Message>,
clipboard: &mut LayerShellClipboard,
should_exit: &mut bool,
proxy: &mut IcedProxy<A::Message>,
debug: &mut Debug,
Expand All @@ -907,6 +912,7 @@ pub(crate) fn update<A: Application, C, E: Executor>(
compositor,
command,
runtime,
clipboard,
custom_actions,
should_exit,
proxy,
Expand All @@ -927,6 +933,7 @@ pub(crate) fn run_command<A, C, E>(
compositor: &mut C,
command: Command<A::Message>,
runtime: &mut Runtime<E, IcedProxy<A::Message>, A::Message>,
clipboard: &mut LayerShellClipboard,
custom_actions: &mut Vec<LayerShellActions<A::WindowInfo>>,
should_exit: &mut bool,
proxy: &mut IcedProxy<A::Message>,
Expand All @@ -942,6 +949,7 @@ pub(crate) fn run_command<A, C, E>(
A::WindowInfo: Clone + 'static,
{
use iced_core::widget::operation;
use iced_runtime::clipboard;
use iced_runtime::command;
use iced_runtime::window;
use iced_runtime::window::Action as WinowAction;
Expand All @@ -954,9 +962,17 @@ pub(crate) fn run_command<A, C, E>(
command::Action::Stream(stream) => {
runtime.run(stream);
}
command::Action::Clipboard(_action) => {
// TODO:
}

command::Action::Clipboard(action) => match action {
clipboard::Action::Read(tag, kind) => {
let message = tag(clipboard.read(kind));

proxy.send(message);
}
clipboard::Action::Write(contents, kind) => {
clipboard.write(kind, contents);
}
},
command::Action::Widget(action) => {
let mut current_operation = Some(action);

Expand Down
2 changes: 2 additions & 0 deletions iced_sessionlock/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ tracing.workspace = true
thiserror.workspace = true
sessionlockev.workspace = true
futures.workspace = true
window_clipboard.workspace = true
log.workspace = true
77 changes: 71 additions & 6 deletions iced_sessionlock/src/clipboard.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,75 @@
use iced_core::clipboard::Kind;
use iced_core::Clipboard;
pub struct LayerShellClipboard;
use sessionlockev::WindowWrapper;

// TODO: clipboard
impl Clipboard for LayerShellClipboard {
fn read(&self, _kind: iced_core::clipboard::Kind) -> Option<String> {
None
pub struct SessionLockClipboard {
state: State,
}

enum State {
Connected(window_clipboard::Clipboard),
Unavailable,
}

impl SessionLockClipboard {
/// Creates a new [`Clipboard`] for the given window.
pub fn connect(window: &WindowWrapper) -> Self {
#[allow(unsafe_code)]
let state = unsafe { window_clipboard::Clipboard::connect(window) }
.ok()
.map(State::Connected)
.unwrap_or(State::Unavailable);

Self { state }
}

/// Creates a new [`Clipboard`] that isn't associated with a window.
/// This clipboard will never contain a copied value.
#[allow(unused)]
pub fn unconnected() -> Self {
Self {
state: State::Unavailable,
}
}

/// Reads the current content of the [`Clipboard`] as text.
pub fn read(&self, kind: Kind) -> Option<String> {
match &self.state {
State::Connected(clipboard) => match kind {
Kind::Standard => clipboard.read().ok(),
Kind::Primary => clipboard.read_primary().and_then(Result::ok),
},
State::Unavailable => None,
}
}

/// Writes the given text contents to the [`Clipboard`].
pub fn write(&mut self, kind: Kind, contents: String) {
match &mut self.state {
State::Connected(clipboard) => {
let result = match kind {
Kind::Standard => clipboard.write(contents),
Kind::Primary => clipboard.write_primary(contents).unwrap_or(Ok(())),
};

match result {
Ok(()) => {}
Err(error) => {
log::warn!("error writing to clipboard: {error}");
}
}
}
State::Unavailable => {}
}
}
}

impl Clipboard for SessionLockClipboard {
fn read(&self, kind: Kind) -> Option<String> {
self.read(kind)
}

fn write(&mut self, kind: Kind, contents: String) {
self.write(kind, contents);
}
fn write(&mut self, _kind: iced_core::clipboard::Kind, _contents: String) {}
}
Loading

0 comments on commit 0ff3220

Please sign in to comment.