Compare commits
1 commit
master
...
testing-li
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4af432ede |
152 changed files with 3281 additions and 7064 deletions
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
|
|
@ -33,17 +33,16 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
test_args:
|
features:
|
||||||
- --no-default-features --features "" # for cosmic-comp, don't remove!
|
- "" # for cosmic-comp, don't remove!
|
||||||
- --no-default-features --features "winit_debug"
|
- 'winit_debug'
|
||||||
- --no-default-features --features "winit_tokio"
|
- 'winit_tokio'
|
||||||
- --no-default-features --features "winit"
|
- winit
|
||||||
- --no-default-features --features "winit_wgpu"
|
- winit_wgpu
|
||||||
- --no-default-features --features "wayland"
|
- wayland
|
||||||
- --no-default-features --features "applet"
|
- applet
|
||||||
- --no-default-features --features "desktop,smol"
|
- desktop,smol
|
||||||
- --no-default-features --features "desktop,tokio"
|
- desktop,tokio
|
||||||
- -p cosmic-theme
|
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
|
|
@ -67,7 +66,7 @@ jobs:
|
||||||
- name: Rust toolchain
|
- name: Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Test features
|
- name: Test features
|
||||||
run: cargo test ${{ matrix.test_args }} -- --test-threads=1
|
run: cargo test --no-default-features --features "${{ matrix.features }}"
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: full
|
RUST_BACKTRACE: full
|
||||||
|
|
||||||
|
|
@ -104,7 +103,7 @@ jobs:
|
||||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||||
- name: Rust toolchain
|
- name: Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Check example
|
- name: Test example
|
||||||
run: cargo check -p "${{ matrix.examples }}"
|
run: cargo check -p "${{ matrix.examples }}"
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: full
|
RUST_BACKTRACE: full
|
||||||
|
|
|
||||||
15
.github/workflows/pages.yml
vendored
15
.github/workflows/pages.yml
vendored
|
|
@ -7,6 +7,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pages:
|
pages:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -14,20 +15,8 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install Rust nightly
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: nightly-2025-07-31
|
|
||||||
- name: System dependencies
|
|
||||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: |
|
run: cargo doc --verbose --features tokio,winit
|
||||||
RUSTDOCFLAGS="--cfg docsrs" \
|
|
||||||
cargo +nightly-2025-07-31 doc --no-deps \
|
|
||||||
-p cosmic-client-toolkit \
|
|
||||||
-p cosmic-protocols \
|
|
||||||
-p libcosmic \
|
|
||||||
--verbose --features tokio,winit,wayland,desktop,single-instance,applet,xdg-portal,multi-window
|
|
||||||
- name: Deploy documentation
|
- name: Deploy documentation
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
61
Cargo.toml
61
Cargo.toml
|
|
@ -8,29 +8,13 @@ rust-version = "1.90"
|
||||||
name = "cosmic"
|
name = "cosmic"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [
|
default = ["dbus-config", "multi-window", "a11y"]
|
||||||
"winit",
|
|
||||||
"tokio",
|
|
||||||
"a11y",
|
|
||||||
"dbus-config",
|
|
||||||
"x11",
|
|
||||||
"iced-wayland",
|
|
||||||
"multi-window",
|
|
||||||
]
|
|
||||||
advanced-shaping = ["iced/advanced-shaping"]
|
|
||||||
# Accessibility support
|
# Accessibility support
|
||||||
a11y = ["iced/a11y", "iced_accessibility"]
|
a11y = ["iced/a11y", "iced_accessibility"]
|
||||||
# Enable about widget
|
# Enable about widget
|
||||||
about = []
|
about = []
|
||||||
# Builds support for animated images
|
# Builds support for animated images
|
||||||
animated-image = [
|
animated-image = ["dep:async-fs", "image/gif", "tokio?/io-util", "tokio?/fs"]
|
||||||
"dep:async-fs",
|
|
||||||
"image/gif",
|
|
||||||
"image/webp",
|
|
||||||
"image/png",
|
|
||||||
"tokio?/io-util",
|
|
||||||
"tokio?/fs",
|
|
||||||
]
|
|
||||||
# XXX autosize should not be used on winit windows unless dialogs
|
# XXX autosize should not be used on winit windows unless dialogs
|
||||||
autosize = []
|
autosize = []
|
||||||
applet = [
|
applet = [
|
||||||
|
|
@ -81,24 +65,18 @@ tokio = [
|
||||||
]
|
]
|
||||||
# Tokio async runtime
|
# Tokio async runtime
|
||||||
# Wayland window support
|
# Wayland window support
|
||||||
iced-wayland = [
|
wayland = [
|
||||||
"ashpd?/wayland",
|
"ashpd?/wayland",
|
||||||
"autosize",
|
"autosize",
|
||||||
|
"iced_runtime/wayland",
|
||||||
"iced/wayland",
|
"iced/wayland",
|
||||||
"iced_winit/wayland",
|
"iced_winit/wayland",
|
||||||
|
"cctk",
|
||||||
"surface-message",
|
"surface-message",
|
||||||
]
|
]
|
||||||
wayland = [
|
|
||||||
"iced-wayland",
|
|
||||||
"iced_runtime/cctk",
|
|
||||||
"iced_winit/cctk",
|
|
||||||
"iced_wgpu/cctk",
|
|
||||||
"iced/cctk",
|
|
||||||
"dep:cctk",
|
|
||||||
]
|
|
||||||
surface-message = []
|
surface-message = []
|
||||||
# multi-window support
|
# multi-window support
|
||||||
multi-window = []
|
multi-window = ["iced/multi-window"]
|
||||||
# Render with wgpu
|
# Render with wgpu
|
||||||
wgpu = ["iced/wgpu", "iced_wgpu"]
|
wgpu = ["iced/wgpu", "iced_wgpu"]
|
||||||
# X11 window support via winit
|
# X11 window support via winit
|
||||||
|
|
@ -118,15 +96,14 @@ async-std = [
|
||||||
"zbus?/async-io",
|
"zbus?/async-io",
|
||||||
"iced/async-std",
|
"iced/async-std",
|
||||||
]
|
]
|
||||||
x11 = ["iced/x11", "iced_winit/x11"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
apply = "0.3.0"
|
apply = "0.3.0"
|
||||||
ashpd = { version = "0.12.3", default-features = false, optional = true }
|
ashpd = { version = "0.12.1", default-features = false, optional = true }
|
||||||
async-fs = { version = "2.2", optional = true }
|
async-fs = { version = "2.2", optional = true }
|
||||||
async-std = { version = "1.13", optional = true }
|
async-std = { version = "1.13", optional = true }
|
||||||
auto_enums = "0.8.8"
|
auto_enums = "0.8.7"
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086", optional = true }
|
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "d0e95be", optional = true }
|
||||||
jiff = "0.2"
|
jiff = "0.2"
|
||||||
cosmic-config = { path = "cosmic-config" }
|
cosmic-config = { path = "cosmic-config" }
|
||||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
||||||
|
|
@ -138,16 +115,17 @@ i18n-embed = { version = "0.16.0", features = [
|
||||||
i18n-embed-fl = "0.10"
|
i18n-embed-fl = "0.10"
|
||||||
rust-embed = "8.11.0"
|
rust-embed = "8.11.0"
|
||||||
css-color = "0.2.8"
|
css-color = "0.2.8"
|
||||||
derive_setters = "0.1.9"
|
derive_setters = "0.1.8"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
image = { version = "0.25.10", default-features = false, features = [
|
image = { version = "0.25.9", default-features = false, features = [
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"png",
|
"png",
|
||||||
] }
|
] }
|
||||||
libc = { version = "0.2.183", optional = true }
|
libc = { version = "0.2.180", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = { version = "0.3.17", optional = true }
|
mime = { version = "0.3.17", optional = true }
|
||||||
palette = "0.7.6"
|
palette = "0.7.6"
|
||||||
|
raw-window-handle = "0.6"
|
||||||
rfd = { version = "0.16.0", default-features = false, features = [
|
rfd = { version = "0.16.0", default-features = false, features = [
|
||||||
"xdg-portal",
|
"xdg-portal",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
|
|
@ -157,25 +135,24 @@ slotmap = "1.1.1"
|
||||||
smol = { version = "2.0.2", optional = true }
|
smol = { version = "2.0.2", optional = true }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
taffy = { version = "0.9.2", features = ["grid"] }
|
taffy = { version = "0.9.2", features = ["grid"] }
|
||||||
tokio = { version = "1.50.0", optional = true }
|
tokio = { version = "1.49.0", optional = true }
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
unicode-segmentation = "1.12"
|
unicode-segmentation = "1.12"
|
||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
zbus = { version = "5.14.0", default-features = false, optional = true }
|
zbus = { version = "5.13.2", default-features = false, optional = true }
|
||||||
float-cmp = "0.10.0"
|
|
||||||
|
|
||||||
# Enable DBus feature on Linux targets
|
# Enable DBus feature on Linux targets
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
||||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||||
zbus = { version = "5.14.0", default-features = false }
|
zbus = { version = "5.13.2", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
freedesktop-icons = { package = "cosmic-freedesktop-icons", git = "https://github.com/pop-os/freedesktop-icons" }
|
freedesktop-icons = { package = "cosmic-freedesktop-icons", git = "https://github.com/pop-os/freedesktop-icons" }
|
||||||
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
||||||
shlex = { version = "1.3.0", optional = true }
|
shlex = { version = "1.3.0", optional = true }
|
||||||
|
|
||||||
[target.'cfg(any(not(unix), target_os = "macos"))'.dependencies]
|
[target.'cfg(not(unix))'.dependencies]
|
||||||
# Used to embed bundled icons for non-unix platforms.
|
# Used to embed bundled icons for non-unix platforms.
|
||||||
phf = { version = "0.13.1", features = ["macros"] }
|
phf = { version = "0.13.1", features = ["macros"] }
|
||||||
|
|
||||||
|
|
@ -248,4 +225,4 @@ exclude = ["iced"]
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.27.0"
|
tempfile = "3.24.0"
|
||||||
|
|
|
||||||
4
build.rs
4
build.rs
|
|
@ -3,9 +3,7 @@ use std::env;
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo::rerun-if-changed=build.rs");
|
println!("cargo::rerun-if-changed=build.rs");
|
||||||
|
|
||||||
if env::var_os("CARGO_CFG_UNIX").is_none()
|
if env::var_os("CARGO_CFG_UNIX").is_none() {
|
||||||
|| env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos")
|
|
||||||
{
|
|
||||||
generate_bundled_icons();
|
generate_bundled_icons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ subscription = ["iced_futures"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
||||||
zbus = { version = "5.14.0", default-features = false, optional = true }
|
zbus = { version = "5.13.2", default-features = false, optional = true }
|
||||||
atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" }
|
atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" }
|
||||||
calloop = { version = "0.14.4", optional = true }
|
calloop = { version = "0.14.3", optional = true }
|
||||||
notify = "8.2.0"
|
notify = "8.2.0"
|
||||||
ron = "0.12.0"
|
ron = "0.12.0"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
|
|
@ -22,7 +22,7 @@ iced = { path = "../iced/", default-features = false, optional = true }
|
||||||
iced_futures = { path = "../iced/futures/", default-features = false, optional = true }
|
iced_futures = { path = "../iced/futures/", default-features = false, optional = true }
|
||||||
futures-util = { version = "0.3", optional = true }
|
futures-util = { version = "0.3", optional = true }
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
tokio = { version = "1.50", optional = true, features = ["time"] }
|
tokio = { version = "1.49", optional = true, features = ["time"] }
|
||||||
async-std = { version = "1.13", optional = true }
|
async-std = { version = "1.13", optional = true }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
||||||
|
|
@ -30,4 +30,4 @@ tracing = "0.1"
|
||||||
xdg = "3.0"
|
xdg = "3.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
known-folders = "1.4.2"
|
known-folders = "1.4.0"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::{any::TypeId, ops::Deref};
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::{CosmicConfigEntry, Update};
|
use crate::{CosmicConfigEntry, Update};
|
||||||
use cosmic_settings_daemon::{Changed, ConfigProxy, CosmicSettingsDaemonProxy};
|
use cosmic_settings_daemon::{Changed, ConfigProxy, CosmicSettingsDaemonProxy};
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use iced_futures::{
|
use iced_futures::{
|
||||||
Subscription,
|
Subscription,
|
||||||
futures::{self, StreamExt, future::pending},
|
futures::{self, Stream, StreamExt, future::pending},
|
||||||
stream,
|
stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -57,20 +57,6 @@ impl Watcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Wrapper(
|
|
||||||
TypeId,
|
|
||||||
CosmicSettingsDaemonProxy<'static>,
|
|
||||||
&'static str,
|
|
||||||
bool,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl std::hash::Hash for Wrapper {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.0.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>(
|
pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>(
|
||||||
settings_daemon: CosmicSettingsDaemonProxy<'static>,
|
settings_daemon: CosmicSettingsDaemonProxy<'static>,
|
||||||
|
|
@ -78,19 +64,22 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
|
||||||
is_state: bool,
|
is_state: bool,
|
||||||
) -> iced_futures::Subscription<Update<T>> {
|
) -> iced_futures::Subscription<Update<T>> {
|
||||||
let id = std::any::TypeId::of::<T>();
|
let id = std::any::TypeId::of::<T>();
|
||||||
Subscription::run_with(
|
Subscription::run_with_id(
|
||||||
Wrapper(id, settings_daemon, config_id, is_state),
|
(id, config_id),
|
||||||
|&Wrapper(_, ref settings_daemon, ref config_id, ref is_state)| {
|
watcher_stream(settings_daemon, config_id, is_state),
|
||||||
let is_state = *is_state;
|
)
|
||||||
let config_id = *config_id;
|
}
|
||||||
let settings_daemon = settings_daemon.clone();
|
|
||||||
|
fn watcher_stream<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>(
|
||||||
|
settings_daemon: CosmicSettingsDaemonProxy<'static>,
|
||||||
|
config_id: &'static str,
|
||||||
|
is_state: bool,
|
||||||
|
) -> impl Stream<Item = Update<T>> {
|
||||||
enum Change {
|
enum Change {
|
||||||
Changes(Changed),
|
Changes(Changed),
|
||||||
OwnerChanged(bool),
|
OwnerChanged(bool),
|
||||||
}
|
}
|
||||||
stream::channel(
|
stream::channel(5, move |mut tx| async move {
|
||||||
5,
|
|
||||||
move |mut tx: futures::channel::mpsc::Sender<Update<T>>| async move {
|
|
||||||
let version = T::VERSION;
|
let version = T::VERSION;
|
||||||
|
|
||||||
let Ok(cosmic_config) = (if is_state {
|
let Ok(cosmic_config) = (if is_state {
|
||||||
|
|
@ -114,15 +103,9 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
|
||||||
tracing::error!("Failed to create watcher for {config_id}");
|
tracing::error!("Failed to create watcher for {config_id}");
|
||||||
|
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
::tokio::time::sleep(::tokio::time::Duration::from_secs(
|
::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await;
|
||||||
2_u64.pow(attempts),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
#[cfg(feature = "async-std")]
|
#[cfg(feature = "async-std")]
|
||||||
async_std::task::sleep(std::time::Duration::from_secs(
|
async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await;
|
||||||
2_u64.pow(attempts),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
||||||
{
|
{
|
||||||
pending::<()>().await;
|
pending::<()>().await;
|
||||||
|
|
@ -136,15 +119,9 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
|
||||||
tracing::error!("Failed to listen for changes for {config_id}");
|
tracing::error!("Failed to listen for changes for {config_id}");
|
||||||
|
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
::tokio::time::sleep(::tokio::time::Duration::from_secs(
|
::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await;
|
||||||
2_u64.pow(attempts),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
#[cfg(feature = "async-std")]
|
#[cfg(feature = "async-std")]
|
||||||
async_std::task::sleep(std::time::Duration::from_secs(
|
async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await;
|
||||||
2_u64.pow(attempts),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
||||||
{
|
{
|
||||||
pending::<()>().await;
|
pending::<()>().await;
|
||||||
|
|
@ -157,19 +134,12 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
|
||||||
|
|
||||||
let mut changes = changes.map(Change::Changes).fuse();
|
let mut changes = changes.map(Change::Changes).fuse();
|
||||||
|
|
||||||
let Ok(owner_changed) = watcher.inner().receive_owner_changed().await
|
let Ok(owner_changed) = watcher.inner().receive_owner_changed().await else {
|
||||||
else {
|
|
||||||
tracing::error!("Failed to listen for owner changes for {config_id}");
|
tracing::error!("Failed to listen for owner changes for {config_id}");
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
::tokio::time::sleep(::tokio::time::Duration::from_secs(
|
::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await;
|
||||||
2_u64.pow(attempts),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
#[cfg(feature = "async-std")]
|
#[cfg(feature = "async-std")]
|
||||||
async_std::task::sleep(std::time::Duration::from_secs(
|
async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await;
|
||||||
2_u64.pow(attempts),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
||||||
{
|
{
|
||||||
pending::<()>().await;
|
pending::<()>().await;
|
||||||
|
|
@ -255,8 +225,5 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,24 +25,7 @@ pub fn config_subscription<
|
||||||
config_id: Cow<'static, str>,
|
config_id: Cow<'static, str>,
|
||||||
config_version: u64,
|
config_version: u64,
|
||||||
) -> iced_futures::Subscription<crate::Update<T>> {
|
) -> iced_futures::Subscription<crate::Update<T>> {
|
||||||
iced_futures::Subscription::run_with(
|
iced_futures::Subscription::run_with_id(id, watcher_stream(config_id, config_version, false))
|
||||||
(id, config_id, config_version, false),
|
|
||||||
// FIXME there are type issues related to the 'static lifetime of the Cow if this is extracted to a named function...
|
|
||||||
|(_, config_id, config_version, is_state)| {
|
|
||||||
let config_id = config_id.clone();
|
|
||||||
let config_version = *config_version;
|
|
||||||
let is_state = *is_state;
|
|
||||||
|
|
||||||
stream::channel(100, move |mut output| async move {
|
|
||||||
let config_id = config_id.clone();
|
|
||||||
let mut state = ConfigState::Init(config_id, config_version, is_state);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
state = start_listening::<T>(state, &mut output).await;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
|
|
@ -54,23 +37,25 @@ pub fn config_state_subscription<
|
||||||
config_id: Cow<'static, str>,
|
config_id: Cow<'static, str>,
|
||||||
config_version: u64,
|
config_version: u64,
|
||||||
) -> iced_futures::Subscription<crate::Update<T>> {
|
) -> iced_futures::Subscription<crate::Update<T>> {
|
||||||
iced_futures::Subscription::run_with(
|
iced_futures::Subscription::run_with_id(id, watcher_stream(config_id, config_version, true))
|
||||||
(id, config_id, config_version, true),
|
}
|
||||||
|(_, config_id, config_version, is_state)| {
|
|
||||||
let config_id = config_id.clone();
|
|
||||||
let config_version = *config_version;
|
|
||||||
let is_state = *is_state;
|
|
||||||
|
|
||||||
stream::channel(100, move |mut output| async move {
|
fn watcher_stream<T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry>(
|
||||||
|
config_id: Cow<'static, str>,
|
||||||
|
config_version: u64,
|
||||||
|
is_state: bool,
|
||||||
|
) -> impl Stream<Item = crate::Update<T>> {
|
||||||
|
stream::channel(100, move |mut output| {
|
||||||
|
let config_id = config_id.clone();
|
||||||
|
async move {
|
||||||
let config_id = config_id.clone();
|
let config_id = config_id.clone();
|
||||||
let mut state = ConfigState::Init(config_id, config_version, is_state);
|
let mut state = ConfigState::Init(config_id, config_version, is_state);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
state = start_listening::<T>(state, &mut output).await;
|
state = start_listening::<T>(state, &mut output).await;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_listening<T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry>(
|
async fn start_listening<T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry>(
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5252095787cc96e2aed64604158f94e450703455
|
Subproject commit 70b07582e24ec2114672256b9657ca80670bca8a
|
||||||
|
|
@ -22,7 +22,7 @@ serde_json = { version = "1.0.149", optional = true, features = [
|
||||||
"preserve_order",
|
"preserve_order",
|
||||||
] }
|
] }
|
||||||
ron = "0.12.0"
|
ron = "0.12.0"
|
||||||
csscolorparser = { version = "0.8.3", features = ["serde"] }
|
csscolorparser = { version = "0.8.1", features = ["serde"] }
|
||||||
cosmic-config = { path = "../cosmic-config/", default-features = false, features = [
|
cosmic-config = { path = "../cosmic-config/", default-features = false, features = [
|
||||||
"subscription",
|
"subscription",
|
||||||
"macro",
|
"macro",
|
||||||
|
|
@ -30,10 +30,3 @@ cosmic-config = { path = "../cosmic-config/", default-features = false, features
|
||||||
configparser = "3.1.0"
|
configparser = "3.1.0"
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
insta = "1.47.2"
|
|
||||||
|
|
||||||
[profile.dev.package]
|
|
||||||
insta.opt-level = 3
|
|
||||||
similar.opt-level = 3
|
|
||||||
|
|
|
||||||
|
|
@ -986,19 +986,19 @@ impl ThemeBuilder {
|
||||||
let success = if let Some(success) = success {
|
let success = if let Some(success) = success {
|
||||||
success.into_color()
|
success.into_color()
|
||||||
} else {
|
} else {
|
||||||
palette.as_ref().bright_green
|
palette.as_ref().accent_green
|
||||||
};
|
};
|
||||||
|
|
||||||
let warning = if let Some(warning) = warning {
|
let warning = if let Some(warning) = warning {
|
||||||
warning.into_color()
|
warning.into_color()
|
||||||
} else {
|
} else {
|
||||||
palette.as_ref().bright_orange
|
palette.as_ref().accent_yellow
|
||||||
};
|
};
|
||||||
|
|
||||||
let destructive = if let Some(destructive) = destructive {
|
let destructive = if let Some(destructive) = destructive {
|
||||||
destructive.into_color()
|
destructive.into_color()
|
||||||
} else {
|
} else {
|
||||||
palette.as_ref().bright_red
|
palette.as_ref().accent_red
|
||||||
};
|
};
|
||||||
|
|
||||||
let text_steps_array = text_tint.map(|c| steps(c, NonZeroUsize::new(100).unwrap()));
|
let text_steps_array = text_tint.map(|c| steps(c, NonZeroUsize::new(100).unwrap()));
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,8 @@ impl Theme {
|
||||||
pub fn write_exports(&self) -> Result<(), OutputError> {
|
pub fn write_exports(&self) -> Result<(), OutputError> {
|
||||||
let gtk_res = self.write_gtk4();
|
let gtk_res = self.write_gtk4();
|
||||||
let qt_res = self.write_qt();
|
let qt_res = self.write_qt();
|
||||||
let qt56ct_res = self.write_qt56ct();
|
|
||||||
gtk_res?;
|
gtk_res?;
|
||||||
qt_res?;
|
qt_res?;
|
||||||
qt56ct_res?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,10 +56,10 @@ impl Theme {
|
||||||
pub fn reset_exports() -> Result<(), OutputError> {
|
pub fn reset_exports() -> Result<(), OutputError> {
|
||||||
let gtk_res = Theme::reset_gtk();
|
let gtk_res = Theme::reset_gtk();
|
||||||
let qt_res = Theme::reset_qt();
|
let qt_res = Theme::reset_qt();
|
||||||
let qt56ct_res = Theme::reset_qt56ct();
|
let vs_res = Theme::reset_vs_code();
|
||||||
gtk_res?;
|
gtk_res?;
|
||||||
qt_res?;
|
qt_res?;
|
||||||
qt56ct_res?;
|
vs_res?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
use crate::Theme;
|
use crate::Theme;
|
||||||
use configparser::ini::Ini;
|
use configparser::ini::Ini;
|
||||||
use palette::{Mix, Srgba, WithAlpha, blend::Compose, rgb::Rgba};
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::Write,
|
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{OutputError, qt_settings_ini_style};
|
use super::{OutputError, qt_settings_ini_style};
|
||||||
|
|
@ -18,117 +15,7 @@ impl Theme {
|
||||||
/// Increment this value when changes to qt{5,6}ct.conf are needed.
|
/// Increment this value when changes to qt{5,6}ct.conf are needed.
|
||||||
/// If the config's version is outdated, we update several sections.
|
/// If the config's version is outdated, we update several sections.
|
||||||
/// Otherwise, only the light/dark mode is updated.
|
/// Otherwise, only the light/dark mode is updated.
|
||||||
const COSMIC_QT_VERSION: u64 = 2;
|
const COSMIC_QT_VERSION: u64 = 1;
|
||||||
|
|
||||||
/// Produces a QPalette ini file for qt5ct and qt6ct.
|
|
||||||
///
|
|
||||||
/// Example file: https://github.com/trialuser02/qt6ct/blob/master/colors/airy.conf
|
|
||||||
#[must_use]
|
|
||||||
#[cold]
|
|
||||||
pub fn as_qpalette(&self) -> String {
|
|
||||||
let lightest = if self.is_dark {
|
|
||||||
self.background.on
|
|
||||||
} else {
|
|
||||||
self.background.base
|
|
||||||
};
|
|
||||||
let darkest = if self.is_dark {
|
|
||||||
self.background.base
|
|
||||||
} else {
|
|
||||||
self.background.on
|
|
||||||
};
|
|
||||||
let active = QPaletteGroup {
|
|
||||||
window_text: self.background.on,
|
|
||||||
button: self.button.base,
|
|
||||||
light: self.button.base.mix(lightest, 0.1),
|
|
||||||
midlight: self.button.base.mix(lightest, 0.05),
|
|
||||||
dark: self.button.base.mix(darkest, 0.1),
|
|
||||||
mid: self.button.base.mix(darkest, 0.05),
|
|
||||||
text: self.background.component.on,
|
|
||||||
bright_text: lightest,
|
|
||||||
button_text: self.button.on,
|
|
||||||
base: self.background.component.base,
|
|
||||||
window: self.background.base,
|
|
||||||
shadow: darkest,
|
|
||||||
// selection colors are swapped to fix menu bar contrast
|
|
||||||
highlight: self.background.component.selected_text,
|
|
||||||
highlighted_text: self.background.component.selected,
|
|
||||||
link: self.link_button.on,
|
|
||||||
link_visited: self.link_button.on.mix(self.secondary.component.base, 0.2),
|
|
||||||
alternate_base: self.background.base.mix(self.accent.base, 0.05),
|
|
||||||
no_role: self.background.component.disabled,
|
|
||||||
tool_tip_base: self.background.component.base,
|
|
||||||
tool_tip_text: self.background.component.on,
|
|
||||||
placeholder_text: self.background.component.on.with_alpha(0.5),
|
|
||||||
};
|
|
||||||
let inactive = QPaletteGroup {
|
|
||||||
window_text: active.window_text.with_alpha(0.8),
|
|
||||||
text: active.text.with_alpha(0.8),
|
|
||||||
highlighted_text: active.highlighted_text.with_alpha(0.8),
|
|
||||||
tool_tip_text: active.tool_tip_text.with_alpha(0.8),
|
|
||||||
..active
|
|
||||||
};
|
|
||||||
let disabled = QPaletteGroup {
|
|
||||||
button: self.button.disabled,
|
|
||||||
text: self.background.component.on_disabled,
|
|
||||||
button_text: self.button.on_disabled,
|
|
||||||
base: self.background.component.disabled,
|
|
||||||
highlighted_text: active.highlighted_text.with_alpha(0.5),
|
|
||||||
link: self.link_button.on_disabled,
|
|
||||||
link_visited: self
|
|
||||||
.link_button
|
|
||||||
.on_disabled
|
|
||||||
.mix(self.secondary.component.disabled, 0.2),
|
|
||||||
alternate_base: self.background.base.mix(self.accent.disabled, 0.05),
|
|
||||||
tool_tip_base: self.background.component.disabled,
|
|
||||||
tool_tip_text: self.background.component.on_disabled,
|
|
||||||
placeholder_text: self.background.component.on_disabled.with_alpha(0.5),
|
|
||||||
..inactive
|
|
||||||
};
|
|
||||||
|
|
||||||
format!(
|
|
||||||
r#"# GENERATED BY COSMIC
|
|
||||||
|
|
||||||
[ColorScheme]
|
|
||||||
active_colors={}
|
|
||||||
disabled_colors={}
|
|
||||||
inactive_colors={}
|
|
||||||
"#,
|
|
||||||
active.as_list(),
|
|
||||||
disabled.as_list(),
|
|
||||||
inactive.as_list(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the QPalette ini files to:
|
|
||||||
/// - `~/.config/qt6ct/colors/`
|
|
||||||
/// - `~/.config/qt5ct/colors/`
|
|
||||||
#[cold]
|
|
||||||
pub fn write_qt56ct(&self) -> Result<(), OutputError> {
|
|
||||||
let qpalette = self.as_qpalette();
|
|
||||||
let qt5ct_res = self.write_ct("qt5ct", &qpalette);
|
|
||||||
let qt6ct_res = self.write_ct("qt6ct", &qpalette);
|
|
||||||
qt5ct_res?;
|
|
||||||
qt6ct_res?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
#[cold]
|
|
||||||
fn write_ct(&self, ct: &str, qpalette: &str) -> Result<(), OutputError> {
|
|
||||||
let file_path = Self::get_qpalette_path(ct, self.is_dark)?;
|
|
||||||
let tmp_file_path = file_path.with_extension("conf.new");
|
|
||||||
|
|
||||||
let mut tmp_file = File::create(&tmp_file_path).map_err(OutputError::Io)?;
|
|
||||||
let res = tmp_file
|
|
||||||
.write_all(qpalette.as_bytes())
|
|
||||||
.and_then(|_| tmp_file.flush())
|
|
||||||
.and_then(|_| std::fs::rename(&tmp_file_path, file_path));
|
|
||||||
if let Err(e) = res {
|
|
||||||
_ = std::fs::remove_file(&tmp_file_path);
|
|
||||||
return Err(OutputError::Io(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Edits qt{5,6}ct.conf to use COSMIC styles if needed.
|
/// Edits qt{5,6}ct.conf to use COSMIC styles if needed.
|
||||||
#[cold]
|
#[cold]
|
||||||
|
|
@ -152,7 +39,7 @@ inactive_colors={}
|
||||||
.map_err(OutputError::Ini)?
|
.map_err(OutputError::Ini)?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let color_scheme_path = Self::get_qpalette_path(ct, is_dark)?;
|
let color_scheme_path = Self::get_qt_colors_path(is_dark)?;
|
||||||
let icon_theme = if is_dark { "breeze-dark" } else { "breeze" };
|
let icon_theme = if is_dark { "breeze-dark" } else { "breeze" };
|
||||||
|
|
||||||
ini.set(
|
ini.set(
|
||||||
|
|
@ -204,48 +91,11 @@ inactive_colors={}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset the applied qt56ct config by removing COSMIC-specific entries from the config file.
|
|
||||||
#[cold]
|
|
||||||
pub fn reset_qt56ct() -> Result<(), OutputError> {
|
|
||||||
let qt5ct_res = Self::reset_ct("qt5ct");
|
|
||||||
let qt6ct_res = Self::reset_ct("qt6ct");
|
|
||||||
qt5ct_res?;
|
|
||||||
qt6ct_res?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
#[cold]
|
|
||||||
fn reset_ct(ct: &str) -> Result<(), OutputError> {
|
|
||||||
let path = Self::get_conf_path(ct)?;
|
|
||||||
let file_content = fs::read_to_string(&path).map_err(OutputError::Io)?;
|
|
||||||
let mut ini = Ini::new_cs();
|
|
||||||
ini.read(file_content).map_err(OutputError::Ini)?;
|
|
||||||
|
|
||||||
let old_version = ini
|
|
||||||
.getuint("Appearance", "cosmic_qt_version")
|
|
||||||
.map_err(OutputError::Ini)?
|
|
||||||
.unwrap_or_default();
|
|
||||||
if old_version == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
ini.remove_key("Appearance", "cosmic_qt_version");
|
|
||||||
ini.remove_key("Appearance", "color_scheme_path");
|
|
||||||
ini.remove_key("Appearance", "icon_theme");
|
|
||||||
|
|
||||||
ini.pretty_write(path, &qt_settings_ini_style())
|
|
||||||
.map_err(OutputError::Io)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the file paths of the form `~/.config/ct/ct.conf`:
|
/// Returns the file paths of the form `~/.config/ct/ct.conf`:
|
||||||
/// e.g. `~/.config/qt6ct/qt6ct.conf`.
|
/// e.g. `~/.config/qt6ct/qt6ct.conf`.
|
||||||
///
|
///
|
||||||
/// The file and its parent directory are created if they don't exist.
|
/// The file and its parent directory are created if they don't exist.
|
||||||
#[cold]
|
|
||||||
fn get_conf_path(ct: &str) -> Result<PathBuf, OutputError> {
|
fn get_conf_path(ct: &str) -> Result<PathBuf, OutputError> {
|
||||||
assert!(ct == "qt5ct" || ct == "qt6ct");
|
|
||||||
|
|
||||||
let Some(mut config_dir) = dirs::config_dir() else {
|
let Some(mut config_dir) = dirs::config_dir() else {
|
||||||
return Err(OutputError::MissingConfigDir);
|
return Err(OutputError::MissingConfigDir);
|
||||||
};
|
};
|
||||||
|
|
@ -261,155 +111,4 @@ inactive_colors={}
|
||||||
|
|
||||||
Ok(file_path)
|
Ok(file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a path like `~/.config/qt6ct/colors/CosmicDark.conf`
|
|
||||||
///
|
|
||||||
/// Its parent directory is created if it doesn't exist.
|
|
||||||
#[cold]
|
|
||||||
fn get_qpalette_path(ct: &str, is_dark: bool) -> Result<PathBuf, OutputError> {
|
|
||||||
assert!(ct == "qt5ct" || ct == "qt6ct");
|
|
||||||
|
|
||||||
let Some(mut config_dir) = dirs::config_dir() else {
|
|
||||||
return Err(OutputError::MissingConfigDir);
|
|
||||||
};
|
|
||||||
config_dir.push(&ct);
|
|
||||||
config_dir.push("colors");
|
|
||||||
if !config_dir.exists() {
|
|
||||||
fs::create_dir_all(&config_dir).map_err(OutputError::Io)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_name = if is_dark {
|
|
||||||
"CosmicDark.conf"
|
|
||||||
} else {
|
|
||||||
"CosmicLight.conf"
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(config_dir.join(file_name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the different symbolic color roles used in current GUIs.
|
|
||||||
///
|
|
||||||
/// qt5ct and qt6ct consume this as a list of colors, ordered by ColorRole:
|
|
||||||
/// - https://doc.qt.io/qt-6/qpalette.html#ColorRole-enum
|
|
||||||
/// - https://doc.qt.io/archives/qt-5.15/qpalette.html#ColorRole-enum
|
|
||||||
struct QPaletteGroup {
|
|
||||||
/// A general foreground color.
|
|
||||||
window_text: Srgba,
|
|
||||||
/// The general button background color.
|
|
||||||
button: Srgba,
|
|
||||||
/// Lighter than [button] color, used mostly for 3D bevel and shadow effects.
|
|
||||||
light: Srgba,
|
|
||||||
/// Between [button] and [light], used mostly for 3D bevel and shadow effects.
|
|
||||||
midlight: Srgba,
|
|
||||||
/// Darker than [button], used mostly for 3D bevel and shadow effects.
|
|
||||||
dark: Srgba,
|
|
||||||
/// Between [button] and [dark], used mostly for 3D bevel and shadow effects.
|
|
||||||
mid: Srgba,
|
|
||||||
/// The foreground color used with [base].
|
|
||||||
text: Srgba,
|
|
||||||
/// A text color that is very different from [window_text], and contrasts well with e.g. [dark].
|
|
||||||
/// Typically used for text that needs to be drawn where [text] or [window_text] would give poor contrast, such as on pressed push buttons.
|
|
||||||
bright_text: Srgba,
|
|
||||||
/// A foreground color used with the [button] color.
|
|
||||||
button_text: Srgba,
|
|
||||||
/// Used mostly as the background color for text entry widgets, but can also be used for other painting -
|
|
||||||
/// such as the background of combobox drop down lists and toolbar handles.
|
|
||||||
base: Srgba,
|
|
||||||
/// A general background color.
|
|
||||||
window: Srgba,
|
|
||||||
/// A very dark color, used mostly for 3D bevel and shadow effects.
|
|
||||||
/// Opaque black by default.
|
|
||||||
shadow: Srgba,
|
|
||||||
/// A color to indicate a selected item or the current item.
|
|
||||||
highlight: Srgba,
|
|
||||||
/// A text color that contrasts with [highlight].
|
|
||||||
highlighted_text: Srgba,
|
|
||||||
/// A text color used for unvisited hyperlinks.
|
|
||||||
link: Srgba,
|
|
||||||
/// A text color used for already visited hyperlinks.
|
|
||||||
link_visited: Srgba,
|
|
||||||
/// Used as the alternate background color in views with alternating row colors.
|
|
||||||
alternate_base: Srgba,
|
|
||||||
/// No role; this special role is often used to indicate that a role has not been assigned.
|
|
||||||
no_role: Srgba,
|
|
||||||
/// Used as the background color for QToolTip and QWhatsThis.
|
|
||||||
/// Tool tips use the inactive color group of QPalette, because tool tips are not active windows.
|
|
||||||
tool_tip_base: Srgba,
|
|
||||||
/// Used as the foreground color for QToolTip and QWhatsThis.
|
|
||||||
/// Tool tips use the inactive color group of QPalette, because tool tips are not active windows.
|
|
||||||
tool_tip_text: Srgba,
|
|
||||||
/// Used as the placeholder color for various text input widgets.
|
|
||||||
placeholder_text: Srgba,
|
|
||||||
// /// [accent] only exists since Qt 6.6. Including it here breaks qt5ct.
|
|
||||||
// /// When omitted, it defaults to [highlight].
|
|
||||||
// accent: Srgba,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QPaletteGroup {
|
|
||||||
/// Returns a comma-separated list of the colors as hex codes.
|
|
||||||
/// E.g. `#ff000000, #ffdcdcdc, ...`
|
|
||||||
///
|
|
||||||
/// Any transparent colors are flattened with [base] to avoid issues with
|
|
||||||
/// the Fusion style.
|
|
||||||
fn as_list(&self) -> String {
|
|
||||||
let colors = vec![
|
|
||||||
to_argb_hex(self.window_text.over(self.base)),
|
|
||||||
to_argb_hex(self.button.over(self.base)),
|
|
||||||
to_argb_hex(self.light.over(self.base)),
|
|
||||||
to_argb_hex(self.midlight.over(self.base)),
|
|
||||||
to_argb_hex(self.dark.over(self.base)),
|
|
||||||
to_argb_hex(self.mid.over(self.base)),
|
|
||||||
to_argb_hex(self.text.over(self.base)),
|
|
||||||
to_argb_hex(self.bright_text.over(self.base)),
|
|
||||||
to_argb_hex(self.button_text.over(self.base)),
|
|
||||||
to_argb_hex(self.base.over(self.base)),
|
|
||||||
to_argb_hex(self.window.over(self.base)),
|
|
||||||
to_argb_hex(self.shadow.over(self.base)),
|
|
||||||
to_argb_hex(self.highlight.over(self.base)),
|
|
||||||
to_argb_hex(self.highlighted_text.over(self.base)),
|
|
||||||
to_argb_hex(self.link.over(self.base)),
|
|
||||||
to_argb_hex(self.link_visited.over(self.base)),
|
|
||||||
to_argb_hex(self.alternate_base.over(self.base)),
|
|
||||||
to_argb_hex(self.no_role.over(self.base)),
|
|
||||||
to_argb_hex(self.tool_tip_base.over(self.base)),
|
|
||||||
to_argb_hex(self.tool_tip_text.over(self.base)),
|
|
||||||
to_argb_hex(self.placeholder_text.over(self.base)),
|
|
||||||
];
|
|
||||||
colors.join(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a color to a hex string in the format `#AARRGGBB`.
|
|
||||||
/// Do not use [to_hex] since that uses the format `RRGGBBAA`.
|
|
||||||
fn to_argb_hex(c: Srgba) -> String {
|
|
||||||
let c_u8: Rgba<palette::encoding::Srgb, u8> = c.into_format();
|
|
||||||
format!(
|
|
||||||
"#{:02x}{:02x}{:02x}{:02x}",
|
|
||||||
c_u8.alpha, c_u8.red, c_u8.green, c_u8.blue
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_color_to_argb_hex() {
|
|
||||||
let color = Srgba::new(0x33, 0x55, 0x77, 0xff);
|
|
||||||
let argb = to_argb_hex(color.into());
|
|
||||||
assert_eq!(argb, "#ff335577");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_light_default_qpalette() {
|
|
||||||
let light_default_qpalette = Theme::light_default().as_qpalette();
|
|
||||||
insta::assert_snapshot!(light_default_qpalette);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_dark_default_qpalette() {
|
|
||||||
let dark_default_qpalette = Theme::dark_default().as_qpalette();
|
|
||||||
insta::assert_snapshot!(dark_default_qpalette);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,10 @@ impl Theme {
|
||||||
/// Produces a color scheme ini file for Qt.
|
/// Produces a color scheme ini file for Qt.
|
||||||
///
|
///
|
||||||
/// Some high-level documentation for this file can be found at:
|
/// Some high-level documentation for this file can be found at:
|
||||||
/// - https://api.kde.org/kcolorscheme.html
|
/// https://web.archive.org/web/20250402234329/https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/colors/
|
||||||
/// - https://web.archive.org/web/20250402234329/https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/colors/
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn as_kcolorscheme(&self) -> String {
|
pub fn as_qt(&self) -> String {
|
||||||
// Usually, disabled elements will have strongly reduced contrast and are often notably darker or lighter
|
// Usually, disabled elements will have strongly reduced contrast and are often notably darker or lighter
|
||||||
let disabled_color_effects = IniColorEffects {
|
let disabled_color_effects = IniColorEffects {
|
||||||
color: self.button.disabled,
|
color: self.button.disabled,
|
||||||
|
|
@ -42,7 +41,7 @@ impl Theme {
|
||||||
|
|
||||||
let bg = self.background.base;
|
let bg = self.background.base;
|
||||||
// the background container
|
// the background container
|
||||||
let window_colors = IniColors {
|
let view_colors = IniColors {
|
||||||
background_alternate: bg.mix(self.accent.base, 0.05),
|
background_alternate: bg.mix(self.accent.base, 0.05),
|
||||||
background_normal: bg,
|
background_normal: bg,
|
||||||
decoration_focus: self.accent_text_color(),
|
decoration_focus: self.accent_text_color(),
|
||||||
|
|
@ -57,17 +56,16 @@ impl Theme {
|
||||||
foreground_visited: self.accent_text_color(),
|
foreground_visited: self.accent_text_color(),
|
||||||
};
|
};
|
||||||
// components inside the background container
|
// components inside the background container
|
||||||
let view_colors = IniColors {
|
let window_colors = IniColors {
|
||||||
background_alternate: self.background.component.base.mix(self.accent.base, 0.05),
|
background_alternate: self.background.component.base.mix(self.accent.base, 0.05),
|
||||||
background_normal: self.background.component.base,
|
background_normal: self.background.component.base,
|
||||||
..window_colors
|
..view_colors
|
||||||
};
|
};
|
||||||
|
|
||||||
// selected text and items
|
// selected text and items
|
||||||
let selection_colors = {
|
let selection_colors = {
|
||||||
// selection colors are swapped to fix menu bar contrast
|
let selected = self.background.component.selected;
|
||||||
let selected = self.background.component.selected_text;
|
let selected_text = self.background.component.selected_text;
|
||||||
let selected_text = self.background.component.selected;
|
|
||||||
IniColors {
|
IniColors {
|
||||||
background_alternate: selected.mix(bg, 0.5),
|
background_alternate: selected.mix(bg, 0.5),
|
||||||
background_normal: selected,
|
background_normal: selected,
|
||||||
|
|
@ -94,11 +92,8 @@ impl Theme {
|
||||||
let complementary_colors = {
|
let complementary_colors = {
|
||||||
let dark = if self.is_dark {
|
let dark = if self.is_dark {
|
||||||
self.clone()
|
self.clone()
|
||||||
} else if cfg!(test) {
|
|
||||||
// For reproducible results in tests, use the default dark theme
|
|
||||||
Theme::dark_default()
|
|
||||||
} else {
|
} else {
|
||||||
Theme::dark_config()
|
Theme::light_config()
|
||||||
.ok()
|
.ok()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|conf| Theme::get_entry(conf).ok())
|
.and_then(|conf| Theme::get_entry(conf).ok())
|
||||||
|
|
@ -121,10 +116,10 @@ impl Theme {
|
||||||
};
|
};
|
||||||
|
|
||||||
// headers in cosmic don't have a background
|
// headers in cosmic don't have a background
|
||||||
let header_colors = &window_colors;
|
let header_colors = &view_colors;
|
||||||
let header_colors_inactive = &window_colors;
|
let header_colors_inactive = &view_colors;
|
||||||
// tool tips, "What's This" tips, and similar elements
|
// tool tips, "What's This" tips, and similar elements
|
||||||
let tooltip_colors = &view_colors;
|
let tooltip_colors = &window_colors;
|
||||||
|
|
||||||
let general_color_scheme = if self.is_dark {
|
let general_color_scheme = if self.is_dark {
|
||||||
"CosmicDark"
|
"CosmicDark"
|
||||||
|
|
@ -203,7 +198,7 @@ widgetStyle=qt6ct-style
|
||||||
format_ini_colors(&tooltip_colors, bg),
|
format_ini_colors(&tooltip_colors, bg),
|
||||||
format_ini_colors(&view_colors, bg),
|
format_ini_colors(&view_colors, bg),
|
||||||
format_ini_colors(&window_colors, bg),
|
format_ini_colors(&window_colors, bg),
|
||||||
format_ini_wm_colors(&window_colors, self.is_dark),
|
format_ini_wm_colors(&view_colors, self.is_dark),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,14 +212,14 @@ widgetStyle=qt6ct-style
|
||||||
/// Returns an `OutputError` if there is an error writing the colors file.
|
/// Returns an `OutputError` if there is an error writing the colors file.
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn write_qt(&self) -> Result<(), OutputError> {
|
pub fn write_qt(&self) -> Result<(), OutputError> {
|
||||||
let kcolorscheme = self.as_kcolorscheme();
|
let colors = self.as_qt();
|
||||||
let file_path = Self::get_kcolorscheme_path(self.is_dark)?;
|
let file_path = Self::get_qt_colors_path(self.is_dark)?;
|
||||||
let tmp_file_path = file_path.with_extension("colors.new");
|
let tmp_file_path = file_path.with_extension("colors.new");
|
||||||
|
|
||||||
// Write to tmp_file_path first, then move it to file_path
|
// Write to tmp_file_path first, then move it to file_path
|
||||||
let mut tmp_file = File::create(&tmp_file_path).map_err(OutputError::Io)?;
|
let mut tmp_file = File::create(&tmp_file_path).map_err(OutputError::Io)?;
|
||||||
let res = tmp_file
|
let res = tmp_file
|
||||||
.write_all(kcolorscheme.as_bytes())
|
.write_all(colors.as_bytes())
|
||||||
.and_then(|_| tmp_file.flush())
|
.and_then(|_| tmp_file.flush())
|
||||||
.and_then(|_| std::fs::rename(&tmp_file_path, file_path));
|
.and_then(|_| std::fs::rename(&tmp_file_path, file_path));
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
|
@ -250,7 +245,7 @@ widgetStyle=qt6ct-style
|
||||||
let kdeglobals_file = config_dir.join("kdeglobals");
|
let kdeglobals_file = config_dir.join("kdeglobals");
|
||||||
let mut kdeglobals_ini = Self::read_ini(&kdeglobals_file)?;
|
let mut kdeglobals_ini = Self::read_ini(&kdeglobals_file)?;
|
||||||
|
|
||||||
let src_file = Self::get_kcolorscheme_path(is_dark)?;
|
let src_file = Self::get_qt_colors_path(is_dark)?;
|
||||||
let src_ini = Self::read_ini(&src_file)?;
|
let src_ini = Self::read_ini(&src_file)?;
|
||||||
|
|
||||||
Self::backup_non_cosmic_kdeglobals(&kdeglobals_ini, &kdeglobals_file)
|
Self::backup_non_cosmic_kdeglobals(&kdeglobals_ini, &kdeglobals_file)
|
||||||
|
|
@ -293,7 +288,7 @@ widgetStyle=qt6ct-style
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_dark = false; // doesn't matter since we're only reading keys
|
let is_dark = false; // doesn't matter since we're only reading keys
|
||||||
let src_file = Self::get_kcolorscheme_path(is_dark)?;
|
let src_file = Self::get_qt_colors_path(is_dark)?;
|
||||||
let src_ini = Self::read_ini(&src_file)?;
|
let src_ini = Self::read_ini(&src_file)?;
|
||||||
|
|
||||||
for (section, key_value) in src_ini.get_map_ref() {
|
for (section, key_value) in src_ini.get_map_ref() {
|
||||||
|
|
@ -308,8 +303,8 @@ widgetStyle=qt6ct-style
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a path like `~/.local/share/color-schemes/CosmicDark.colors`
|
/// Gets a path like `~/.config/color-schemes/CosmicDark.colors`
|
||||||
fn get_kcolorscheme_path(is_dark: bool) -> Result<PathBuf, OutputError> {
|
pub fn get_qt_colors_path(is_dark: bool) -> Result<PathBuf, OutputError> {
|
||||||
let Some(mut data_dir) = dirs::data_dir() else {
|
let Some(mut data_dir) = dirs::data_dir() else {
|
||||||
return Err(OutputError::MissingDataDir);
|
return Err(OutputError::MissingDataDir);
|
||||||
};
|
};
|
||||||
|
|
@ -525,44 +520,3 @@ impl ColorEffect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_opaque_color_to_rgb() {
|
|
||||||
let color = Srgba::new(30.0 / 255.0, 50.0 / 255.0, 70.0 / 255.0, 1.0);
|
|
||||||
let bg = Srgba::new(1.0, 1.0, 1.0, 1.0);
|
|
||||||
let result = to_rgb(color, bg);
|
|
||||||
assert_eq!(result, "30,50,70");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transparent_color_to_rgb() {
|
|
||||||
let color = Srgba::new(0.0, 0.0, 0.0, 0.0);
|
|
||||||
let bg = Srgba::new(1.0, 1.0, 1.0, 1.0);
|
|
||||||
let result = to_rgb(color, bg);
|
|
||||||
assert_eq!(result, "255,255,255");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_translucent_color_to_rgb() {
|
|
||||||
let color = Srgba::new(0.0, 0.0, 0.0, 0.9);
|
|
||||||
let bg = Srgba::new(1.0, 1.0, 1.0, 1.0);
|
|
||||||
let result = to_rgb(color, bg);
|
|
||||||
assert_eq!(result, "26,26,26");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_light_default_kcolorscheme() {
|
|
||||||
let light_default_kcolorscheme = Theme::light_default().as_kcolorscheme();
|
|
||||||
insta::assert_snapshot!(light_default_kcolorscheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_dark_default_kcolorscheme() {
|
|
||||||
let dark_default_kcolorscheme = Theme::dark_default().as_kcolorscheme();
|
|
||||||
insta::assert_snapshot!(dark_default_kcolorscheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
source: cosmic-theme/src/output/qt56ct_output.rs
|
|
||||||
expression: dark_default_qpalette
|
|
||||||
---
|
|
||||||
# GENERATED BY COSMIC
|
|
||||||
|
|
||||||
[ColorScheme]
|
|
||||||
active_colors=#ffe7e7e7, #ff4a4a4a, #ff555555, #ff505050, #ff4f4f4f, #ff4d4d4d, #ffc0c0c0, #ffe7e7e7, #ffc0c0c0, #ff2e2e2e, #ff1b1b1b, #ff1b1b1b, #ff63d0df, #ff434343, #ff63d0df, #ff5bb2be, #ff1f2425, #ff2e2e2e, #ff2e2e2e, #ffc0c0c0, #ff777777
|
|
||||||
disabled_colors=#e6d3d3d3, #8f474747, #a9696969, #a4626262, #a95f5f5f, #a45d5d5d, #d2a1a1a1, #ffe7e7e7, #d2a1a1a1, #bf2e2e2e, #ff1b1b1b, #ff1b1b1b, #ff63d0df, #bf3c3c3c, #bf30555a, #bf324f53, #ff1f2425, #bf2e2e2e, #bf2e2e2e, #d2a1a1a1, #bf909090
|
|
||||||
inactive_colors=#ffc2c2c2, #ff4a4a4a, #ff555555, #ff505050, #ff4f4f4f, #ff4d4d4d, #ffa3a3a3, #ffe7e7e7, #ffc0c0c0, #ff2e2e2e, #ff1b1b1b, #ff1b1b1b, #ff63d0df, #ff3f3f3f, #ff63d0df, #ff5bb2be, #ff1f2425, #ff2e2e2e, #ff2e2e2e, #ffa3a3a3, #ff777777
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
source: cosmic-theme/src/output/qt56ct_output.rs
|
|
||||||
expression: light_default_qpalette
|
|
||||||
---
|
|
||||||
# GENERATED BY COSMIC
|
|
||||||
|
|
||||||
[ColorScheme]
|
|
||||||
active_colors=#ff121212, #ffc3c3c3, #ffbababa, #ffbebebe, #ffb3b3b3, #ffbbbbbb, #ff272727, #ffd7d7d7, #ff272727, #fff5f5f5, #ffd7d7d7, #ff121212, #ff00525a, #fff6f6f6, #ff00525a, #ff317379, #ffccd0d1, #fff5f5f5, #fff5f5f5, #ff272727, #ff8e8e8e
|
|
||||||
disabled_colors=#e62b2b2b, #8fc9c9c9, #a99b9b9b, #a4a0a0a0, #a9929292, #a49b9b9b, #d2535353, #ffd7d7d7, #d2535353, #bff5f5f5, #ffd7d7d7, #ff121212, #ff00525a, #bff6f6f6, #bf526d70, #bf72888a, #ffccd0d1, #bff5f5f5, #bff5f5f5, #d2535353, #bf6c6c6c
|
|
||||||
inactive_colors=#ff3f3f3f, #ffc3c3c3, #ffbababa, #ffbebebe, #ffb3b3b3, #ffbbbbbb, #ff505050, #ffd7d7d7, #ff272727, #fff5f5f5, #ffd7d7d7, #ff121212, #ff00525a, #fff6f6f6, #ff00525a, #ff317379, #ffccd0d1, #fff5f5f5, #fff5f5f5, #ff505050, #ff8e8e8e
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
---
|
|
||||||
source: cosmic-theme/src/output/qt_output.rs
|
|
||||||
expression: dark_default_kcolorscheme
|
|
||||||
---
|
|
||||||
# GENERATED BY COSMIC
|
|
||||||
|
|
||||||
[ColorEffects:Disabled]
|
|
||||||
Color=43,43,43
|
|
||||||
ColorAmount=0
|
|
||||||
ColorEffect=0
|
|
||||||
ContrastAmount=0.65
|
|
||||||
ContrastEffect=1
|
|
||||||
IntensityAmount=0.1
|
|
||||||
IntensityEffect=2
|
|
||||||
|
|
||||||
[ColorEffects:Inactive]
|
|
||||||
ChangeSelectionColor=false
|
|
||||||
Enable=false
|
|
||||||
Color=27,27,27
|
|
||||||
ColorAmount=0.025
|
|
||||||
ColorEffect=2
|
|
||||||
ContrastAmount=0.1
|
|
||||||
ContrastEffect=2
|
|
||||||
IntensityAmount=0
|
|
||||||
IntensityEffect=0
|
|
||||||
|
|
||||||
[Colors:Button]
|
|
||||||
BackgroundAlternate=99,208,223
|
|
||||||
BackgroundNormal=60,60,60
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Complementary]
|
|
||||||
BackgroundAlternate=99,208,223
|
|
||||||
BackgroundNormal=27,27,27
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Header]
|
|
||||||
BackgroundAlternate=31,36,37
|
|
||||||
BackgroundNormal=27,27,27
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Header][Inactive]
|
|
||||||
BackgroundAlternate=31,36,37
|
|
||||||
BackgroundNormal=27,27,27
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Selection]
|
|
||||||
BackgroundAlternate=63,118,125
|
|
||||||
BackgroundNormal=99,208,223
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=67,67,67
|
|
||||||
ForegroundInactive=83,138,145
|
|
||||||
ForegroundLink=27,27,27
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=67,67,67
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Tooltip]
|
|
||||||
BackgroundAlternate=49,55,55
|
|
||||||
BackgroundNormal=46,46,46
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:View]
|
|
||||||
BackgroundAlternate=49,55,55
|
|
||||||
BackgroundNormal=46,46,46
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Window]
|
|
||||||
BackgroundAlternate=31,36,37
|
|
||||||
BackgroundNormal=27,27,27
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[General]
|
|
||||||
ColorScheme=CosmicDark
|
|
||||||
Name=COSMIC Dark
|
|
||||||
shadeSortColumn=true
|
|
||||||
|
|
||||||
[Icons]
|
|
||||||
Theme=breeze-dark
|
|
||||||
|
|
||||||
[KDE]
|
|
||||||
contrast=4
|
|
||||||
widgetStyle=qt6ct-style
|
|
||||||
|
|
||||||
[WM]
|
|
||||||
activeBackground=27,27,27
|
|
||||||
activeBlend=99,208,223
|
|
||||||
activeForeground=99,208,223
|
|
||||||
inactiveBackground=27,27,27
|
|
||||||
inactiveBlend=99,208,223
|
|
||||||
inactiveForeground=99,208,223
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
---
|
|
||||||
source: cosmic-theme/src/output/qt_output.rs
|
|
||||||
expression: light_default_kcolorscheme
|
|
||||||
---
|
|
||||||
# GENERATED BY COSMIC
|
|
||||||
|
|
||||||
[ColorEffects:Disabled]
|
|
||||||
Color=194,194,194
|
|
||||||
ColorAmount=0
|
|
||||||
ColorEffect=0
|
|
||||||
ContrastAmount=0.65
|
|
||||||
ContrastEffect=1
|
|
||||||
IntensityAmount=0.1
|
|
||||||
IntensityEffect=2
|
|
||||||
|
|
||||||
[ColorEffects:Inactive]
|
|
||||||
ChangeSelectionColor=false
|
|
||||||
Enable=false
|
|
||||||
Color=215,215,215
|
|
||||||
ColorAmount=0.025
|
|
||||||
ColorEffect=2
|
|
||||||
ContrastAmount=0.1
|
|
||||||
ContrastEffect=2
|
|
||||||
IntensityAmount=0
|
|
||||||
IntensityEffect=0
|
|
||||||
|
|
||||||
[Colors:Button]
|
|
||||||
BackgroundAlternate=0,82,90
|
|
||||||
BackgroundNormal=173,173,173
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=0,82,90
|
|
||||||
ForegroundInactive=38,38,38
|
|
||||||
ForegroundLink=0,82,90
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=18,18,18
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[Colors:Complementary]
|
|
||||||
BackgroundAlternate=99,208,223
|
|
||||||
BackgroundNormal=27,27,27
|
|
||||||
DecorationFocus=99,208,223
|
|
||||||
DecorationHover=99,208,223
|
|
||||||
ForegroundActive=99,208,223
|
|
||||||
ForegroundInactive=211,211,211
|
|
||||||
ForegroundLink=99,208,223
|
|
||||||
ForegroundNegative=255,160,154
|
|
||||||
ForegroundNeutral=255,163,125
|
|
||||||
ForegroundNormal=231,231,231
|
|
||||||
ForegroundPositive=94,219,140
|
|
||||||
ForegroundVisited=99,208,223
|
|
||||||
|
|
||||||
[Colors:Header]
|
|
||||||
BackgroundAlternate=204,208,209
|
|
||||||
BackgroundNormal=215,215,215
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=0,82,90
|
|
||||||
ForegroundInactive=38,38,38
|
|
||||||
ForegroundLink=0,82,90
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=18,18,18
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[Colors:Header][Inactive]
|
|
||||||
BackgroundAlternate=204,208,209
|
|
||||||
BackgroundNormal=215,215,215
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=0,82,90
|
|
||||||
ForegroundInactive=38,38,38
|
|
||||||
ForegroundLink=0,82,90
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=18,18,18
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[Colors:Selection]
|
|
||||||
BackgroundAlternate=108,149,152
|
|
||||||
BackgroundNormal=0,82,90
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=246,246,246
|
|
||||||
ForegroundInactive=123,164,168
|
|
||||||
ForegroundLink=215,215,215
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=246,246,246
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[Colors:Tooltip]
|
|
||||||
BackgroundAlternate=233,237,237
|
|
||||||
BackgroundNormal=245,245,245
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=0,82,90
|
|
||||||
ForegroundInactive=38,38,38
|
|
||||||
ForegroundLink=0,82,90
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=18,18,18
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[Colors:View]
|
|
||||||
BackgroundAlternate=233,237,237
|
|
||||||
BackgroundNormal=245,245,245
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=0,82,90
|
|
||||||
ForegroundInactive=38,38,38
|
|
||||||
ForegroundLink=0,82,90
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=18,18,18
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[Colors:Window]
|
|
||||||
BackgroundAlternate=204,208,209
|
|
||||||
BackgroundNormal=215,215,215
|
|
||||||
DecorationFocus=0,82,90
|
|
||||||
DecorationHover=0,82,90
|
|
||||||
ForegroundActive=0,82,90
|
|
||||||
ForegroundInactive=38,38,38
|
|
||||||
ForegroundLink=0,82,90
|
|
||||||
ForegroundNegative=137,4,24
|
|
||||||
ForegroundNeutral=121,44,0
|
|
||||||
ForegroundNormal=18,18,18
|
|
||||||
ForegroundPositive=0,87,44
|
|
||||||
ForegroundVisited=0,82,90
|
|
||||||
|
|
||||||
[General]
|
|
||||||
ColorScheme=CosmicLight
|
|
||||||
Name=COSMIC Light
|
|
||||||
shadeSortColumn=true
|
|
||||||
|
|
||||||
[Icons]
|
|
||||||
Theme=breeze
|
|
||||||
|
|
||||||
[KDE]
|
|
||||||
contrast=4
|
|
||||||
widgetStyle=qt6ct-style
|
|
||||||
|
|
||||||
[WM]
|
|
||||||
activeBackground=215,215,215
|
|
||||||
activeBlend=215,215,215
|
|
||||||
activeForeground=0,82,90
|
|
||||||
inactiveBackground=215,215,215
|
|
||||||
inactiveBlend=215,215,215
|
|
||||||
inactiveForeground=0,82,90
|
|
||||||
|
|
@ -145,6 +145,7 @@ pub fn is_valid_srgb(c: Srgba) -> bool {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use almost::equal;
|
||||||
use palette::{OklabHue, Srgba};
|
use palette::{OklabHue, Srgba};
|
||||||
|
|
||||||
use super::{is_valid_srgb, oklch_to_srgba_nearest_chroma};
|
use super::{is_valid_srgb, oklch_to_srgba_nearest_chroma};
|
||||||
|
|
@ -172,57 +173,57 @@ mod tests {
|
||||||
fn test_conversion_boundaries() {
|
fn test_conversion_boundaries() {
|
||||||
let c1 = palette::Oklcha::new(0.0, 0.288, OklabHue::from_degrees(0.0), 1.0);
|
let c1 = palette::Oklcha::new(0.0, 0.288, OklabHue::from_degrees(0.0), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1);
|
let srgb = oklch_to_srgba_nearest_chroma(c1);
|
||||||
almost::zero(srgb.red);
|
equal(srgb.red, 0.0);
|
||||||
almost::zero(srgb.blue);
|
equal(srgb.blue, 0.0);
|
||||||
almost::zero(srgb.green);
|
equal(srgb.green, 0.0);
|
||||||
|
|
||||||
let c1 = palette::Oklcha::new(1.0, 0.288, OklabHue::from_degrees(0.0), 1.0);
|
let c1 = palette::Oklcha::new(1.0, 0.288, OklabHue::from_degrees(0.0), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1);
|
let srgb = oklch_to_srgba_nearest_chroma(c1);
|
||||||
|
|
||||||
almost::equal(srgb.red, 1.0);
|
equal(srgb.red, 1.0);
|
||||||
almost::equal(srgb.blue, 1.0);
|
equal(srgb.blue, 1.0);
|
||||||
almost::equal(srgb.green, 1.0);
|
equal(srgb.green, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_conversion_colors() {
|
fn test_conversion_colors() {
|
||||||
let c1 = palette::Oklcha::new(0.4608, 0.11111, OklabHue::new(57.31), 1.0);
|
let c1 = palette::Oklcha::new(0.4608, 0.11111, OklabHue::new(57.31), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
||||||
assert_eq!(srgb.red, 133);
|
assert!(srgb.red == 133);
|
||||||
assert_eq!(srgb.green, 69);
|
assert!(srgb.green == 69);
|
||||||
assert_eq!(srgb.blue, 0);
|
assert!(srgb.blue == 0);
|
||||||
|
|
||||||
let c1 = palette::Oklcha::new(0.30, 0.08, OklabHue::new(35.0), 1.0);
|
let c1 = palette::Oklcha::new(0.30, 0.08, OklabHue::new(35.0), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
||||||
assert_eq!(srgb.red, 78);
|
assert!(srgb.red == 78);
|
||||||
assert_eq!(srgb.green, 27);
|
assert!(srgb.green == 27);
|
||||||
assert_eq!(srgb.blue, 15);
|
assert!(srgb.blue == 15);
|
||||||
|
|
||||||
let c1 = palette::Oklcha::new(0.757, 0.146, OklabHue::new(301.2), 1.0);
|
let c1 = palette::Oklcha::new(0.757, 0.146, OklabHue::new(301.2), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
||||||
assert_eq!(srgb.red, 192);
|
assert!(srgb.red == 192);
|
||||||
assert_eq!(srgb.green, 153);
|
assert!(srgb.green == 153);
|
||||||
assert_eq!(srgb.blue, 253);
|
assert!(srgb.blue == 253);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_conversion_fallback_colors() {
|
fn test_conversion_fallback_colors() {
|
||||||
let c1 = palette::Oklcha::new(0.70, 0.284, OklabHue::new(35.0), 1.0);
|
let c1 = palette::Oklcha::new(0.70, 0.284, OklabHue::new(35.0), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
||||||
assert_eq!(srgb.red, 255);
|
assert!(srgb.red == 255);
|
||||||
assert_eq!(srgb.green, 102);
|
assert!(srgb.green == 103);
|
||||||
assert_eq!(srgb.blue, 65);
|
assert!(srgb.blue == 65);
|
||||||
|
|
||||||
let c1 = palette::Oklcha::new(0.757, 0.239, OklabHue::new(301.2), 1.0);
|
let c1 = palette::Oklcha::new(0.757, 0.239, OklabHue::new(301.2), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
||||||
assert_eq!(srgb.red, 193);
|
assert!(srgb.red == 193);
|
||||||
assert_eq!(srgb.green, 152);
|
assert!(srgb.green == 152);
|
||||||
assert_eq!(srgb.blue, 255);
|
assert!(srgb.blue == 255);
|
||||||
|
|
||||||
let c1 = palette::Oklcha::new(0.163, 0.333, OklabHue::new(141.0), 1.0);
|
let c1 = palette::Oklcha::new(0.163, 0.333, OklabHue::new(141.0), 1.0);
|
||||||
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
|
||||||
assert_eq!(srgb.red, 1);
|
assert!(srgb.red == 1);
|
||||||
assert_eq!(srgb.green, 19);
|
assert!(srgb.green == 19);
|
||||||
assert_eq!(srgb.blue, 0);
|
assert!(srgb.blue == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ impl cosmic::Application for App {
|
||||||
fn view(&self) -> Element<'_, Self::Message> {
|
fn view(&self) -> Element<'_, Self::Message> {
|
||||||
let show_about_button = widget::button::text("Show about").on_press(Message::ToggleAbout);
|
let show_about_button = widget::button::text("Show about").on_press(Message::ToggleAbout);
|
||||||
let centered = cosmic::widget::container(
|
let centered = cosmic::widget::container(
|
||||||
widget::column::with_capacity(1)
|
widget::column()
|
||||||
.push(show_about_button)
|
.push(show_about_button)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,6 @@ env_logger = "0.10.2"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic]
|
||||||
path = "../../"
|
git = "https://github.com/pop-os/libcosmic"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["applet-token"]
|
features = ["applet-token"]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use cosmic::app::{Core, Task};
|
use cosmic::app::{Core, Task};
|
||||||
|
|
||||||
use cosmic::iced::core::window;
|
|
||||||
use cosmic::iced::window::Id;
|
use cosmic::iced::window::Id;
|
||||||
use cosmic::iced::{Length, Rectangle};
|
use cosmic::iced::{Length, Rectangle};
|
||||||
|
use cosmic::iced_runtime::core::window;
|
||||||
use cosmic::surface::action::{app_popup, destroy_popup};
|
use cosmic::surface::action::{app_popup, destroy_popup};
|
||||||
use cosmic::widget::{dropdown::popup_dropdown, list_column, settings, toggler};
|
use cosmic::widget::{dropdown::popup_dropdown, list_column, settings, toggler};
|
||||||
use cosmic::Element;
|
use cosmic::Element;
|
||||||
|
|
@ -13,7 +13,6 @@ pub struct Window {
|
||||||
core: Core,
|
core: Core,
|
||||||
popup: Option<Id>,
|
popup: Option<Id>,
|
||||||
example_row: bool,
|
example_row: bool,
|
||||||
toggle: bool,
|
|
||||||
selected: Option<usize>,
|
selected: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,7 +22,6 @@ impl Default for Window {
|
||||||
core: Core::default(),
|
core: Core::default(),
|
||||||
popup: None,
|
popup: None,
|
||||||
example_row: false,
|
example_row: false,
|
||||||
toggle: false,
|
|
||||||
selected: None,
|
selected: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +33,6 @@ pub enum Message {
|
||||||
ToggleExampleRow(bool),
|
ToggleExampleRow(bool),
|
||||||
Selected(usize),
|
Selected(usize),
|
||||||
Surface(cosmic::surface::Action),
|
Surface(cosmic::surface::Action),
|
||||||
Toggle(bool),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl cosmic::Application for Window {
|
impl cosmic::Application for Window {
|
||||||
|
|
@ -74,6 +71,7 @@ impl cosmic::Application for Window {
|
||||||
Message::ToggleExampleRow(toggled) => {
|
Message::ToggleExampleRow(toggled) => {
|
||||||
self.example_row = toggled;
|
self.example_row = toggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::Surface(a) => {
|
Message::Surface(a) => {
|
||||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
return cosmic::task::message(cosmic::Action::Cosmic(
|
||||||
cosmic::app::Action::Surface(a),
|
cosmic::app::Action::Surface(a),
|
||||||
|
|
@ -82,9 +80,6 @@ impl cosmic::Application for Window {
|
||||||
Message::Selected(i) => {
|
Message::Selected(i) => {
|
||||||
self.selected = Some(i);
|
self.selected = Some(i);
|
||||||
}
|
}
|
||||||
Message::Toggle(v) => {
|
|
||||||
self.toggle = v;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -128,8 +123,9 @@ impl cosmic::Application for Window {
|
||||||
"Example row",
|
"Example row",
|
||||||
cosmic::widget::container(
|
cosmic::widget::container(
|
||||||
toggler(state.example_row)
|
toggler(state.example_row)
|
||||||
.on_toggle(Message::ToggleExampleRow),
|
.on_toggle(|value| Message::ToggleExampleRow(value)),
|
||||||
),
|
)
|
||||||
|
.height(Length::Fixed(50.)),
|
||||||
))
|
))
|
||||||
.add(popup_dropdown(
|
.add(popup_dropdown(
|
||||||
&["1", "asdf", "hello", "test"],
|
&["1", "asdf", "hello", "test"],
|
||||||
|
|
@ -159,7 +155,7 @@ impl cosmic::Application for Window {
|
||||||
"oops".into()
|
"oops".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||||
Some(cosmic::applet::style())
|
Some(cosmic::applet::style())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ default = ["wayland"]
|
||||||
wayland = ["libcosmic/wayland"]
|
wayland = ["libcosmic/wayland"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.11"
|
tracing = "0.1.44"
|
||||||
|
tracing-subscriber = "0.3.22"
|
||||||
|
tracing-log = "0.2.0"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
|
|
@ -18,7 +20,7 @@ features = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"xdg-portal",
|
"xdg-portal",
|
||||||
"a11y",
|
"a11y",
|
||||||
|
"wgpu",
|
||||||
"single-instance",
|
"single-instance",
|
||||||
"surface-message",
|
"surface-message",
|
||||||
"multi-window",
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,8 @@ impl widget::menu::Action for Action {
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// tracing_subscriber::fmt::init();
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
// let _ = tracing_log::LogTracer::init();
|
||||||
|
|
||||||
|
|
||||||
let input = vec![
|
let input = vec![
|
||||||
(Page::Page1, "🖖 Hello from libcosmic.".into()),
|
(Page::Page1, "🖖 Hello from libcosmic.".into()),
|
||||||
|
|
@ -67,7 +66,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
let settings = Settings::default()
|
let settings = Settings::default()
|
||||||
.size(Size::new(1024., 768.));
|
.size(Size::new(1024., 768.));
|
||||||
cosmic::app::run::<App>(settings, input).unwrap();
|
|
||||||
|
cosmic::app::run::<App>(settings, input)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +83,6 @@ pub enum Message {
|
||||||
Hi,
|
Hi,
|
||||||
Hi2,
|
Hi2,
|
||||||
Hi3,
|
Hi3,
|
||||||
Tick,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`App`] stores application-specific state.
|
/// The [`App`] stores application-specific state.
|
||||||
|
|
@ -93,7 +93,6 @@ pub struct App {
|
||||||
input_2: String,
|
input_2: String,
|
||||||
hidden: bool,
|
hidden: bool,
|
||||||
keybinds: HashMap<KeyBind, Action>,
|
keybinds: HashMap<KeyBind, Action>,
|
||||||
progress: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
||||||
|
|
@ -135,7 +134,6 @@ impl cosmic::Application for App {
|
||||||
input_2: String::new(),
|
input_2: String::new(),
|
||||||
hidden: true,
|
hidden: true,
|
||||||
keybinds: HashMap::new(),
|
keybinds: HashMap::new(),
|
||||||
progress: 0.0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let command = app.update_title();
|
let command = app.update_title();
|
||||||
|
|
@ -181,17 +179,10 @@ impl cosmic::Application for App {
|
||||||
Message::Hi3 => {
|
Message::Hi3 => {
|
||||||
dbg!("hi 3");
|
dbg!("hi 3");
|
||||||
}
|
}
|
||||||
Message::Tick => {
|
|
||||||
self.progress = (self.progress + 0.01) % 1.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> iced::Subscription<Self::Message> {
|
|
||||||
iced::time::every(std::time::Duration::from_millis(64)).map(|_| Message::Tick)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a view after each update.
|
/// Creates a view after each update.
|
||||||
fn view(&self) -> Element<'_, Self::Message> {
|
fn view(&self) -> Element<'_, Self::Message> {
|
||||||
let page_content = self
|
let page_content = self
|
||||||
|
|
@ -200,7 +191,7 @@ impl cosmic::Application for App {
|
||||||
.map_or("No page selected", String::as_str);
|
.map_or("No page selected", String::as_str);
|
||||||
|
|
||||||
let centered = widget::container(
|
let centered = widget::container(
|
||||||
widget::column::with_capacity(5)
|
widget::column()
|
||||||
.push(widget::text::body(page_content))
|
.push(widget::text::body(page_content))
|
||||||
.push(
|
.push(
|
||||||
widget::text_input::text_input("", &self.input_1)
|
widget::text_input::text_input("", &self.input_1)
|
||||||
|
|
@ -222,46 +213,6 @@ impl cosmic::Application for App {
|
||||||
.on_input(Message::Input2)
|
.on_input(Message::Input2)
|
||||||
.on_clear(Message::Ignore),
|
.on_clear(Message::Ignore),
|
||||||
)
|
)
|
||||||
.push(widget::progress_bar::circular::Circular::new().size(50.0))
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::linear::Linear::new()
|
|
||||||
.girth(10.0)
|
|
||||||
.width(Length::Fill),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::circular::Circular::new()
|
|
||||||
.bar_height(10.0)
|
|
||||||
.size(50.0)
|
|
||||||
.progress(self.progress),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::linear::Linear::new()
|
|
||||||
.girth(10.0)
|
|
||||||
.progress(self.progress)
|
|
||||||
.width(Length::Fill),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::circular::Circular::new()
|
|
||||||
.size(50.0)
|
|
||||||
.progress(0.0),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::linear::Linear::new()
|
|
||||||
.girth(10.0)
|
|
||||||
.progress(0.0)
|
|
||||||
.width(Length::Fill),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::circular::Circular::new()
|
|
||||||
.size(50.0)
|
|
||||||
.progress(1.0),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
widget::progress_bar::linear::Linear::new()
|
|
||||||
.girth(10.0)
|
|
||||||
.progress(1.0)
|
|
||||||
.width(Length::Fill),
|
|
||||||
)
|
|
||||||
.spacing(cosmic::theme::spacing().space_s)
|
.spacing(cosmic::theme::spacing().space_s)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,8 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
/// Creates a view after each update.
|
/// Creates a view after each update.
|
||||||
fn view(&self) -> Element<'_, Self::Message> {
|
fn view(&self) -> Element<'_, Self::Message> {
|
||||||
|
let mut content = cosmic::widget::column().spacing(12);
|
||||||
|
|
||||||
let calendar = cosmic::widget::calendar(
|
let calendar = cosmic::widget::calendar(
|
||||||
&self.calendar_model,
|
&self.calendar_model,
|
||||||
|date| Message::DateSelected(date),
|
|date| Message::DateSelected(date),
|
||||||
|
|
@ -93,7 +95,9 @@ impl cosmic::Application for App {
|
||||||
Weekday::Sunday,
|
Weekday::Sunday,
|
||||||
);
|
);
|
||||||
|
|
||||||
let centered = cosmic::widget::container(calendar)
|
content = content.push(calendar);
|
||||||
|
|
||||||
|
let centered = cosmic::widget::container(content)
|
||||||
.width(iced::Length::Fill)
|
.width(iced::Length::Fill)
|
||||||
.height(iced::Length::Shrink)
|
.height(iced::Length::Shrink)
|
||||||
.align_x(iced::Alignment::Center)
|
.align_x(iced::Alignment::Center)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
//! Application API example
|
//! Application API example
|
||||||
|
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::Size;
|
use cosmic::iced_core::Size;
|
||||||
use cosmic::widget::menu;
|
use cosmic::widget::menu;
|
||||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,13 @@ impl State {
|
||||||
column!(
|
column!(
|
||||||
list_column().add(settings::item(
|
list_column().add(settings::item(
|
||||||
"Bluetooth",
|
"Bluetooth",
|
||||||
toggler(self.enabled).on_toggle(Message::Enable)
|
toggler(None, self.enabled, Message::Enable)
|
||||||
)),
|
)),
|
||||||
text("Now visible as \"TODO\", just kidding")
|
text("Now visible as \"TODO\", just kidding")
|
||||||
)
|
)
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Devices")
|
||||||
.title("Devices")
|
|
||||||
.add(settings::item("No devices found", text("")))
|
.add(settings::item("No devices found", text("")))
|
||||||
.into(),
|
.into(),
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -258,13 +258,12 @@ impl State {
|
||||||
match self.tab_bar.active_data() {
|
match self.tab_bar.active_data() {
|
||||||
None => panic!("no tab is active"),
|
None => panic!("no tab is active"),
|
||||||
Some(DemoView::TabA) => settings::view_column(vec![
|
Some(DemoView::TabA) => settings::view_column(vec![
|
||||||
settings::section()
|
settings::view_section("Debug")
|
||||||
.title("Debug")
|
|
||||||
.add(settings::item("Debug theme", choose_theme))
|
.add(settings::item("Debug theme", choose_theme))
|
||||||
.add(settings::item("Debug icon theme", choose_icon_theme))
|
.add(settings::item("Debug icon theme", choose_icon_theme))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Debug layout",
|
"Debug layout",
|
||||||
toggler(window.debug).on_toggle(Message::Debug),
|
toggler(None, window.debug, Message::Debug),
|
||||||
))
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Scaling Factor",
|
"Scaling Factor",
|
||||||
|
|
@ -277,11 +276,10 @@ impl State {
|
||||||
.into(),
|
.into(),
|
||||||
]))
|
]))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Controls")
|
||||||
.title("Controls")
|
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Toggler",
|
"Toggler",
|
||||||
toggler(self.toggler_value).on_toggle(Message::TogglerToggled),
|
toggler(None, self.toggler_value, Message::TogglerToggled),
|
||||||
))
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Pick List (TODO)",
|
"Pick List (TODO)",
|
||||||
|
|
@ -301,12 +299,14 @@ impl State {
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Progress",
|
"Progress",
|
||||||
progress_bar(0.0..=100.0, self.slider_value)
|
progress_bar(0.0..=100.0, self.slider_value)
|
||||||
.length(Length::Fixed(250.0))
|
.width(Length::Fixed(250.0))
|
||||||
.girth(Length::Fixed(4.0)),
|
.height(Length::Fixed(4.0)),
|
||||||
))
|
))
|
||||||
.add(settings::item_row(vec![checkbox(self.checkbox_value)
|
.add(settings::item_row(vec![checkbox(
|
||||||
.label("Checkbox")
|
"Checkbox",
|
||||||
.on_toggle(Message::CheckboxToggled)
|
self.checkbox_value,
|
||||||
|
Message::CheckboxToggled,
|
||||||
|
)
|
||||||
.into()]))
|
.into()]))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
format!(
|
format!(
|
||||||
|
|
@ -354,7 +354,8 @@ impl State {
|
||||||
.width(Length::Shrink)
|
.width(Length::Shrink)
|
||||||
.on_activate(Message::MultiSelection)
|
.on_activate(Message::MultiSelection)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.center_x(Length::Fill)
|
.center_x()
|
||||||
|
.width(Length::Fill)
|
||||||
.into(),
|
.into(),
|
||||||
text("Vertical With Spacing").into(),
|
text("Vertical With Spacing").into(),
|
||||||
cosmic::iced::widget::row(vec![
|
cosmic::iced::widget::row(vec![
|
||||||
|
|
@ -423,12 +424,13 @@ impl State {
|
||||||
])
|
])
|
||||||
.padding(0)
|
.padding(0)
|
||||||
.into(),
|
.into(),
|
||||||
Some(DemoView::TabC) => settings::view_column(vec![settings::section()
|
Some(DemoView::TabC) => {
|
||||||
.title("Tab C")
|
settings::view_column(vec![settings::view_section("Tab C")
|
||||||
.add(text("Nothing here yet").width(Length::Fill))
|
.add(text("Nothing here yet").width(Length::Fill))
|
||||||
.into()])
|
.into()])
|
||||||
.padding(0)
|
.padding(0)
|
||||||
.into(),
|
.into()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
container(text("Background container with some text").size(24))
|
container(text("Background container with some text").size(24))
|
||||||
.layer(cosmic_theme::Layer::Background)
|
.layer(cosmic_theme::Layer::Background)
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,7 @@ impl State {
|
||||||
fn view_desktop_options<'a>(&'a self, window: &'a Window) -> Element<'a, Message> {
|
fn view_desktop_options<'a>(&'a self, window: &'a Window) -> Element<'a, Message> {
|
||||||
settings::view_column(vec![
|
settings::view_column(vec![
|
||||||
window.parent_page_button(DesktopPage::DesktopOptions),
|
window.parent_page_button(DesktopPage::DesktopOptions),
|
||||||
settings::section()
|
settings::view_section("Super Key Action")
|
||||||
.title("Super Key Action")
|
|
||||||
.add(settings::item("Launcher", horizontal_space(Length::Fill)))
|
.add(settings::item("Launcher", horizontal_space(Length::Fill)))
|
||||||
.add(settings::item("Workspaces", horizontal_space(Length::Fill)))
|
.add(settings::item("Workspaces", horizontal_space(Length::Fill)))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
|
|
@ -156,34 +155,38 @@ impl State {
|
||||||
horizontal_space(Length::Fill),
|
horizontal_space(Length::Fill),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Hot Corner")
|
||||||
.title("Hot Corner")
|
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Enable top-left hot corner for Workspaces",
|
"Enable top-left hot corner for Workspaces",
|
||||||
toggler(self.top_left_hot_corner).on_toggle(Message::TopLeftHotCorner),
|
toggler(None, self.top_left_hot_corner, Message::TopLeftHotCorner),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Top Panel")
|
||||||
.title("Top Panel")
|
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Show Workspaces Button",
|
"Show Workspaces Button",
|
||||||
toggler(self.show_workspaces_button).on_toggle(Message::ShowWorkspacesButton),
|
toggler(
|
||||||
|
None,
|
||||||
|
self.show_workspaces_button,
|
||||||
|
Message::ShowWorkspacesButton,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Show Applications Button",
|
"Show Applications Button",
|
||||||
toggler(self.show_applications_button)
|
toggler(
|
||||||
.on_toggle(Message::ShowApplicationsButton),
|
None,
|
||||||
|
self.show_applications_button,
|
||||||
|
Message::ShowApplicationsButton,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Window Controls")
|
||||||
.title("Window Controls")
|
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Show Minimize Button",
|
"Show Minimize Button",
|
||||||
toggler(self.show_minimize_button).on_toggle(Message::ShowMinimizeButton),
|
toggler(None, self.show_minimize_button, Message::ShowMinimizeButton),
|
||||||
))
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Show Maximize Button",
|
"Show Maximize Button",
|
||||||
toggler(self.show_maximize_button).on_toggle(Message::ShowMaximizeButton),
|
toggler(None, self.show_maximize_button, Message::ShowMaximizeButton),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
])
|
])
|
||||||
|
|
@ -242,12 +245,12 @@ impl State {
|
||||||
list_column()
|
list_column()
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Same background on all displays",
|
"Same background on all displays",
|
||||||
toggler(self.same_background).on_toggle(Message::SameBackground),
|
toggler(None, self.same_background, Message::SameBackground),
|
||||||
))
|
))
|
||||||
.add(settings::item("Background fit", text("TODO")))
|
.add(settings::item("Background fit", text("TODO")))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Slideshow",
|
"Slideshow",
|
||||||
toggler(self.slideshow).on_toggle(Message::Slideshow),
|
toggler(None, self.slideshow, Message::Slideshow),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
column(image_column).spacing(16).into(),
|
column(image_column).spacing(16).into(),
|
||||||
|
|
@ -258,8 +261,7 @@ impl State {
|
||||||
fn view_desktop_workspaces<'a>(&'a self, window: &'a Window) -> Element<'a, Message> {
|
fn view_desktop_workspaces<'a>(&'a self, window: &'a Window) -> Element<'a, Message> {
|
||||||
settings::view_column(vec![
|
settings::view_column(vec![
|
||||||
window.parent_page_button(DesktopPage::Wallpaper),
|
window.parent_page_button(DesktopPage::Wallpaper),
|
||||||
settings::section()
|
settings::view_section("Workspace Behavior")
|
||||||
.title("Workspace Behavior")
|
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Dynamic workspaces",
|
"Dynamic workspaces",
|
||||||
horizontal_space(Length::Fill),
|
horizontal_space(Length::Fill),
|
||||||
|
|
@ -269,8 +271,7 @@ impl State {
|
||||||
horizontal_space(Length::Fill),
|
horizontal_space(Length::Fill),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Multi-monitor Behavior")
|
||||||
.title("Multi-monitor Behavior")
|
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Workspaces Span Displays",
|
"Workspaces Span Displays",
|
||||||
horizontal_space(Length::Fill),
|
horizontal_space(Length::Fill),
|
||||||
|
|
|
||||||
|
|
@ -69,16 +69,14 @@ impl State {
|
||||||
list_column()
|
list_column()
|
||||||
.add(settings::item("Device name", text("TODO")))
|
.add(settings::item("Device name", text("TODO")))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Hardware")
|
||||||
.title("Hardware")
|
|
||||||
.add(settings::item("Hardware model", text("TODO")))
|
.add(settings::item("Hardware model", text("TODO")))
|
||||||
.add(settings::item("Memory", text("TODO")))
|
.add(settings::item("Memory", text("TODO")))
|
||||||
.add(settings::item("Processor", text("TODO")))
|
.add(settings::item("Processor", text("TODO")))
|
||||||
.add(settings::item("Graphics", text("TODO")))
|
.add(settings::item("Graphics", text("TODO")))
|
||||||
.add(settings::item("Disk Capacity", text("TODO")))
|
.add(settings::item("Disk Capacity", text("TODO")))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Operating System")
|
||||||
.title("Operating System")
|
|
||||||
.add(settings::item("Operating system", text("TODO")))
|
.add(settings::item("Operating system", text("TODO")))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Operating system architecture",
|
"Operating system architecture",
|
||||||
|
|
@ -87,8 +85,7 @@ impl State {
|
||||||
.add(settings::item("Desktop environment", text("TODO")))
|
.add(settings::item("Desktop environment", text("TODO")))
|
||||||
.add(settings::item("Windowing system", text("TODO")))
|
.add(settings::item("Windowing system", text("TODO")))
|
||||||
.into(),
|
.into(),
|
||||||
settings::section()
|
settings::view_section("Related settings")
|
||||||
.title("Related settings")
|
|
||||||
.add(settings::item("Get support", text("TODO")))
|
.add(settings::item("Get support", text("TODO")))
|
||||||
.into(),
|
.into(),
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
/// Creates a view after each update.
|
/// Creates a view after each update.
|
||||||
fn view(&self) -> Element<'_, Self::Message> {
|
fn view(&self) -> Element<'_, Self::Message> {
|
||||||
let mut content = cosmic::widget::column::with_capacity(self.images.len()).spacing(12);
|
let mut content = cosmic::widget::column().spacing(12);
|
||||||
|
|
||||||
for (id, image) in self.images.iter().enumerate() {
|
for (id, image) in self.images.iter().enumerate() {
|
||||||
content = content.push(
|
content = content.push(
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use std::collections::HashMap;
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
|
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
|
||||||
use cosmic::iced::keyboard::Key;
|
|
||||||
use cosmic::iced::window;
|
use cosmic::iced::window;
|
||||||
use cosmic::iced::{Length, Size};
|
use cosmic::iced_core::alignment::{Horizontal, Vertical};
|
||||||
|
use cosmic::iced_core::keyboard::Key;
|
||||||
|
use cosmic::iced_core::{Length, Size};
|
||||||
use cosmic::widget::menu::action::MenuAction;
|
use cosmic::widget::menu::action::MenuAction;
|
||||||
use cosmic::widget::menu::key_bind::KeyBind;
|
use cosmic::widget::menu::key_bind::KeyBind;
|
||||||
use cosmic::widget::menu::key_bind::Modifier;
|
use cosmic::widget::menu::key_bind::Modifier;
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
app::Core,
|
app::Core,
|
||||||
iced::core::{id, Alignment, Length, Point},
|
|
||||||
iced::widget::{column, container, scrollable, text},
|
|
||||||
iced::{self, event, window, Subscription},
|
iced::{self, event, window, Subscription},
|
||||||
|
iced_core::{id, Alignment, Length, Point},
|
||||||
|
iced_widget::{column, container, scrollable, text},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{button, header_bar},
|
widget::{button, header_bar},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::Size;
|
use cosmic::iced_core::Size;
|
||||||
use cosmic::widget::{menu, nav_bar};
|
use cosmic::widget::{menu, nav_bar};
|
||||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::dialog::file_chooser::{self, FileFilter};
|
use cosmic::dialog::file_chooser::{self, FileFilter};
|
||||||
use cosmic::iced::Length;
|
use cosmic::iced_core::Length;
|
||||||
use cosmic::widget::button;
|
use cosmic::widget::button;
|
||||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -207,7 +207,7 @@ impl cosmic::Application for App {
|
||||||
);
|
);
|
||||||
|
|
||||||
content.push(
|
content.push(
|
||||||
iced::widget::space::vertical()
|
iced::widget::vertical_space()
|
||||||
.height(Length::Fixed(12.0))
|
.height(Length::Fixed(12.0))
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
/// Creates a view after each update.
|
/// Creates a view after each update.
|
||||||
fn view(&self) -> Element<'_, Self::Message> {
|
fn view(&self) -> Element<'_, Self::Message> {
|
||||||
widget::Row::new().into()
|
widget::row().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::Size;
|
use cosmic::iced_core::Size;
|
||||||
use cosmic::prelude::*;
|
use cosmic::prelude::*;
|
||||||
use cosmic::widget::table;
|
use cosmic::widget::table;
|
||||||
use cosmic::widget::{self, nav_bar};
|
use cosmic::widget::{self, nav_bar};
|
||||||
|
|
|
||||||
|
|
@ -99,9 +99,7 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
let inline = cosmic::widget::inline_input("", &self.input).on_input(Message::Input);
|
let inline = cosmic::widget::inline_input("", &self.input).on_input(Message::Input);
|
||||||
|
|
||||||
let column = cosmic::widget::column::with_capacity(2)
|
let column = cosmic::widget::column().push(editable).push(inline);
|
||||||
.push(editable)
|
|
||||||
.push(inline);
|
|
||||||
|
|
||||||
let centered = cosmic::widget::container(column.width(200))
|
let centered = cosmic::widget::container(column.width(200))
|
||||||
.width(iced::Length::Fill)
|
.width(iced::Length::Fill)
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,3 @@ thursday = الخميس
|
||||||
friday = الجمعة
|
friday = الجمعة
|
||||||
saturday = السبت
|
saturday = السبت
|
||||||
sunday = الأحد
|
sunday = الأحد
|
||||||
mon = ن
|
|
||||||
tue = ث
|
|
||||||
wed = ر
|
|
||||||
thu = خ
|
|
||||||
fri = ج
|
|
||||||
sat = س
|
|
||||||
sun = ح
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ designers = Designéři
|
||||||
artists = Grafici
|
artists = Grafici
|
||||||
translators = Překladatelé
|
translators = Překladatelé
|
||||||
documenters = Tvůrci dokumentace
|
documenters = Tvůrci dokumentace
|
||||||
sunday = Neděle
|
sunday = Ne
|
||||||
january = Leden { $year }
|
january = Leden { $year }
|
||||||
february = Únor { $year }
|
february = Únor { $year }
|
||||||
march = Březen { $year }
|
march = Březen { $year }
|
||||||
|
|
@ -21,16 +21,9 @@ september = Září { $year }
|
||||||
october = Říjen { $year }
|
october = Říjen { $year }
|
||||||
november = Listopad { $year }
|
november = Listopad { $year }
|
||||||
december = Prosinec { $year }
|
december = Prosinec { $year }
|
||||||
monday = Pondělí
|
monday = Po
|
||||||
tuesday = Úterý
|
tuesday = Út
|
||||||
wednesday = Středa
|
wednesday = St
|
||||||
thursday = Čtvrtek
|
thursday = Čt
|
||||||
friday = Pátek
|
friday = Pá
|
||||||
saturday = Sobota
|
saturday = So
|
||||||
mon = Po
|
|
||||||
tue = Út
|
|
||||||
wed = St
|
|
||||||
thu = Čt
|
|
||||||
fri = Pá
|
|
||||||
sat = So
|
|
||||||
sun = Ne
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ close = Schließen
|
||||||
# About
|
# About
|
||||||
license = Lizenz
|
license = Lizenz
|
||||||
links = Links
|
links = Links
|
||||||
developers = Entwickler(innen)
|
developers = Entwickler*innen
|
||||||
designers = Designer(innen)
|
designers = Designer*innen
|
||||||
artists = Künstler(innen)
|
artists = Künstler*innen
|
||||||
translators = Übersetzer(innen)
|
translators = Übersetzer*innen
|
||||||
documenters = Dokumentierer(innen)
|
documenters = Dokumentierer*innen
|
||||||
# Calendar
|
# Calendar
|
||||||
january = Januar { $year }
|
january = Januar { $year }
|
||||||
february = Februar { $year }
|
february = Februar { $year }
|
||||||
|
|
@ -21,17 +21,10 @@ september = September { $year }
|
||||||
october = Oktober { $year }
|
october = Oktober { $year }
|
||||||
november = November { $year }
|
november = November { $year }
|
||||||
december = Dezember { $year }
|
december = Dezember { $year }
|
||||||
monday = Montag
|
monday = Mo
|
||||||
tuesday = Dienstag
|
tuesday = Di
|
||||||
wednesday = Mittwoch
|
wednesday = Mi
|
||||||
thursday = Donnerstag
|
thursday = Do
|
||||||
friday = Freitag
|
friday = Fr
|
||||||
saturday = Samstag
|
saturday = Sa
|
||||||
sunday = Sonntag
|
sunday = So
|
||||||
wed = Mi
|
|
||||||
thu = Do
|
|
||||||
fri = Fr
|
|
||||||
sat = Sa
|
|
||||||
sun = So
|
|
||||||
tue = Di
|
|
||||||
mon = Mo
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
monday = Maanantai
|
|
||||||
mon = ma
|
|
||||||
tuesday = Tiistai
|
|
||||||
tue = ti
|
|
||||||
wednesday = Keskiviikko
|
|
||||||
wed = ke
|
|
||||||
thursday = Torstai
|
|
||||||
thu = to
|
|
||||||
friday = Perjantai
|
|
||||||
fri = pe
|
|
||||||
saturday = Lauantai
|
|
||||||
sat = la
|
|
||||||
sunday = Sunnuntai
|
|
||||||
sun = su
|
|
||||||
close = Sulje
|
|
||||||
license = Lisenssi
|
|
||||||
links = Linkit
|
|
||||||
developers = Kehittäjät
|
|
||||||
designers = Suunnittelijat
|
|
||||||
artists = Artistit
|
|
||||||
translators = Kääntäjät
|
|
||||||
documenters = Dokumentoijat
|
|
||||||
january = Tammikuu { $year }
|
|
||||||
february = Helmikuu { $year }
|
|
||||||
march = Maaliskuu { $year }
|
|
||||||
april = Huhtikuu { $year }
|
|
||||||
may = Toukokuu { $year }
|
|
||||||
june = Kesäkuu { $year }
|
|
||||||
july = Heinäkuu { $year }
|
|
||||||
august = Elokuu { $year }
|
|
||||||
september = Syyskuu { $year }
|
|
||||||
october = Lokakuu { $year }
|
|
||||||
november = Marraskuu { $year }
|
|
||||||
december = Joulukuu { $year }
|
|
||||||
|
|
@ -10,25 +10,18 @@ february = Février { $year }
|
||||||
april = Avril { $year }
|
april = Avril { $year }
|
||||||
march = Mars { $year }
|
march = Mars { $year }
|
||||||
november = Novembre { $year }
|
november = Novembre { $year }
|
||||||
friday = Vendredi
|
friday = Ven
|
||||||
tuesday = Mardi
|
tuesday = Mar
|
||||||
may = Mai { $year }
|
may = Mai { $year }
|
||||||
wednesday = Mercredi
|
wednesday = Mer
|
||||||
monday = Lundi
|
monday = Lun
|
||||||
december = Décembre { $year }
|
december = Décembre { $year }
|
||||||
sunday = Dimanche
|
sunday = Dim
|
||||||
june = Juin { $year }
|
june = Juin { $year }
|
||||||
saturday = Samedi
|
saturday = Sam
|
||||||
august = Août { $year }
|
august = Août { $year }
|
||||||
july = Juillet { $year }
|
july = Juillet { $year }
|
||||||
thursday = Jeudi
|
thursday = Jeu
|
||||||
september = Septembre { $year }
|
september = Septembre { $year }
|
||||||
october = Octobre { $year }
|
october = Octobre { $year }
|
||||||
designers = Designers
|
designers = Designers
|
||||||
mon = Lun
|
|
||||||
tue = Mar
|
|
||||||
wed = Mer
|
|
||||||
thu = Jeu
|
|
||||||
fri = Ven
|
|
||||||
sat = Sam
|
|
||||||
sun = Dim
|
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,10 @@ september = Meán Fómhair { $year }
|
||||||
october = Deireadh Fómhair { $year }
|
october = Deireadh Fómhair { $year }
|
||||||
november = Samhain { $year }
|
november = Samhain { $year }
|
||||||
december = Nollaig { $year }
|
december = Nollaig { $year }
|
||||||
monday = Dé Luain
|
monday = Lua
|
||||||
tuesday = Dé Máirt
|
tuesday = Mái
|
||||||
wednesday = Dé Céadaoin
|
wednesday = Céa
|
||||||
thursday = Déardaoin
|
thursday = Déa
|
||||||
friday = Dé hAoine
|
friday = Aoi
|
||||||
saturday = Dé Sathairn
|
saturday = Sat
|
||||||
sunday = Dé Domhnaigh
|
sunday = Dom
|
||||||
mon = Lua
|
|
||||||
tue = Mái
|
|
||||||
wed = Céa
|
|
||||||
thu = Déa
|
|
||||||
fri = Aoi
|
|
||||||
sat = Sat
|
|
||||||
sun = Dom
|
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,10 @@ september = { $year } szeptember
|
||||||
october = { $year } október
|
october = { $year } október
|
||||||
november = { $year } november
|
november = { $year } november
|
||||||
december = { $year } december
|
december = { $year } december
|
||||||
monday = Hétfő
|
monday = H
|
||||||
tuesday = Kedd
|
tuesday = K
|
||||||
wednesday = Szerda
|
wednesday = Sze
|
||||||
thursday = Csütörtök
|
thursday = Cs
|
||||||
friday = Péntek
|
friday = P
|
||||||
saturday = Szombat
|
saturday = Szo
|
||||||
sunday = Vasárnap
|
sunday = V
|
||||||
mon = H
|
|
||||||
tue = K
|
|
||||||
wed = Sze
|
|
||||||
thu = Cs
|
|
||||||
fri = P
|
|
||||||
sat = Szo
|
|
||||||
sun = V
|
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,10 @@ september = September { $year }
|
||||||
october = Oktober { $year }
|
october = Oktober { $year }
|
||||||
november = November { $year }
|
november = November { $year }
|
||||||
december = Desember { $year }
|
december = Desember { $year }
|
||||||
monday = Senin
|
monday = Sen
|
||||||
tuesday = Selasa
|
tuesday = Sel
|
||||||
wednesday = Rabu
|
wednesday = Rab
|
||||||
sunday = Minggu
|
sunday = Min
|
||||||
saturday = Sabtu
|
saturday = Sab
|
||||||
friday = Jum'at
|
friday = Jum
|
||||||
thursday = Kamis
|
thursday = Kam
|
||||||
mon = Sen
|
|
||||||
tue = Sel
|
|
||||||
wed = Rab
|
|
||||||
thu = Kam
|
|
||||||
fri = Jum
|
|
||||||
sat = Sab
|
|
||||||
sun = Min
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
close = Mdel
|
|
||||||
license = Turagt
|
|
||||||
links = Iseɣwan
|
|
||||||
developers = Ineflayen
|
|
||||||
artists = Inaẓuren
|
|
||||||
translators = Imsuqlen
|
|
||||||
january = Yennayer { $year }
|
|
||||||
february = Fuṛar { $year }
|
|
||||||
march = Meɣres { $year }
|
|
||||||
april = Yebrir { $year }
|
|
||||||
may = Mayyu { $year }
|
|
||||||
june = Yunyu { $year }
|
|
||||||
july = Yulyu { $year }
|
|
||||||
august = Ɣuct { $year }
|
|
||||||
september = Ctembeṛ { $year }
|
|
||||||
october = Tubeṛ { $year }
|
|
||||||
november = Wambeṛ { $year }
|
|
||||||
december = Dujembeṛ { $year }
|
|
||||||
documenters = Imeskaren
|
|
||||||
monday = Arim
|
|
||||||
mon = Ari
|
|
||||||
tuesday = Aram
|
|
||||||
tue = Ara
|
|
||||||
wednesday = Ahad
|
|
||||||
wed = Aha
|
|
||||||
thursday = Amhad
|
|
||||||
thu = Amh
|
|
||||||
friday = Sem
|
|
||||||
fri = Sm
|
|
||||||
saturday = Sed
|
|
||||||
sat = Sd
|
|
||||||
sunday = Acer
|
|
||||||
sun = Ace
|
|
||||||
|
|
@ -18,17 +18,10 @@ september = Қыркүйек { $year }
|
||||||
october = Қазан { $year }
|
october = Қазан { $year }
|
||||||
november = Қараша { $year }
|
november = Қараша { $year }
|
||||||
december = Желтоқсан { $year }
|
december = Желтоқсан { $year }
|
||||||
monday = Дүйсенбі
|
monday = Дс
|
||||||
tuesday = Сейсенбі
|
tuesday = Сс
|
||||||
wednesday = Сәрсенбі
|
wednesday = Ср
|
||||||
thursday = Бейсенбі
|
thursday = Бс
|
||||||
friday = Жұма
|
friday = Жм
|
||||||
saturday = Сенбі
|
saturday = Сб
|
||||||
sunday = Жексенбі
|
sunday = Жс
|
||||||
mon = Дс
|
|
||||||
tue = Сс
|
|
||||||
wed = Ср
|
|
||||||
thu = Бс
|
|
||||||
fri = Жм
|
|
||||||
sat = Сн
|
|
||||||
sun = Жк
|
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,26 @@ february = { $year }년 2월
|
||||||
close = 닫기
|
close = 닫기
|
||||||
documenters = 문서 작성자
|
documenters = 문서 작성자
|
||||||
november = { $year }년 11월
|
november = { $year }년 11월
|
||||||
friday = 금요일
|
friday = 금
|
||||||
tuesday = 화요일
|
tuesday = 화
|
||||||
may = { $year }년 5월
|
may = { $year }년 5월
|
||||||
wednesday = 수요일
|
wednesday = 수
|
||||||
april = { $year }년 4월
|
april = { $year }년 4월
|
||||||
monday = 월요일
|
monday = 월
|
||||||
translators = 번역가
|
translators = 번역가
|
||||||
artists = 아티스트
|
artists = 아티스트
|
||||||
license = 라이선스
|
license = 라이선스
|
||||||
december = { $year }년 12월
|
december = { $year }년 12월
|
||||||
sunday = 일요일
|
sunday = 일
|
||||||
links = 링크
|
links = 링크
|
||||||
march = { $year }년 3월
|
march = { $year }년 3월
|
||||||
june = { $year }년 6월
|
june = { $year }년 6월
|
||||||
saturday = 토요일
|
saturday = 토
|
||||||
august = { $year }년 8월
|
august = { $year }년 8월
|
||||||
developers = 개발자
|
developers = 개발자
|
||||||
july = { $year }년 7월
|
july = { $year }년 7월
|
||||||
thursday = 목요일
|
thursday = 목
|
||||||
september = { $year }년 9월
|
september = { $year }년 9월
|
||||||
designers = 디자이너
|
designers = 디자이너
|
||||||
october = { $year }년 10월
|
october = { $year }년 10월
|
||||||
january = { $year }년 1월
|
january = { $year }년 1월
|
||||||
mon = 월
|
|
||||||
tue = 화
|
|
||||||
wed = 수
|
|
||||||
thu = 목
|
|
||||||
fri = 금
|
|
||||||
sat = 토
|
|
||||||
sun = 일
|
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,26 @@ february = Vasaris { $year }
|
||||||
close = Uždaryti
|
close = Uždaryti
|
||||||
documenters = Dokumentuotojai
|
documenters = Dokumentuotojai
|
||||||
november = Lapkritis { $year }
|
november = Lapkritis { $year }
|
||||||
friday = Penktadienis
|
friday = Penk
|
||||||
tuesday = Antradienis
|
tuesday = Antr
|
||||||
may = Gegužė { $year }
|
may = Gegužė { $year }
|
||||||
wednesday = Trečiadienis
|
wednesday = Treč
|
||||||
april = Balandis { $year }
|
april = Balandis { $year }
|
||||||
monday = Pirmadienis
|
monday = Pirm
|
||||||
translators = Vertėjai
|
translators = Vertėjai
|
||||||
artists = Menininkai
|
artists = Menininkai
|
||||||
license = Licencija
|
license = Licencija
|
||||||
december = Gruodis { $year }
|
december = Gruodis { $year }
|
||||||
sunday = Sekmadienis
|
sunday = Sekm
|
||||||
links = Nuorodos
|
links = Nuorodos
|
||||||
march = Kovas { $year }
|
march = Kovas { $year }
|
||||||
june = Birželis { $year }
|
june = Birželis { $year }
|
||||||
saturday = Šeštadienis
|
saturday = Šešt
|
||||||
august = Rugpjūtis { $year }
|
august = Rugpjūtis { $year }
|
||||||
developers = Kūrėjai
|
developers = Kūrėjai
|
||||||
july = Liepa { $year }
|
july = Liepa { $year }
|
||||||
thursday = Ketvirtadienis
|
thursday = Ketv
|
||||||
september = Rugsėjis { $year }
|
september = Rugsėjis { $year }
|
||||||
designers = Dizaineriai
|
designers = Dizaineriai
|
||||||
october = Spalis { $year }
|
october = Spalis { $year }
|
||||||
january = Sausis { $year }
|
january = Sausis { $year }
|
||||||
mon = Pirm
|
|
||||||
tue = Antr
|
|
||||||
wed = Treč
|
|
||||||
thu = Ketv
|
|
||||||
fri = Penkt
|
|
||||||
sat = Šešt
|
|
||||||
sun = Sekm
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,4 @@ wednesday = Woe
|
||||||
thursday = Do
|
thursday = Do
|
||||||
friday = Vrij
|
friday = Vrij
|
||||||
saturday = Za
|
saturday = Za
|
||||||
sunday = Zo
|
sunday = Zon
|
||||||
links = Links
|
|
||||||
developers = Ontwikkeling
|
|
||||||
designers = Ontwerp
|
|
||||||
translators = Vertaling
|
|
||||||
documenters = Documentatie
|
|
||||||
artists = Vormgeving
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
close = ਬੰਦ ਕਰੋ
|
|
||||||
license = ਲਸੰਸ
|
|
||||||
links = ਲਿੰਕ
|
|
||||||
developers = ਡਿਵੈਲਪਰ
|
|
||||||
designers = ਡਿਜ਼ਾਇਨਰ
|
|
||||||
artists = ਕਲਾਕਾਰ
|
|
||||||
translators = ਅਨੁਵਾਦਕ
|
|
||||||
documenters = ਦਸਤਾਵੇਜ਼ ਤਿਆਰ ਕਰਤਾ
|
|
||||||
january = ਜਨਵਰੀ { $year }
|
|
||||||
february = ਫਰਵਰੀ { $year }
|
|
||||||
march = ਮਾਰਚ { $year }
|
|
||||||
april = ਅਪਰੈਲ { $year }
|
|
||||||
may = ਮਈ { $year }
|
|
||||||
june = ਜੂਨ { $year }
|
|
||||||
july = ਜੁਲਾਈ { $year }
|
|
||||||
august = ਅਗਸਤ { $year }
|
|
||||||
september = ਸਤੰਬਰ { $year }
|
|
||||||
october = ਅਕਤੂਬਰ { $year }
|
|
||||||
november = ਨਵੰਬਰ { $year }
|
|
||||||
december = ਦਸੰਬਰ { $year }
|
|
||||||
monday = ਸੋਮਵਾਰ
|
|
||||||
mon = ਸੋਮ
|
|
||||||
tuesday = ਮੰਗਲਵਾਰ
|
|
||||||
tue = ਮੰਗਲ
|
|
||||||
wednesday = ਬੁੱਧਵਾਰ
|
|
||||||
wed = ਬੁੱਧ
|
|
||||||
thursday = ਵੀਰਵਾਰ
|
|
||||||
thu = ਵੀਰ
|
|
||||||
friday = ਸ਼ੁੱਕਰਵਾਰ
|
|
||||||
fri = ਸ਼ੁੱਕਰ
|
|
||||||
saturday = ਸ਼ਨਿੱਚਰਵਾਰ
|
|
||||||
sat = ਸ਼ਨਿੱਚਰ
|
|
||||||
sunday = ਐਤਵਾਰ
|
|
||||||
sun = ਐਤ
|
|
||||||
|
|
@ -20,17 +20,10 @@ september = Wrzesień { $year }
|
||||||
october = Październik { $year }
|
october = Październik { $year }
|
||||||
november = Listopad { $year }
|
november = Listopad { $year }
|
||||||
december = Grudzień { $year }
|
december = Grudzień { $year }
|
||||||
monday = Poniedziałek
|
monday = Pon
|
||||||
tuesday = Wtorek
|
tuesday = Wto
|
||||||
wednesday = Środa
|
wednesday = Śro
|
||||||
thursday = Czwartek
|
thursday = Czw
|
||||||
friday = Piątek
|
friday = Pią
|
||||||
saturday = Sobota
|
saturday = Sob
|
||||||
sunday = Niedziela
|
sunday = Nie
|
||||||
mon = Pon
|
|
||||||
tue = Wto
|
|
||||||
wed = Śro
|
|
||||||
thu = Czw
|
|
||||||
fri = Pia
|
|
||||||
sat = Sob
|
|
||||||
sun = Nie
|
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,10 @@ september = Setembro de { $year }
|
||||||
october = Outubro de { $year }
|
october = Outubro de { $year }
|
||||||
november = Novembro de { $year }
|
november = Novembro de { $year }
|
||||||
december = Dezembro de { $year }
|
december = Dezembro de { $year }
|
||||||
monday = Segunda-feira
|
monday = Seg
|
||||||
tuesday = Terça-feira
|
tuesday = Ter
|
||||||
wednesday = Quarta-feira
|
wednesday = Qua
|
||||||
thursday = Quinta-feira
|
thursday = Qui
|
||||||
friday = Sexta-feira
|
friday = Sex
|
||||||
saturday = Sábado
|
saturday = Sáb
|
||||||
sunday = Domingo
|
sunday = Dom
|
||||||
mon = Seg
|
|
||||||
tue = Ter
|
|
||||||
wed = Qua
|
|
||||||
thu = Qui
|
|
||||||
fri = Sex
|
|
||||||
sat = Sáb
|
|
||||||
sun = Dom
|
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,10 @@ september = Сентябрь { $year }
|
||||||
october = Октябрь { $year }
|
october = Октябрь { $year }
|
||||||
november = Ноябрь { $year }
|
november = Ноябрь { $year }
|
||||||
december = Декабрь { $year }
|
december = Декабрь { $year }
|
||||||
monday = Понедельник
|
monday = Пн
|
||||||
tuesday = Вторник
|
tuesday = Вт
|
||||||
wednesday = Среда
|
wednesday = Ср
|
||||||
thursday = Четверг
|
thursday = Чт
|
||||||
friday = Пятница
|
friday = Пт
|
||||||
saturday = Суббота
|
saturday = Сб
|
||||||
sunday = Воскресенье
|
sunday = Вс
|
||||||
mon = Пн
|
|
||||||
tue = Вт
|
|
||||||
wed = Ср
|
|
||||||
thu = Чт
|
|
||||||
fri = Пт
|
|
||||||
sat = Сб
|
|
||||||
sun = Вс
|
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,10 @@ september = September { $year }
|
||||||
october = Oktober { $year }
|
october = Oktober { $year }
|
||||||
november = November { $year }
|
november = November { $year }
|
||||||
december = December { $year }
|
december = December { $year }
|
||||||
monday = Måndag
|
monday = Mån
|
||||||
tuesday = Tisdag
|
tuesday = Tis
|
||||||
wednesday = Onsdag
|
wednesday = Ons
|
||||||
thursday = Torsdag
|
thursday = Tor
|
||||||
friday = Fredag
|
friday = Fre
|
||||||
saturday = Lördag
|
saturday = Lör
|
||||||
sunday = Söndag
|
sunday = Sön
|
||||||
sun = Sön
|
|
||||||
mon = Mån
|
|
||||||
tue = Tis
|
|
||||||
wed = Ons
|
|
||||||
thu = Tor
|
|
||||||
fri = Fre
|
|
||||||
sat = Lör
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# Context Drawer
|
# Context Drawer
|
||||||
close = Kapat
|
close = Kapat
|
||||||
|
|
||||||
# About
|
# About
|
||||||
license = Lisans
|
license = Lisans
|
||||||
links = Bağlantılar
|
links = Bağlantılar
|
||||||
|
|
@ -8,29 +9,3 @@ designers = Tasarımcılar
|
||||||
artists = Sanatçılar
|
artists = Sanatçılar
|
||||||
translators = Çevirmenler
|
translators = Çevirmenler
|
||||||
documenters = Belgelendiriciler
|
documenters = Belgelendiriciler
|
||||||
january = Ocak { $year }
|
|
||||||
february = Şubat { $year }
|
|
||||||
march = Mart { $year }
|
|
||||||
april = Nisan { $year }
|
|
||||||
may = Mayıs { $year }
|
|
||||||
june = Haziran { $year }
|
|
||||||
july = Temmuz { $year }
|
|
||||||
august = Ağustos { $year }
|
|
||||||
september = Eylül { $year }
|
|
||||||
october = Ekim { $year }
|
|
||||||
november = Kasım { $year }
|
|
||||||
december = Aralık { $year }
|
|
||||||
monday = Pazartesi
|
|
||||||
mon = Pzt
|
|
||||||
tuesday = Salı
|
|
||||||
tue = Sal
|
|
||||||
wednesday = Çarşamba
|
|
||||||
wed = Çar
|
|
||||||
thursday = Perşembe
|
|
||||||
thu = Per
|
|
||||||
friday = Cuma
|
|
||||||
fri = Cum
|
|
||||||
saturday = Cumartesi
|
|
||||||
sat = Cmt
|
|
||||||
sunday = Pazar
|
|
||||||
sun = Paz
|
|
||||||
|
|
|
||||||
|
|
@ -10,27 +10,20 @@ translators = Перекладачі
|
||||||
documenters = Документатори
|
documenters = Документатори
|
||||||
february = Лютий { $year }
|
february = Лютий { $year }
|
||||||
november = Листопад { $year }
|
november = Листопад { $year }
|
||||||
friday = П'ятниця
|
friday = Пт
|
||||||
tuesday = Вівторок
|
tuesday = Вт
|
||||||
may = Травень { $year }
|
may = Травень { $year }
|
||||||
wednesday = Середа
|
wednesday = Ср
|
||||||
april = Квітень { $year }
|
april = Квітень { $year }
|
||||||
monday = Понеділок
|
monday = Пн
|
||||||
december = Грудень { $year }
|
december = Грудень { $year }
|
||||||
sunday = Неділя
|
sunday = Нд
|
||||||
march = Березень { $year }
|
march = Березень { $year }
|
||||||
june = Червень { $year }
|
june = Червень { $year }
|
||||||
saturday = Субота
|
saturday = Сб
|
||||||
august = Серпень { $year }
|
august = Серпень { $year }
|
||||||
july = Липень { $year }
|
july = Липень { $year }
|
||||||
thursday = Четвер
|
thursday = Чт
|
||||||
september = Вересень { $year }
|
september = Вересень { $year }
|
||||||
october = Жовтень { $year }
|
october = Жовтень { $year }
|
||||||
january = Січень { $year }
|
january = Січень { $year }
|
||||||
mon = Пн
|
|
||||||
tue = Вт
|
|
||||||
wed = Ср
|
|
||||||
thu = Чт
|
|
||||||
fri = Пт
|
|
||||||
sat = Cб
|
|
||||||
sun = Нд
|
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,12 @@ september = { $year }年9月
|
||||||
october = { $year }年10月
|
october = { $year }年10月
|
||||||
november = { $year }年11月
|
november = { $year }年11月
|
||||||
december = { $year }年12月
|
december = { $year }年12月
|
||||||
monday = 星期一
|
monday = 周一
|
||||||
tuesday = 星期二
|
tuesday = 周二
|
||||||
wednesday = 星期三
|
wednesday = 周三
|
||||||
thursday = 星期四
|
thursday = 周四
|
||||||
friday = 星期五
|
friday = 周五
|
||||||
saturday = 星期六
|
saturday = 周六
|
||||||
sunday = 星期日
|
sunday = 周日
|
||||||
artists = 艺术家
|
artists = 艺术家
|
||||||
documenters = 文档作者
|
documenters = 文档作者
|
||||||
mon = 周一
|
|
||||||
tue = 周二
|
|
||||||
wed = 周三
|
|
||||||
thu = 周四
|
|
||||||
fri = 周五
|
|
||||||
sat = 周六
|
|
||||||
sun = 周日
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
close = 關閉
|
|
||||||
developers = 開發人員
|
|
||||||
designers = 設計人員
|
|
||||||
artists = 美編設計
|
|
||||||
translators = 翻譯人員
|
|
||||||
documenters = 文件編輯人員
|
|
||||||
january = { $year } 年 1 月
|
|
||||||
monday = 星期一
|
|
||||||
tuesday = 星期二
|
|
||||||
wednesday = 星期三
|
|
||||||
thursday = 星期四
|
|
||||||
friday = 星期五
|
|
||||||
saturday = 星期六
|
|
||||||
sunday = 星期日
|
|
||||||
mon = 週一
|
|
||||||
tue = 週二
|
|
||||||
wed = 週三
|
|
||||||
thu = 週四
|
|
||||||
fri = 週五
|
|
||||||
sat = 週六
|
|
||||||
sun = 週日
|
|
||||||
license = 授權
|
|
||||||
links = 連結
|
|
||||||
february = { $year } 年 2 月
|
|
||||||
march = { $year } 年 3 月
|
|
||||||
april = { $year } 年 4 月
|
|
||||||
may = { $year } 年 5 月
|
|
||||||
june = { $year } 年 6 月
|
|
||||||
july = { $year } 年 7 月
|
|
||||||
august = { $year } 年 8 月
|
|
||||||
september = { $year } 年 9 月
|
|
||||||
october = { $year } 年 10 月
|
|
||||||
november = { $year } 年 11 月
|
|
||||||
december = { $year } 年 12 月
|
|
||||||
2
iced
2
iced
|
|
@ -1 +1 @@
|
||||||
Subproject commit 78caabba7ef91cd1030da6f70b41d266704ffece
|
Subproject commit e2a2441789a7e302f099c0e8e9493ef81b58e265
|
||||||
51
src/anim.rs
51
src/anim.rs
|
|
@ -1,51 +0,0 @@
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
/// A simple linear interpolation calculation function.
|
|
||||||
/// p = `percent_complete` in decimal form
|
|
||||||
#[must_use]
|
|
||||||
pub fn lerp(start: f32, end: f32, p: f32) -> f32 {
|
|
||||||
(1.0 - p) * start + p * end
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A fast smooth interpolation calculation function.
|
|
||||||
/// p = `percent_complete` in decimal form
|
|
||||||
#[must_use]
|
|
||||||
pub fn slerp(start: f32, end: f32, p: f32) -> f32 {
|
|
||||||
let t = smootherstep(p);
|
|
||||||
(1.0 - t) * start + t * end
|
|
||||||
}
|
|
||||||
|
|
||||||
/// utility function which maps a value [0, 1] -> [0, 1] using the smootherstep function
|
|
||||||
pub fn smootherstep(t: f32) -> f32 {
|
|
||||||
(6.0 * t.powi(5) - 15.0 * t.powi(4) + 10.0 * t.powi(3)).clamp(0.0, 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct State {
|
|
||||||
pub last_change: Option<Instant>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub fn changed(&mut self, dur: Duration) {
|
|
||||||
let t = self.t(dur, false);
|
|
||||||
let diff = dur.mul_f32(t.abs());
|
|
||||||
let now = Instant::now();
|
|
||||||
self.last_change = Some(now.checked_sub(diff).unwrap_or(now));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn anim_done(&mut self, dur: Duration) {
|
|
||||||
if self
|
|
||||||
.last_change
|
|
||||||
.is_some_and(|t| Instant::now().duration_since(t) > dur)
|
|
||||||
{
|
|
||||||
self.last_change = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn t(&self, dur: Duration, forward: bool) -> f32 {
|
|
||||||
let res = self.last_change.map_or(1., |t| {
|
|
||||||
Instant::now().duration_since(t).as_millis() as f32 / dur.as_millis() as f32
|
|
||||||
});
|
|
||||||
if forward { res } else { 1. - res }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,9 +5,11 @@ use crate::surface;
|
||||||
use crate::theme::Theme;
|
use crate::theme::Theme;
|
||||||
use crate::widget::nav_bar;
|
use crate::widget::nav_bar;
|
||||||
use crate::{config::CosmicTk, keyboard_nav};
|
use crate::{config::CosmicTk, keyboard_nav};
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
|
use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
|
||||||
use cosmic_theme::ThemeMode;
|
use cosmic_theme::ThemeMode;
|
||||||
|
#[cfg(not(any(feature = "multi-window", feature = "wayland")))]
|
||||||
|
use iced::Application as IcedApplication;
|
||||||
|
|
||||||
/// A message managed internally by COSMIC.
|
/// A message managed internally by COSMIC.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -69,10 +71,10 @@ pub enum Action {
|
||||||
/// Updates the tracked window geometry.
|
/// Updates the tracked window geometry.
|
||||||
WindowResize(iced::window::Id, f32, f32),
|
WindowResize(iced::window::Id, f32, f32),
|
||||||
/// Tracks updates to window state.
|
/// Tracks updates to window state.
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
WindowState(iced::window::Id, WindowState),
|
WindowState(iced::window::Id, WindowState),
|
||||||
/// Capabilities the window manager supports
|
/// Capabilities the window manager supports
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
WmCapabilities(iced::window::Id, WindowManagerCapabilities),
|
WmCapabilities(iced::window::Id, WindowManagerCapabilities),
|
||||||
#[cfg(feature = "xdg-portal")]
|
#[cfg(feature = "xdg-portal")]
|
||||||
DesktopSettings(crate::theme::portal::Desktop),
|
DesktopSettings(crate::theme::portal::Desktop),
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ use std::sync::Arc;
|
||||||
use super::{Action, Application, ApplicationExt, Subscription};
|
use super::{Action, Application, ApplicationExt, Subscription};
|
||||||
use crate::theme::{THEME, Theme, ThemeType};
|
use crate::theme::{THEME, Theme, ThemeType};
|
||||||
use crate::{Core, Element, keyboard_nav};
|
use crate::{Core, Element, keyboard_nav};
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
|
use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
|
||||||
use cosmic_theme::ThemeMode;
|
use cosmic_theme::ThemeMode;
|
||||||
#[cfg(not(any(feature = "multi-window", feature = "wayland", target_os = "linux")))]
|
#[cfg(not(any(feature = "multi-window", feature = "wayland")))]
|
||||||
use iced::Application as IcedApplication;
|
use iced::Application as IcedApplication;
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
use iced::event::wayland;
|
use iced::event::wayland;
|
||||||
use iced::{Task, theme, window};
|
use iced::{Task, window};
|
||||||
use iced_futures::event::listen_with;
|
use iced_futures::event::listen_with;
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
use iced_winit::SurfaceIdWrapper;
|
use iced_winit::SurfaceIdWrapper;
|
||||||
use palette::color_difference::EuclideanDistance;
|
use palette::color_difference::EuclideanDistance;
|
||||||
|
|
||||||
|
|
@ -49,8 +49,8 @@ pub fn windowing_system() -> Option<WindowingSystem> {
|
||||||
WINDOWING_SYSTEM.get().copied()
|
WINDOWING_SYSTEM.get().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_windowing_system<M>(handle: window::raw_window_handle::WindowHandle) -> crate::Action<M> {
|
fn init_windowing_system<M>(handle: raw_window_handle::WindowHandle) -> crate::Action<M> {
|
||||||
let raw = handle.as_ref();
|
let raw: &raw_window_handle::RawWindowHandle = handle.as_ref();
|
||||||
let system = match raw {
|
let system = match raw {
|
||||||
window::raw_window_handle::RawWindowHandle::UiKit(_) => WindowingSystem::UiKit,
|
window::raw_window_handle::RawWindowHandle::UiKit(_) => WindowingSystem::UiKit,
|
||||||
window::raw_window_handle::RawWindowHandle::AppKit(_) => WindowingSystem::AppKit,
|
window::raw_window_handle::RawWindowHandle::AppKit(_) => WindowingSystem::AppKit,
|
||||||
|
|
@ -83,7 +83,7 @@ fn init_windowing_system<M>(handle: window::raw_window_handle::WindowHandle) ->
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Cosmic<App: Application> {
|
pub struct Cosmic<App: Application> {
|
||||||
pub app: App,
|
pub app: App,
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
pub surface_views: HashMap<
|
pub surface_views: HashMap<
|
||||||
window::Id,
|
window::Id,
|
||||||
(
|
(
|
||||||
|
|
@ -138,7 +138,7 @@ where
|
||||||
) -> iced::Task<crate::Action<T::Message>> {
|
) -> iced::Task<crate::Action<T::Message>> {
|
||||||
#[cfg(feature = "surface-message")]
|
#[cfg(feature = "surface-message")]
|
||||||
match _surface_message {
|
match _surface_message {
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::AppSubsurface(settings, view) => {
|
crate::surface::Action::AppSubsurface(settings, view) => {
|
||||||
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -168,7 +168,7 @@ where
|
||||||
iced_winit::commands::subsurface::get_subsurface(settings(&mut self.app))
|
iced_winit::commands::subsurface::get_subsurface(settings(&mut self.app))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::Subsurface(settings, view) => {
|
crate::surface::Action::Subsurface(settings, view) => {
|
||||||
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -196,7 +196,7 @@ where
|
||||||
iced_winit::commands::subsurface::get_subsurface(settings())
|
iced_winit::commands::subsurface::get_subsurface(settings())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::AppPopup(settings, view) => {
|
crate::surface::Action::AppPopup(settings, view) => {
|
||||||
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -225,26 +225,15 @@ where
|
||||||
iced_winit::commands::popup::get_popup(settings(&mut self.app))
|
iced_winit::commands::popup::get_popup(settings(&mut self.app))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::DestroyPopup(id) => {
|
crate::surface::Action::DestroyPopup(id) => {
|
||||||
iced_winit::commands::popup::destroy_popup(id)
|
iced_winit::commands::popup::destroy_popup(id)
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::DestroyTooltipPopup => {
|
|
||||||
#[cfg(feature = "applet")]
|
|
||||||
{
|
|
||||||
iced_winit::commands::popup::destroy_popup(*crate::applet::TOOLTIP_WINDOW_ID)
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "applet"))]
|
|
||||||
{
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
|
||||||
crate::surface::Action::DestroySubsurface(id) => {
|
crate::surface::Action::DestroySubsurface(id) => {
|
||||||
iced_winit::commands::subsurface::destroy_subsurface(id)
|
iced_winit::commands::subsurface::destroy_subsurface(id)
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::DestroyWindow(id) => iced::window::close(id),
|
crate::surface::Action::DestroyWindow(id) => iced::window::close(id),
|
||||||
crate::surface::Action::ResponsiveMenuBar {
|
crate::surface::Action::ResponsiveMenuBar {
|
||||||
menu_bar,
|
menu_bar,
|
||||||
|
|
@ -255,7 +244,7 @@ where
|
||||||
core.menu_bars.insert(menu_bar, (limits, size));
|
core.menu_bars.insert(menu_bar, (limits, size));
|
||||||
iced::Task::none()
|
iced::Task::none()
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::Popup(settings, view) => {
|
crate::surface::Action::Popup(settings, view) => {
|
||||||
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
let Some(settings) = std::sync::Arc::try_unwrap(settings)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -282,7 +271,7 @@ where
|
||||||
iced_winit::commands::popup::get_popup(settings())
|
iced_winit::commands::popup::get_popup(settings())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::AppWindow(id, settings, view) => {
|
crate::surface::Action::AppWindow(id, settings, view) => {
|
||||||
let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| {
|
let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| {
|
||||||
s.downcast::<Box<dyn Fn(&mut T) -> iced::window::Settings + Send + Sync>>()
|
s.downcast::<Box<dyn Fn(&mut T) -> iced::window::Settings + Send + Sync>>()
|
||||||
|
|
@ -321,7 +310,7 @@ where
|
||||||
.discard()
|
.discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
crate::surface::Action::Window(id, settings, view) => {
|
crate::surface::Action::Window(id, settings, view) => {
|
||||||
let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| {
|
let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| {
|
||||||
s.downcast::<Box<dyn Fn() -> iced::window::Settings + Send + Sync>>()
|
s.downcast::<Box<dyn Fn() -> iced::window::Settings + Send + Sync>>()
|
||||||
|
|
@ -408,16 +397,15 @@ where
|
||||||
f64::from(self.app.core().scale_factor())
|
f64::from(self.app.core().scale_factor())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style(&self, theme: &Theme) -> theme::Style {
|
pub fn style(&self, theme: &Theme) -> iced_runtime::Appearance {
|
||||||
if let Some(style) = self.app.style() {
|
if let Some(style) = self.app.style() {
|
||||||
style
|
style
|
||||||
} else if self.app.core().window.is_maximized {
|
} else if self.app.core().window.is_maximized {
|
||||||
let theme = THEME.lock().unwrap();
|
let theme = THEME.lock().unwrap();
|
||||||
crate::style::iced::application::style(theme.borrow())
|
crate::style::iced::application::appearance(theme.borrow())
|
||||||
} else {
|
} else {
|
||||||
let theme = THEME.lock().unwrap();
|
let theme = THEME.lock().unwrap();
|
||||||
|
iced_runtime::Appearance {
|
||||||
theme::Style {
|
|
||||||
background_color: iced_core::Color::TRANSPARENT,
|
background_color: iced_core::Color::TRANSPARENT,
|
||||||
icon_color: theme.cosmic().on_bg_color().into(),
|
icon_color: theme.cosmic().on_bg_color().into(),
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
text_color: theme.cosmic().on_bg_color().into(),
|
||||||
|
|
@ -441,7 +429,7 @@ where
|
||||||
}
|
}
|
||||||
iced::Event::Window(window::Event::Focused) => return Some(Action::Focus(id)),
|
iced::Event::Window(window::Event::Focused) => return Some(Action::Focus(id)),
|
||||||
iced::Event::Window(window::Event::Unfocused) => return Some(Action::Unfocus(id)),
|
iced::Event::Window(window::Event::Unfocused) => return Some(Action::Unfocus(id)),
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(event)) => {
|
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(event)) => {
|
||||||
match event {
|
match event {
|
||||||
wayland::Event::Popup(wayland::PopupEvent::Done, _, id)
|
wayland::Event::Popup(wayland::PopupEvent::Done, _, id)
|
||||||
|
|
@ -454,7 +442,7 @@ where
|
||||||
) => {
|
) => {
|
||||||
return Some(Action::SuggestedBounds(b));
|
return Some(Action::SuggestedBounds(b));
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
wayland::Event::Window(iced::event::wayland::WindowEvent::WindowState(
|
wayland::Event::Window(iced::event::wayland::WindowEvent::WindowState(
|
||||||
s,
|
s,
|
||||||
)) => {
|
)) => {
|
||||||
|
|
@ -571,7 +559,7 @@ where
|
||||||
|
|
||||||
#[cfg(feature = "multi-window")]
|
#[cfg(feature = "multi-window")]
|
||||||
pub fn view(&self, id: window::Id) -> Element<'_, crate::Action<T::Message>> {
|
pub fn view(&self, id: window::Id) -> Element<'_, crate::Action<T::Message>> {
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
if let Some((_, _, v)) = self.surface_views.get(&id) {
|
if let Some((_, _, v)) = self.surface_views.get(&id) {
|
||||||
return v(&self.app);
|
return v(&self.app);
|
||||||
}
|
}
|
||||||
|
|
@ -622,7 +610,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
fn cosmic_update(&mut self, message: Action) -> iced::Task<crate::Action<T::Message>> {
|
fn cosmic_update(&mut self, message: Action) -> iced::Task<crate::Action<T::Message>> {
|
||||||
match message {
|
match message {
|
||||||
Action::WindowMaximized(id, maximized) => {
|
Action::WindowMaximized(id, maximized) => {
|
||||||
#[cfg(not(all(feature = "wayland", target_os = "linux")))]
|
#[cfg(not(feature = "wayland"))]
|
||||||
if self
|
if self
|
||||||
.app
|
.app
|
||||||
.core()
|
.core()
|
||||||
|
|
@ -647,12 +635,12 @@ impl<T: Application> Cosmic<T> {
|
||||||
self.app.on_window_resize(id, width, height);
|
self.app.on_window_resize(id, width, height);
|
||||||
|
|
||||||
//TODO: more efficient test of maximized (winit has no event for maximize if set by the OS)
|
//TODO: more efficient test of maximized (winit has no event for maximize if set by the OS)
|
||||||
return iced::window::is_maximized(id).map(move |maximized| {
|
return iced::window::get_maximized(id).map(move |maximized| {
|
||||||
crate::Action::Cosmic(Action::WindowMaximized(id, maximized))
|
crate::Action::Cosmic(Action::WindowMaximized(id, maximized))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
Action::WindowState(id, state) => {
|
Action::WindowState(id, state) => {
|
||||||
if self
|
if self
|
||||||
.app
|
.app
|
||||||
|
|
@ -704,7 +692,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
Action::WmCapabilities(id, capabilities) => {
|
Action::WmCapabilities(id, capabilities) => {
|
||||||
if self
|
if self
|
||||||
.app
|
.app
|
||||||
|
|
@ -723,10 +711,10 @@ impl<T: Application> Cosmic<T> {
|
||||||
|
|
||||||
Action::KeyboardNav(message) => match message {
|
Action::KeyboardNav(message) => match message {
|
||||||
keyboard_nav::Action::FocusNext => {
|
keyboard_nav::Action::FocusNext => {
|
||||||
return iced::widget::operation::focus_next().map(crate::Action::Cosmic);
|
return iced::widget::focus_next().map(crate::Action::Cosmic);
|
||||||
}
|
}
|
||||||
keyboard_nav::Action::FocusPrevious => {
|
keyboard_nav::Action::FocusPrevious => {
|
||||||
return iced::widget::operation::focus_previous().map(crate::Action::Cosmic);
|
return iced::widget::focus_previous().map(crate::Action::Cosmic);
|
||||||
}
|
}
|
||||||
keyboard_nav::Action::Escape => return self.app.on_escape(),
|
keyboard_nav::Action::Escape => return self.app.on_escape(),
|
||||||
keyboard_nav::Action::Search => return self.app.on_search(),
|
keyboard_nav::Action::Search => return self.app.on_search(),
|
||||||
|
|
@ -811,7 +799,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
new_theme.theme_type.prefer_dark(prefer_dark);
|
new_theme.theme_type.prefer_dark(prefer_dark);
|
||||||
|
|
||||||
cosmic_theme.set_theme(new_theme.theme_type);
|
cosmic_theme.set_theme(new_theme.theme_type);
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
if self.app.core().sync_window_border_radii_to_theme() {
|
||||||
use iced_runtime::platform_specific::wayland::CornerRadius;
|
use iced_runtime::platform_specific::wayland::CornerRadius;
|
||||||
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
||||||
|
|
@ -957,7 +945,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
// Only apply update if the theme is set to load a system theme
|
// Only apply update if the theme is set to load a system theme
|
||||||
if let ThemeType::System { .. } = cosmic_theme.theme_type {
|
if let ThemeType::System { .. } = cosmic_theme.theme_type {
|
||||||
cosmic_theme.set_theme(new_theme.theme_type);
|
cosmic_theme.set_theme(new_theme.theme_type);
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
if self.app.core().sync_window_border_radii_to_theme() {
|
||||||
use iced_runtime::platform_specific::wayland::CornerRadius;
|
use iced_runtime::platform_specific::wayland::CornerRadius;
|
||||||
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
||||||
|
|
@ -1046,28 +1034,15 @@ impl<T: Application> Cosmic<T> {
|
||||||
}
|
}
|
||||||
return Task::batch(cmds);
|
return Task::batch(cmds);
|
||||||
}
|
}
|
||||||
Action::Activate(_token) => {
|
Action::Activate(_token) =>
|
||||||
if let Some(id) = self.app.core().main_window_id() {
|
|
||||||
// Unminimize window before requesting to activate it.
|
|
||||||
let mut task = iced_runtime::window::minimize(id, false);
|
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
|
||||||
{
|
{
|
||||||
task = task.chain(
|
#[cfg(feature = "wayland")]
|
||||||
iced_winit::platform_specific::commands::activation::activate(
|
if let Some(id) = self.app.core().main_window_id() {
|
||||||
|
return iced_winit::platform_specific::commands::activation::activate(
|
||||||
id,
|
id,
|
||||||
#[allow(clippy::used_underscore_binding)]
|
#[allow(clippy::used_underscore_binding)]
|
||||||
_token,
|
_token,
|
||||||
),
|
);
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(all(feature = "wayland", target_os = "linux")))]
|
|
||||||
{
|
|
||||||
task = task.chain(iced_runtime::window::gain_focus(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1079,7 +1054,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
*v == 0
|
*v == 0
|
||||||
}) {
|
}) {
|
||||||
self.opened_surfaces.remove(&id);
|
self.opened_surfaces.remove(&id);
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
self.surface_views.remove(&id);
|
self.surface_views.remove(&id);
|
||||||
self.tracked_windows.remove(&id);
|
self.tracked_windows.remove(&id);
|
||||||
}
|
}
|
||||||
|
|
@ -1201,8 +1176,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "wayland",
|
feature = "wayland",
|
||||||
feature = "multi-window",
|
feature = "multi-window",
|
||||||
feature = "surface-message",
|
feature = "surface-message"
|
||||||
target_os = "linux"
|
|
||||||
))]
|
))]
|
||||||
if let Some((
|
if let Some((
|
||||||
parent,
|
parent,
|
||||||
|
|
@ -1247,7 +1221,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
core.applet.suggested_bounds = b;
|
core.applet.suggested_bounds = b;
|
||||||
}
|
}
|
||||||
Action::Opened(id) => {
|
Action::Opened(id) => {
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
if self.app.core().sync_window_border_radii_to_theme() {
|
||||||
use iced_runtime::platform_specific::wayland::CornerRadius;
|
use iced_runtime::platform_specific::wayland::CornerRadius;
|
||||||
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
||||||
|
|
@ -1296,14 +1270,14 @@ impl<App: Application> Cosmic<App> {
|
||||||
pub fn new(app: App) -> Self {
|
pub fn new(app: App) -> Self {
|
||||||
Self {
|
Self {
|
||||||
app,
|
app,
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
surface_views: HashMap::new(),
|
surface_views: HashMap::new(),
|
||||||
tracked_windows: HashSet::new(),
|
tracked_windows: HashSet::new(),
|
||||||
opened_surfaces: HashMap::new(),
|
opened_surfaces: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
/// Create a subsurface
|
/// Create a subsurface
|
||||||
pub fn get_subsurface(
|
pub fn get_subsurface(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -1326,7 +1300,7 @@ impl<App: Application> Cosmic<App> {
|
||||||
get_subsurface(settings)
|
get_subsurface(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
/// Create a subsurface
|
/// Create a subsurface
|
||||||
pub fn get_popup(
|
pub fn get_popup(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -1348,7 +1322,7 @@ impl<App: Application> Cosmic<App> {
|
||||||
get_popup(settings)
|
get_popup(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
/// Create a window surface
|
/// Create a window surface
|
||||||
pub fn get_window(
|
pub fn get_window(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
||||||
124
src/app/mod.rs
124
src/app/mod.rs
|
|
@ -11,8 +11,9 @@ pub use action::Action;
|
||||||
use cosmic_config::CosmicConfigEntry;
|
use cosmic_config::CosmicConfigEntry;
|
||||||
pub mod context_drawer;
|
pub mod context_drawer;
|
||||||
pub use context_drawer::{ContextDrawer, context_drawer};
|
pub use context_drawer::{ContextDrawer, context_drawer};
|
||||||
use iced::application::BootFn;
|
|
||||||
pub mod cosmic;
|
pub mod cosmic;
|
||||||
|
#[cfg(all(feature = "winit", feature = "multi-window"))]
|
||||||
|
pub(crate) mod multi_window;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
pub type Task<M> = iced::Task<crate::Action<M>>;
|
pub type Task<M> = iced::Task<crate::Action<M>>;
|
||||||
|
|
@ -20,13 +21,12 @@ pub type Task<M> = iced::Task<crate::Action<M>>;
|
||||||
pub use crate::Core;
|
pub use crate::Core;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::theme::THEME;
|
use crate::theme::THEME;
|
||||||
use crate::widget::{container, id_container, menu, nav_bar, popover, space};
|
use crate::widget::{container, horizontal_space, id_container, menu, nav_bar, popover};
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
|
use iced::window;
|
||||||
use iced::{Length, Subscription};
|
use iced::{Length, Subscription};
|
||||||
use iced::{theme, window};
|
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
pub(crate) fn iced_settings<App: Application>(
|
pub(crate) fn iced_settings<App: Application>(
|
||||||
|
|
@ -82,7 +82,7 @@ pub(crate) fn iced_settings<App: Application>(
|
||||||
window_settings.min_size = Some(min_size);
|
window_settings.min_size = Some(min_size);
|
||||||
}
|
}
|
||||||
let max_size = settings.size_limits.max();
|
let max_size = settings.size_limits.max();
|
||||||
if max_size != iced::Size::INFINITE {
|
if max_size != iced::Size::INFINITY {
|
||||||
window_settings.max_size = Some(max_size);
|
window_settings.max_size = Some(max_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,38 +90,6 @@ pub(crate) fn iced_settings<App: Application>(
|
||||||
(iced, (core, flags), window_settings)
|
(iced, (core, flags), window_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct BootDataInner<A: crate::app::Application> {
|
|
||||||
pub flags: A::Flags,
|
|
||||||
pub core: Core,
|
|
||||||
pub settings: window::Settings,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct BootData<A: crate::app::Application>(pub Rc<RefCell<Option<BootDataInner<A>>>>);
|
|
||||||
|
|
||||||
impl<A: crate::app::Application> BootFn<cosmic::Cosmic<A>, crate::Action<A::Message>>
|
|
||||||
for BootData<A>
|
|
||||||
{
|
|
||||||
fn boot(&self) -> (cosmic::Cosmic<A>, iced::Task<crate::Action<A::Message>>) {
|
|
||||||
let mut data = self.0.borrow_mut();
|
|
||||||
let mut data = data.take().unwrap();
|
|
||||||
let mut tasks = Vec::new();
|
|
||||||
#[cfg(feature = "multi-window")]
|
|
||||||
if data.core.main_window_id().is_some() {
|
|
||||||
let window_task = iced_runtime::task::oneshot(|channel| {
|
|
||||||
iced_runtime::Action::Window(iced_runtime::window::Action::Open(
|
|
||||||
window::Id::RESERVED,
|
|
||||||
data.settings,
|
|
||||||
channel,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
data.core.set_main_window_id(Some(window::Id::RESERVED));
|
|
||||||
tasks.push(window_task.discard());
|
|
||||||
}
|
|
||||||
let (a, t) = cosmic::Cosmic::<A>::init((data.core, data.flags));
|
|
||||||
tasks.push(t);
|
|
||||||
(a, Task::batch(tasks))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Launch a COSMIC application with the given [`Settings`].
|
/// Launch a COSMIC application with the given [`Settings`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
@ -134,52 +102,39 @@ pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Res
|
||||||
}
|
}
|
||||||
|
|
||||||
let default_font = settings.default_font;
|
let default_font = settings.default_font;
|
||||||
let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
|
let (settings, mut flags, window_settings) = iced_settings::<App>(settings, flags);
|
||||||
#[cfg(not(feature = "multi-window"))]
|
#[cfg(not(feature = "multi-window"))]
|
||||||
{
|
{
|
||||||
core.main_window = Some(iced::window::Id::RESERVED);
|
flags.0.main_window = Some(iced::window::Id::RESERVED);
|
||||||
|
|
||||||
iced::application(
|
iced::application(
|
||||||
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
|
cosmic::Cosmic::title,
|
||||||
flags,
|
|
||||||
core,
|
|
||||||
settings: window_settings.clone(),
|
|
||||||
})))),
|
|
||||||
cosmic::Cosmic::update,
|
cosmic::Cosmic::update,
|
||||||
cosmic::Cosmic::view,
|
cosmic::Cosmic::view,
|
||||||
)
|
)
|
||||||
.subscription(cosmic::Cosmic::subscription)
|
.subscription(cosmic::Cosmic::subscription)
|
||||||
.title(cosmic::Cosmic::title)
|
|
||||||
.style(cosmic::Cosmic::style)
|
.style(cosmic::Cosmic::style)
|
||||||
.theme(cosmic::Cosmic::theme)
|
.theme(cosmic::Cosmic::theme)
|
||||||
.window_size((500.0, 800.0))
|
.window_size((500.0, 800.0))
|
||||||
.settings(settings)
|
.settings(settings)
|
||||||
.window(window_settings)
|
.window(window_settings)
|
||||||
.run()
|
.run_with(move || cosmic::Cosmic::<App>::init(flags))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "multi-window")]
|
#[cfg(feature = "multi-window")]
|
||||||
{
|
{
|
||||||
let no_main_window = core.main_window.is_none();
|
let mut app = multi_window::multi_window::<_, _, _, _, App::Executor>(
|
||||||
if no_main_window {
|
cosmic::Cosmic::title,
|
||||||
// app = app.window(window_settings);
|
|
||||||
core.main_window = Some(iced_core::window::Id::RESERVED);
|
|
||||||
}
|
|
||||||
let app = iced::daemon(
|
|
||||||
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
|
|
||||||
flags,
|
|
||||||
core,
|
|
||||||
settings: window_settings,
|
|
||||||
})))),
|
|
||||||
cosmic::Cosmic::update,
|
cosmic::Cosmic::update,
|
||||||
cosmic::Cosmic::view,
|
cosmic::Cosmic::view,
|
||||||
);
|
);
|
||||||
|
if flags.0.main_window.is_none() {
|
||||||
|
app = app.window(window_settings);
|
||||||
|
flags.0.main_window = Some(iced_core::window::Id::RESERVED);
|
||||||
|
}
|
||||||
app.subscription(cosmic::Cosmic::subscription)
|
app.subscription(cosmic::Cosmic::subscription)
|
||||||
.title(cosmic::Cosmic::title)
|
|
||||||
.style(cosmic::Cosmic::style)
|
.style(cosmic::Cosmic::style)
|
||||||
.theme(cosmic::Cosmic::theme)
|
.theme(cosmic::Cosmic::theme)
|
||||||
.settings(settings)
|
.settings(settings)
|
||||||
.run()
|
.run_with(move || cosmic::Cosmic::<App>::init(flags))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,17 +204,13 @@ where
|
||||||
tracing::info!("Another instance is running");
|
tracing::info!("Another instance is running");
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
|
let (settings, mut flags, window_settings) = iced_settings::<App>(settings, flags);
|
||||||
core.single_instance = true;
|
flags.0.single_instance = true;
|
||||||
|
|
||||||
#[cfg(not(feature = "multi-window"))]
|
#[cfg(not(feature = "multi-window"))]
|
||||||
{
|
{
|
||||||
iced::application(
|
iced::application(
|
||||||
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
|
cosmic::Cosmic::title,
|
||||||
flags,
|
|
||||||
core,
|
|
||||||
settings: window_settings.clone(),
|
|
||||||
})))),
|
|
||||||
cosmic::Cosmic::update,
|
cosmic::Cosmic::update,
|
||||||
cosmic::Cosmic::view,
|
cosmic::Cosmic::view,
|
||||||
)
|
)
|
||||||
|
|
@ -269,31 +220,24 @@ where
|
||||||
.window_size((500.0, 800.0))
|
.window_size((500.0, 800.0))
|
||||||
.settings(settings)
|
.settings(settings)
|
||||||
.window(window_settings)
|
.window(window_settings)
|
||||||
.run()
|
.run_with(move || cosmic::Cosmic::<App>::init(flags))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "multi-window")]
|
#[cfg(feature = "multi-window")]
|
||||||
{
|
{
|
||||||
let no_main_window = core.main_window.is_none();
|
let mut app = multi_window::multi_window::<_, _, _, _, App::Executor>(
|
||||||
if no_main_window {
|
cosmic::Cosmic::title,
|
||||||
// app = app.window(window_settings);
|
|
||||||
core.main_window = Some(iced_core::window::Id::RESERVED);
|
|
||||||
}
|
|
||||||
let mut app = iced::daemon(
|
|
||||||
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
|
|
||||||
flags,
|
|
||||||
core,
|
|
||||||
settings: window_settings,
|
|
||||||
})))),
|
|
||||||
cosmic::Cosmic::update,
|
cosmic::Cosmic::update,
|
||||||
cosmic::Cosmic::view,
|
cosmic::Cosmic::view,
|
||||||
);
|
);
|
||||||
|
if flags.0.main_window.is_none() {
|
||||||
|
app = app.window(window_settings);
|
||||||
|
flags.0.main_window = Some(iced_core::window::Id::RESERVED);
|
||||||
|
}
|
||||||
app.subscription(cosmic::Cosmic::subscription)
|
app.subscription(cosmic::Cosmic::subscription)
|
||||||
.style(cosmic::Cosmic::style)
|
.style(cosmic::Cosmic::style)
|
||||||
.title(cosmic::Cosmic::title)
|
|
||||||
.theme(cosmic::Cosmic::theme)
|
.theme(cosmic::Cosmic::theme)
|
||||||
.settings(settings)
|
.settings(settings)
|
||||||
.run()
|
.run_with(move || cosmic::Cosmic::<App>::init(flags))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -385,8 +329,9 @@ where
|
||||||
.on_context(|id| crate::Action::Cosmic(Action::NavBarContext(id)))
|
.on_context(|id| crate::Action::Cosmic(Action::NavBarContext(id)))
|
||||||
.context_menu(self.nav_context_menu(self.core().nav_bar_context()))
|
.context_menu(self.nav_context_menu(self.core().nav_bar_context()))
|
||||||
.into_container()
|
.into_container()
|
||||||
|
// XXX both must be shrink to avoid flex layout from ignoring it
|
||||||
.width(iced::Length::Shrink)
|
.width(iced::Length::Shrink)
|
||||||
.height(iced::Length::Fill);
|
.height(iced::Length::Shrink);
|
||||||
|
|
||||||
if !self.core().is_condensed() {
|
if !self.core().is_condensed() {
|
||||||
nav = nav.max_width(280);
|
nav = nav.max_width(280);
|
||||||
|
|
@ -483,7 +428,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overrides the default style for applications
|
/// Overrides the default style for applications
|
||||||
fn style(&self) -> Option<theme::Style> {
|
fn style(&self) -> Option<iced_runtime::Appearance> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -719,17 +664,16 @@ impl<App: Application> ApplicationExt for App {
|
||||||
[0, 0, 0, 0]
|
[0, 0, 0, 0]
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
//TODO: this element is added to workaround state issues
|
//TODO: this element is added to workaround state issues
|
||||||
widgets.push(space::horizontal().width(Length::Shrink).into());
|
widgets.push(horizontal_space().width(Length::Shrink).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
widgets
|
widgets
|
||||||
});
|
});
|
||||||
|
|
||||||
let content_col = crate::widget::column::with_capacity(2)
|
let content_col = crate::widget::column::with_capacity(2)
|
||||||
.push(content_row)
|
.push(content_row)
|
||||||
.push_maybe(self.footer().map(|footer| {
|
.push_maybe(self.footer().map(|footer| {
|
||||||
|
|
@ -742,6 +686,7 @@ impl<App: Application> ApplicationExt for App {
|
||||||
}));
|
}));
|
||||||
let content: Element<_> = if content_container {
|
let content: Element<_> = if content_container {
|
||||||
content_col
|
content_col
|
||||||
|
.apply(container)
|
||||||
.width(iced::Length::Fill)
|
.width(iced::Length::Fill)
|
||||||
.height(iced::Length::Fill)
|
.height(iced::Length::Fill)
|
||||||
.apply(|w| id_container(w, iced_core::id::Id::new("COSMIC_content_container")))
|
.apply(|w| id_container(w, iced_core::id::Id::new("COSMIC_content_container")))
|
||||||
|
|
@ -771,7 +716,8 @@ impl<App: Application> ApplicationExt for App {
|
||||||
.title(&core.window.header_title)
|
.title(&core.window.header_title)
|
||||||
.on_drag(crate::Action::Cosmic(Action::Drag))
|
.on_drag(crate::Action::Cosmic(Action::Drag))
|
||||||
.on_right_click(crate::Action::Cosmic(Action::ShowWindowMenu))
|
.on_right_click(crate::Action::Cosmic(Action::ShowWindowMenu))
|
||||||
.on_double_click(crate::Action::Cosmic(Action::Maximize));
|
.on_double_click(crate::Action::Cosmic(Action::Maximize))
|
||||||
|
.is_condensed(is_condensed);
|
||||||
|
|
||||||
if self.nav_model().is_some() {
|
if self.nav_model().is_some() {
|
||||||
let toggle = crate::widget::nav_bar_toggle()
|
let toggle = crate::widget::nav_bar_toggle()
|
||||||
|
|
|
||||||
244
src/app/multi_window.rs
Normal file
244
src/app/multi_window.rs
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
// Copyright 2024 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! Create and run daemons that run in the background.
|
||||||
|
//! Copied from iced 0.13, but adds optional initial window
|
||||||
|
|
||||||
|
use iced::application;
|
||||||
|
use iced::window;
|
||||||
|
use iced::{
|
||||||
|
self, Program,
|
||||||
|
program::{self, with_style, with_subscription, with_theme, with_title},
|
||||||
|
runtime::{Appearance, DefaultStyle},
|
||||||
|
};
|
||||||
|
use iced::{Element, Result, Settings, Subscription, Task};
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub(crate) struct Instance<State, Message, Theme, Renderer, Update, View, Executor> {
|
||||||
|
update: Update,
|
||||||
|
view: View,
|
||||||
|
_state: PhantomData<State>,
|
||||||
|
_message: PhantomData<Message>,
|
||||||
|
_theme: PhantomData<Theme>,
|
||||||
|
_renderer: PhantomData<Renderer>,
|
||||||
|
_executor: PhantomData<Executor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an iced [`MultiWindow`] given its title, update, and view logic.
|
||||||
|
pub fn multi_window<State, Message, Theme, Renderer, Executor>(
|
||||||
|
title: impl Title<State>,
|
||||||
|
update: impl application::Update<State, Message>,
|
||||||
|
view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
||||||
|
) -> MultiWindow<impl Program<State = State, Message = Message, Theme = Theme>>
|
||||||
|
where
|
||||||
|
State: 'static,
|
||||||
|
Message: Send + std::fmt::Debug + 'static,
|
||||||
|
Theme: Default + DefaultStyle,
|
||||||
|
Renderer: program::Renderer,
|
||||||
|
Executor: iced::Executor,
|
||||||
|
{
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
impl<State, Message, Theme, Renderer, Update, View, Executor> Program
|
||||||
|
for Instance<State, Message, Theme, Renderer, Update, View, Executor>
|
||||||
|
where
|
||||||
|
Message: Send + std::fmt::Debug + 'static,
|
||||||
|
Theme: Default + DefaultStyle,
|
||||||
|
Renderer: program::Renderer,
|
||||||
|
Update: application::Update<State, Message>,
|
||||||
|
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
||||||
|
Executor: iced::Executor,
|
||||||
|
{
|
||||||
|
type State = State;
|
||||||
|
type Message = Message;
|
||||||
|
type Theme = Theme;
|
||||||
|
type Renderer = Renderer;
|
||||||
|
type Executor = Executor;
|
||||||
|
|
||||||
|
fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
|
||||||
|
self.update.update(state, message).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view<'a>(
|
||||||
|
&self,
|
||||||
|
state: &'a Self::State,
|
||||||
|
window: window::Id,
|
||||||
|
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
|
||||||
|
self.view.view(state, window).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiWindow {
|
||||||
|
raw: Instance {
|
||||||
|
update,
|
||||||
|
view,
|
||||||
|
_state: PhantomData,
|
||||||
|
_message: PhantomData,
|
||||||
|
_theme: PhantomData,
|
||||||
|
_renderer: PhantomData,
|
||||||
|
_executor: PhantomData::<Executor>,
|
||||||
|
},
|
||||||
|
settings: Settings::default(),
|
||||||
|
window: None,
|
||||||
|
}
|
||||||
|
.title(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The underlying definition and configuration of an iced daemon.
|
||||||
|
///
|
||||||
|
/// You can use this API to create and run iced applications
|
||||||
|
/// step by step—without coupling your logic to a trait
|
||||||
|
/// or a specific type.
|
||||||
|
///
|
||||||
|
/// You can create a [`MultiWindow`] with the [`daemon`] helper.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MultiWindow<P: Program> {
|
||||||
|
raw: P,
|
||||||
|
settings: Settings,
|
||||||
|
window: Option<window::Settings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Program> MultiWindow<P> {
|
||||||
|
#[cfg(any(feature = "winit", feature = "wayland"))]
|
||||||
|
/// Runs the [`MultiWindow`].
|
||||||
|
///
|
||||||
|
/// The state of the [`MultiWindow`] must implement [`Default`].
|
||||||
|
/// If your state does not implement [`Default`], use [`run_with`]
|
||||||
|
/// instead.
|
||||||
|
///
|
||||||
|
/// [`run_with`]: Self::run_with
|
||||||
|
pub fn run(self) -> Result
|
||||||
|
where
|
||||||
|
Self: 'static,
|
||||||
|
P::State: Default,
|
||||||
|
{
|
||||||
|
self.raw.run(self.settings, self.window)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "winit", feature = "wayland"))]
|
||||||
|
/// Runs the [`MultiWindow`] with a closure that creates the initial state.
|
||||||
|
pub fn run_with<I>(self, initialize: I) -> Result
|
||||||
|
where
|
||||||
|
Self: 'static,
|
||||||
|
I: FnOnce() -> (P::State, Task<P::Message>) + 'static,
|
||||||
|
{
|
||||||
|
self.raw.run_with(self.settings, self.window, initialize)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the [`Settings`] that will be used to run the [`MultiWindow`].
|
||||||
|
pub fn settings(self, settings: Settings) -> Self {
|
||||||
|
Self { settings, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the [`Title`] of the [`MultiWindow`].
|
||||||
|
pub(crate) fn title(
|
||||||
|
self,
|
||||||
|
title: impl Title<P::State>,
|
||||||
|
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
|
||||||
|
MultiWindow {
|
||||||
|
raw: with_title(self.raw, move |state, window| title.title(state, window)),
|
||||||
|
settings: self.settings,
|
||||||
|
window: self.window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the subscription logic of the [`MultiWindow`].
|
||||||
|
pub fn subscription(
|
||||||
|
self,
|
||||||
|
f: impl Fn(&P::State) -> Subscription<P::Message>,
|
||||||
|
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
|
||||||
|
MultiWindow {
|
||||||
|
raw: with_subscription(self.raw, f),
|
||||||
|
settings: self.settings,
|
||||||
|
window: self.window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the theme logic of the [`MultiWindow`].
|
||||||
|
pub fn theme(
|
||||||
|
self,
|
||||||
|
f: impl Fn(&P::State, window::Id) -> P::Theme,
|
||||||
|
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
|
||||||
|
MultiWindow {
|
||||||
|
raw: with_theme(self.raw, f),
|
||||||
|
settings: self.settings,
|
||||||
|
window: self.window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the style logic of the [`MultiWindow`].
|
||||||
|
pub fn style(
|
||||||
|
self,
|
||||||
|
f: impl Fn(&P::State, &P::Theme) -> Appearance,
|
||||||
|
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
|
||||||
|
MultiWindow {
|
||||||
|
raw: with_style(self.raw, f),
|
||||||
|
settings: self.settings,
|
||||||
|
window: self.window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the window settings of the [`MultiWindow`].
|
||||||
|
pub fn window(self, window: window::Settings) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: self.raw,
|
||||||
|
settings: self.settings,
|
||||||
|
window: Some(window),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The title logic of some [`MultiWindow`].
|
||||||
|
///
|
||||||
|
/// This trait is implemented both for `&static str` and
|
||||||
|
/// any closure `Fn(&State, window::Id) -> String`.
|
||||||
|
///
|
||||||
|
/// This trait allows the [`daemon`] builder to take any of them.
|
||||||
|
pub trait Title<State> {
|
||||||
|
/// Produces the title of the [`MultiWindow`].
|
||||||
|
fn title(&self, state: &State, window: window::Id) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<State> Title<State> for &'static str {
|
||||||
|
fn title(&self, _state: &State, _window: window::Id) -> String {
|
||||||
|
(*self).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, State> Title<State> for T
|
||||||
|
where
|
||||||
|
T: Fn(&State, window::Id) -> String,
|
||||||
|
{
|
||||||
|
fn title(&self, state: &State, window: window::Id) -> String {
|
||||||
|
self(state, window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The view logic of some [`MultiWindow`].
|
||||||
|
///
|
||||||
|
/// This trait allows the [`daemon`] builder to take any closure that
|
||||||
|
/// returns any `Into<Element<'_, Message>>`.
|
||||||
|
pub trait View<'a, State, Message, Theme, Renderer> {
|
||||||
|
/// Produces the widget of the [`MultiWindow`].
|
||||||
|
fn view(
|
||||||
|
&self,
|
||||||
|
state: &'a State,
|
||||||
|
window: window::Id,
|
||||||
|
) -> impl Into<Element<'a, Message, Theme, Renderer>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, State, Message, Theme, Renderer, Widget> View<'a, State, Message, Theme, Renderer> for T
|
||||||
|
where
|
||||||
|
T: Fn(&'a State, window::Id) -> Widget,
|
||||||
|
State: 'static,
|
||||||
|
Widget: Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
{
|
||||||
|
fn view(
|
||||||
|
&self,
|
||||||
|
state: &'a State,
|
||||||
|
window: window::Id,
|
||||||
|
) -> impl Into<Element<'a, Message, Theme, Renderer>> {
|
||||||
|
self(state, window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ pub struct Settings {
|
||||||
pub(crate) antialiasing: bool,
|
pub(crate) antialiasing: bool,
|
||||||
|
|
||||||
/// Autosize the window to fit its contents
|
/// Autosize the window to fit its contents
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
pub(crate) autosize: bool,
|
pub(crate) autosize: bool,
|
||||||
|
|
||||||
/// Set the application to not create a main window
|
/// Set the application to not create a main window
|
||||||
|
|
@ -80,7 +80,7 @@ impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
antialiasing: true,
|
antialiasing: true,
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
autosize: false,
|
autosize: false,
|
||||||
no_main_window: false,
|
no_main_window: false,
|
||||||
client_decorations: true,
|
client_decorations: true,
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -233,26 +233,25 @@ where
|
||||||
self.padding,
|
self.padding,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.align,
|
self.align,
|
||||||
&mut self.children,
|
&self.children,
|
||||||
&mut tree.children,
|
&mut tree.children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn Operation,
|
||||||
) {
|
) {
|
||||||
operation.container(None, layout.bounds());
|
operation.container(None, layout.bounds(), &mut |operation| {
|
||||||
operation.traverse(&mut |operation| {
|
|
||||||
self.children
|
self.children
|
||||||
.iter_mut()
|
.iter()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.for_each(|((child, state), c_layout)| {
|
.for_each(|((child, state), c_layout)| {
|
||||||
child.as_widget_mut().operate(
|
child.as_widget().operate(
|
||||||
state,
|
state,
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -262,17 +261,17 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
let my_state = tree.state.downcast_mut::<State>();
|
let my_state = tree.state.downcast_mut::<State>();
|
||||||
|
|
||||||
if let Some(hovered) = my_state.hovered {
|
if let Some(hovered) = my_state.hovered {
|
||||||
|
|
@ -286,7 +285,7 @@ where
|
||||||
e,
|
e,
|
||||||
mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. }
|
mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. }
|
||||||
) {
|
) {
|
||||||
return self.children[hovered].as_widget_mut().update(
|
return self.children[hovered].as_widget_mut().on_event(
|
||||||
&mut tree.children[hovered],
|
&mut tree.children[hovered],
|
||||||
event,
|
event,
|
||||||
child_layout.with_virtual_offset(layout.virtual_offset()),
|
child_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
|
@ -303,7 +302,7 @@ where
|
||||||
iced::core::touch::Event::FingerLifted { .. }
|
iced::core::touch::Event::FingerLifted { .. }
|
||||||
| iced::core::touch::Event::FingerLost { .. }
|
| iced::core::touch::Event::FingerLost { .. }
|
||||||
) {
|
) {
|
||||||
return self.children[hovered].as_widget_mut().update(
|
return self.children[hovered].as_widget_mut().on_event(
|
||||||
&mut tree.children[hovered],
|
&mut tree.children[hovered],
|
||||||
event,
|
event,
|
||||||
child_layout.with_virtual_offset(layout.virtual_offset()),
|
child_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
|
@ -320,13 +319,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (((i, child), state), c_layout) in self
|
self.children
|
||||||
.children
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
{
|
.map(|(((i, child), state), c_layout)| {
|
||||||
let mut cursor_virtual = cursor;
|
let mut cursor_virtual = cursor;
|
||||||
if matches!(
|
if matches!(
|
||||||
event,
|
event,
|
||||||
|
|
@ -338,9 +336,9 @@ where
|
||||||
) && cursor.is_over(c_layout.bounds())
|
) && cursor.is_over(c_layout.bounds())
|
||||||
{
|
{
|
||||||
my_state.hovered = Some(i);
|
my_state.hovered = Some(i);
|
||||||
return child.as_widget_mut().update(
|
return child.as_widget_mut().on_event(
|
||||||
state,
|
state,
|
||||||
&event,
|
event.clone(),
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
cursor_virtual,
|
cursor_virtual,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -352,17 +350,18 @@ where
|
||||||
cursor_virtual = mouse::Cursor::Unavailable;
|
cursor_virtual = mouse::Cursor::Unavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
child.as_widget_mut().update(
|
child.as_widget_mut().on_event(
|
||||||
state,
|
state,
|
||||||
&event,
|
event.clone(),
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
cursor_virtual,
|
cursor_virtual,
|
||||||
renderer,
|
renderer,
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
)
|
||||||
}
|
})
|
||||||
|
.fold(event::Status::Ignored, event::Status::merge)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -437,19 +436,11 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
overlay::from_children(
|
overlay::from_children(&mut self.children, tree, layout, renderer, translation)
|
||||||
&mut self.children,
|
|
||||||
tree,
|
|
||||||
layout,
|
|
||||||
renderer,
|
|
||||||
viewport,
|
|
||||||
translation,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,34 @@
|
||||||
#[cfg(feature = "applet-token")]
|
#[cfg(feature = "applet-token")]
|
||||||
pub mod token;
|
pub mod token;
|
||||||
|
|
||||||
use crate::app::{BootData, BootDataInner, cosmic};
|
use crate::app::cosmic;
|
||||||
use crate::{
|
use crate::{
|
||||||
Application, Element, Renderer,
|
Application, Element, Renderer,
|
||||||
app::iced_settings,
|
app::iced_settings,
|
||||||
cctk::sctk,
|
cctk::sctk,
|
||||||
|
iced::{
|
||||||
|
self, Color, Length, Limits, Rectangle,
|
||||||
|
alignment::{Alignment, Horizontal, Vertical},
|
||||||
|
widget::Container,
|
||||||
|
window,
|
||||||
|
},
|
||||||
|
iced_widget,
|
||||||
theme::{self, Button, THEME, system_dark, system_light},
|
theme::{self, Button, THEME, system_dark, system_light},
|
||||||
widget::{
|
widget::{
|
||||||
self,
|
self,
|
||||||
autosize::{self, Autosize, autosize},
|
autosize::{self, Autosize, autosize},
|
||||||
column::Column,
|
column::Column,
|
||||||
layer_container,
|
horizontal_space, layer_container,
|
||||||
row::Row,
|
row::Row,
|
||||||
space::horizontal,
|
vertical_space,
|
||||||
space::vertical,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use cosmic_panel_config;
|
pub use cosmic_panel_config;
|
||||||
use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize};
|
use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize};
|
||||||
use iced::{
|
|
||||||
self, Color, Length, Limits, Rectangle,
|
|
||||||
alignment::{Alignment, Horizontal, Vertical},
|
|
||||||
widget::Container,
|
|
||||||
window,
|
|
||||||
};
|
|
||||||
use iced_core::{Padding, Shadow};
|
use iced_core::{Padding, Shadow};
|
||||||
use iced_runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
|
|
||||||
use iced_widget::Text;
|
use iced_widget::Text;
|
||||||
|
use iced_widget::runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
|
||||||
use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
|
use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::{borrow::Cow, num::NonZeroU32, rc::Rc, sync::LazyLock, time::Duration};
|
use std::{borrow::Cow, num::NonZeroU32, rc::Rc, sync::LazyLock, time::Duration};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
|
|
@ -42,7 +40,7 @@ static AUTOSIZE_ID: LazyLock<iced::id::Id> =
|
||||||
static AUTOSIZE_MAIN_ID: LazyLock<iced::id::Id> =
|
static AUTOSIZE_MAIN_ID: LazyLock<iced::id::Id> =
|
||||||
LazyLock::new(|| iced::id::Id::new("cosmic-applet-autosize-main"));
|
LazyLock::new(|| iced::id::Id::new("cosmic-applet-autosize-main"));
|
||||||
static TOOLTIP_ID: LazyLock<crate::widget::Id> = LazyLock::new(|| iced::id::Id::new("subsurface"));
|
static TOOLTIP_ID: LazyLock<crate::widget::Id> = LazyLock::new(|| iced::id::Id::new("subsurface"));
|
||||||
pub(crate) static TOOLTIP_WINDOW_ID: LazyLock<window::Id> = LazyLock::new(window::Id::unique);
|
static TOOLTIP_WINDOW_ID: LazyLock<window::Id> = LazyLock::new(window::Id::unique);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
|
@ -226,7 +224,7 @@ impl Context {
|
||||||
let symbolic = icon.symbolic;
|
let symbolic = icon.symbolic;
|
||||||
let icon = widget::icon(icon)
|
let icon = widget::icon(icon)
|
||||||
.class(if symbolic {
|
.class(if symbolic {
|
||||||
theme::Svg::Custom(Rc::new(|theme| iced_widget::svg::Style {
|
theme::Svg::Custom(Rc::new(|theme| crate::iced_widget::svg::Style {
|
||||||
color: Some(theme.cosmic().background.on.into()),
|
color: Some(theme.cosmic().background.on.into()),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -388,10 +386,10 @@ impl Context {
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
icon_color: Some(cosmic.background.on.into()),
|
icon_color: Some(cosmic.background.on.into()),
|
||||||
snap: true,
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.width(Length::Shrink)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
.align_x(horizontal_align)
|
.align_x(horizontal_align)
|
||||||
.align_y(vertical_align),
|
.align_y(vertical_align),
|
||||||
|
|
@ -573,33 +571,26 @@ pub fn run<App: Application>(flags: App::Flags) -> iced::Result {
|
||||||
|
|
||||||
// TODO make multi-window not mandatory
|
// TODO make multi-window not mandatory
|
||||||
|
|
||||||
let no_main_window = core.main_window.is_none();
|
let mut app = super::app::multi_window::multi_window::<_, _, _, _, App::Executor>(
|
||||||
if no_main_window {
|
cosmic::Cosmic::title,
|
||||||
// TODO still apply window settings?
|
|
||||||
// window_settings = window_settings.clone();
|
|
||||||
core.main_window = Some(iced_core::window::Id::RESERVED);
|
|
||||||
}
|
|
||||||
let mut app = iced::daemon(
|
|
||||||
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
|
|
||||||
flags,
|
|
||||||
core,
|
|
||||||
settings: window_settings,
|
|
||||||
})))),
|
|
||||||
cosmic::Cosmic::update,
|
cosmic::Cosmic::update,
|
||||||
cosmic::Cosmic::view,
|
cosmic::Cosmic::view,
|
||||||
);
|
);
|
||||||
|
if core.main_window.is_none() {
|
||||||
|
app = app.window(window_settings.clone());
|
||||||
|
core.main_window = Some(iced_core::window::Id::RESERVED);
|
||||||
|
}
|
||||||
app.subscription(cosmic::Cosmic::subscription)
|
app.subscription(cosmic::Cosmic::subscription)
|
||||||
.style(cosmic::Cosmic::style)
|
.style(cosmic::Cosmic::style)
|
||||||
.theme(cosmic::Cosmic::theme)
|
.theme(cosmic::Cosmic::theme)
|
||||||
.settings(iced_settings)
|
.settings(iced_settings)
|
||||||
.run()
|
.run_with(move || cosmic::Cosmic::<App>::init((core, flags)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn style() -> iced::theme::Style {
|
pub fn style() -> iced_runtime::Appearance {
|
||||||
let theme = crate::theme::THEME.lock().unwrap();
|
let theme = crate::theme::THEME.lock().unwrap();
|
||||||
iced::theme::Style {
|
iced_runtime::Appearance {
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
text_color: theme.cosmic().on_bg_color().into(),
|
||||||
icon_color: theme.cosmic().on_bg_color().into(),
|
icon_color: theme.cosmic().on_bg_color().into(),
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -222,26 +222,25 @@ where
|
||||||
self.padding,
|
self.padding,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.align,
|
self.align,
|
||||||
&mut self.children,
|
&self.children,
|
||||||
&mut tree.children,
|
&mut tree.children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn Operation,
|
||||||
) {
|
) {
|
||||||
operation.container(None, layout.bounds());
|
operation.container(None, layout.bounds(), &mut |operation| {
|
||||||
operation.traverse(&mut |operation| {
|
|
||||||
self.children
|
self.children
|
||||||
.iter_mut()
|
.iter()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.for_each(|((child, state), c_layout)| {
|
.for_each(|((child, state), c_layout)| {
|
||||||
child.as_widget_mut().operate(
|
child.as_widget().operate(
|
||||||
state,
|
state,
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -251,17 +250,17 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
let my_state = tree.state.downcast_mut::<State>();
|
let my_state = tree.state.downcast_mut::<State>();
|
||||||
|
|
||||||
if let Some(hovered) = my_state.hovered {
|
if let Some(hovered) = my_state.hovered {
|
||||||
|
|
@ -275,7 +274,7 @@ where
|
||||||
e,
|
e,
|
||||||
mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. }
|
mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. }
|
||||||
) {
|
) {
|
||||||
return self.children[hovered].as_widget_mut().update(
|
return self.children[hovered].as_widget_mut().on_event(
|
||||||
&mut tree.children[hovered],
|
&mut tree.children[hovered],
|
||||||
event,
|
event,
|
||||||
child_layout.with_virtual_offset(layout.virtual_offset()),
|
child_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
|
@ -292,7 +291,7 @@ where
|
||||||
iced::core::touch::Event::FingerLifted { .. }
|
iced::core::touch::Event::FingerLifted { .. }
|
||||||
| iced::core::touch::Event::FingerLost { .. }
|
| iced::core::touch::Event::FingerLost { .. }
|
||||||
) {
|
) {
|
||||||
return self.children[hovered].as_widget_mut().update(
|
return self.children[hovered].as_widget_mut().on_event(
|
||||||
&mut tree.children[hovered],
|
&mut tree.children[hovered],
|
||||||
event,
|
event,
|
||||||
child_layout.with_virtual_offset(layout.virtual_offset()),
|
child_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
|
@ -309,13 +308,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (((i, child), state), c_layout) in self
|
self.children
|
||||||
.children
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
{
|
.map(|(((i, child), state), c_layout)| {
|
||||||
let mut cursor_virtual = cursor;
|
let mut cursor_virtual = cursor;
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
|
|
@ -328,9 +326,9 @@ where
|
||||||
) && cursor.is_over(c_layout.bounds())
|
) && cursor.is_over(c_layout.bounds())
|
||||||
{
|
{
|
||||||
my_state.hovered = Some(i);
|
my_state.hovered = Some(i);
|
||||||
return child.as_widget_mut().update(
|
return child.as_widget_mut().on_event(
|
||||||
state,
|
state,
|
||||||
&event,
|
event.clone(),
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
cursor_virtual,
|
cursor_virtual,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -342,17 +340,18 @@ where
|
||||||
cursor_virtual = mouse::Cursor::Unavailable;
|
cursor_virtual = mouse::Cursor::Unavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
child.as_widget_mut().update(
|
child.as_widget_mut().on_event(
|
||||||
state,
|
state,
|
||||||
&event,
|
event.clone(),
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
cursor_virtual,
|
cursor_virtual,
|
||||||
renderer,
|
renderer,
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
)
|
||||||
}
|
})
|
||||||
|
.fold(event::Status::Ignored, event::Status::merge)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -427,19 +426,11 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
overlay::from_children(
|
overlay::from_children(&mut self.children, tree, layout, renderer, translation)
|
||||||
&mut self.children,
|
|
||||||
tree,
|
|
||||||
layout,
|
|
||||||
renderer,
|
|
||||||
viewport,
|
|
||||||
translation,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::iced;
|
use crate::iced;
|
||||||
|
use crate::iced_futures::futures;
|
||||||
use cctk::sctk::reexports::calloop;
|
use cctk::sctk::reexports::calloop;
|
||||||
use futures::{
|
use futures::{
|
||||||
SinkExt, StreamExt,
|
SinkExt, StreamExt,
|
||||||
channel::mpsc::{UnboundedReceiver, unbounded},
|
channel::mpsc::{UnboundedReceiver, unbounded},
|
||||||
};
|
};
|
||||||
use iced::Subscription;
|
use iced::Subscription;
|
||||||
use iced_futures::futures;
|
|
||||||
use iced_futures::stream;
|
use iced_futures::stream;
|
||||||
use std::{fmt::Debug, hash::Hash, thread::JoinHandle};
|
use std::{fmt::Debug, hash::Hash, thread::JoinHandle};
|
||||||
|
|
||||||
|
|
@ -14,15 +14,16 @@ use super::wayland_handler::wayland_handler;
|
||||||
pub fn activation_token_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
pub fn activation_token_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
id: I,
|
id: I,
|
||||||
) -> iced::Subscription<TokenUpdate> {
|
) -> iced::Subscription<TokenUpdate> {
|
||||||
Subscription::run_with(id, |_| {
|
Subscription::run_with_id(
|
||||||
|
id,
|
||||||
stream::channel(50, move |mut output| async move {
|
stream::channel(50, move |mut output| async move {
|
||||||
let mut state = State::Ready;
|
let mut state = State::Ready;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
state = start_listening(state, &mut output).await;
|
state = start_listening(state, &mut output).await;
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum State {
|
pub enum State {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ pub fn set_theme<M: Send + 'static>(theme: crate::Theme) -> iced::Task<crate::Ac
|
||||||
|
|
||||||
/// Sets the window mode to windowed.
|
/// Sets the window mode to windowed.
|
||||||
pub fn set_windowed<M>(id: window::Id) -> iced::Task<crate::Action<M>> {
|
pub fn set_windowed<M>(id: window::Id) -> iced::Task<crate::Action<M>> {
|
||||||
iced_runtime::window::set_mode(id, window::Mode::Windowed)
|
iced_runtime::window::change_mode(id, window::Mode::Windowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggles the windows' maximize state.
|
/// Toggles the windows' maximize state.
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub struct Core {
|
||||||
|
|
||||||
pub(crate) menu_bars: HashMap<crate::widget::Id, (Limits, Size)>,
|
pub(crate) menu_bars: HashMap<crate::widget::Id, (Limits, Size)>,
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
pub(crate) sync_window_border_radii_to_theme: bool,
|
pub(crate) sync_window_border_radii_to_theme: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +159,7 @@ impl Default for Core {
|
||||||
main_window: None,
|
main_window: None,
|
||||||
exit_on_main_window_closed: true,
|
exit_on_main_window_closed: true,
|
||||||
menu_bars: HashMap::new(),
|
menu_bars: HashMap::new(),
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
sync_window_border_radii_to_theme: true,
|
sync_window_border_radii_to_theme: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -493,12 +493,12 @@ impl Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO should we emit tasks setting the corner radius or unsetting it if this is changed?
|
// TODO should we emit tasks setting the corner radius or unsetting it if this is changed?
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
pub fn set_sync_window_border_radii_to_theme(&mut self, sync: bool) {
|
pub fn set_sync_window_border_radii_to_theme(&mut self, sync: bool) {
|
||||||
self.sync_window_border_radii_to_theme = sync;
|
self.sync_window_border_radii_to_theme = sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
pub fn sync_window_border_radii_to_theme(&self) -> bool {
|
pub fn sync_window_border_radii_to_theme(&self) -> bool {
|
||||||
self.sync_window_border_radii_to_theme
|
self.sync_window_border_radii_to_theme
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,9 @@ use {
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Message>> {
|
pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Message>> {
|
||||||
use iced_futures::futures::StreamExt;
|
use iced_futures::futures::StreamExt;
|
||||||
iced_futures::Subscription::run_with(TypeId::of::<DbusActivation>(), |_| {
|
iced_futures::Subscription::run_with_id(
|
||||||
iced::stream::channel(
|
TypeId::of::<DbusActivation>(),
|
||||||
10,
|
iced::stream::channel(10, move |mut output| async move {
|
||||||
move |mut output: Sender<crate::Action<App::Message>>| async move {
|
|
||||||
let mut single_instance: DbusActivation = DbusActivation::new();
|
let mut single_instance: DbusActivation = DbusActivation::new();
|
||||||
let mut rx = single_instance.rx();
|
let mut rx = single_instance.rx();
|
||||||
if let Ok(builder) = zbus::connection::Builder::session() {
|
if let Ok(builder) = zbus::connection::Builder::session() {
|
||||||
|
|
@ -66,16 +65,13 @@ pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Me
|
||||||
while let Some(mut msg) = rx.next().await {
|
while let Some(mut msg) = rx.next().await {
|
||||||
if let Some(token) = msg.activation_token.take() {
|
if let Some(token) = msg.activation_token.take() {
|
||||||
if let Err(err) = output
|
if let Err(err) = output
|
||||||
.send(crate::Action::Cosmic(crate::app::Action::Activate(
|
.send(crate::Action::Cosmic(crate::app::Action::Activate(token)))
|
||||||
token,
|
|
||||||
)))
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
tracing::error!(?err, "Failed to send message");
|
tracing::error!(?err, "Failed to send message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = output.send(crate::Action::DbusActivation(msg)).await
|
if let Err(err) = output.send(crate::Action::DbusActivation(msg)).await {
|
||||||
{
|
|
||||||
tracing::error!(?err, "Failed to send message");
|
tracing::error!(?err, "Failed to send message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,9 +83,8 @@ pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Me
|
||||||
loop {
|
loop {
|
||||||
iced::futures::pending!();
|
iced::futures::pending!();
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
)
|
)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -416,6 +416,7 @@ fn match_exec_basename(
|
||||||
};
|
};
|
||||||
|
|
||||||
let basename_lower = basename.to_ascii_lowercase();
|
let basename_lower = basename.to_ascii_lowercase();
|
||||||
|
|
||||||
if normalized
|
if normalized
|
||||||
.iter()
|
.iter()
|
||||||
.any(|candidate| candidate == &basename_lower)
|
.any(|candidate| candidate == &basename_lower)
|
||||||
|
|
@ -439,7 +440,8 @@ fn fallback_entry(context: &DesktopLookupContext<'_>) -> fde::DesktopEntry {
|
||||||
let name = context
|
let name = context
|
||||||
.title
|
.title
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| context.app_id.to_string(), |title| title.to_string());
|
.map(|title| title.to_string())
|
||||||
|
.unwrap_or_else(|| context.app_id.to_string());
|
||||||
entry.add_desktop_entry("Name".to_string(), name);
|
entry.add_desktop_entry("Name".to_string(), name);
|
||||||
entry
|
entry
|
||||||
}
|
}
|
||||||
|
|
@ -456,9 +458,7 @@ fn proton_or_wine_fallback(
|
||||||
) -> Option<fde::DesktopEntry> {
|
) -> Option<fde::DesktopEntry> {
|
||||||
let app_id = context.app_id.as_ref();
|
let app_id = context.app_id.as_ref();
|
||||||
let is_proton_game = app_id == "steam_app_default";
|
let is_proton_game = app_id == "steam_app_default";
|
||||||
let is_wine_entry = std::path::Path::new(app_id)
|
let is_wine_entry = app_id.ends_with(".exe");
|
||||||
.extension()
|
|
||||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("exe"));
|
|
||||||
|
|
||||||
if !is_proton_game && !is_wine_entry {
|
if !is_proton_game && !is_wine_entry {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -487,6 +487,10 @@ fn proton_or_wine_fallback(
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn candidate_desktop_ids(context: &DesktopLookupContext<'_>) -> Vec<String> {
|
fn candidate_desktop_ids(context: &DesktopLookupContext<'_>) -> Vec<String> {
|
||||||
|
const SUFFIXES: &[&str] = &[".desktop", ".Desktop", ".DESKTOP"];
|
||||||
|
let mut ordered = Vec::new();
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
|
||||||
fn push_candidate(seen: &mut HashSet<String>, ordered: &mut Vec<String>, candidate: &str) {
|
fn push_candidate(seen: &mut HashSet<String>, ordered: &mut Vec<String>, candidate: &str) {
|
||||||
let trimmed = candidate.trim();
|
let trimmed = candidate.trim();
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty() {
|
||||||
|
|
@ -527,13 +531,13 @@ fn candidate_desktop_ids(context: &DesktopLookupContext<'_>) -> Vec<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if trimmed.contains('.')
|
if trimmed.contains('.') {
|
||||||
&& let Some(last) = trimmed.rsplit('.').next()
|
if let Some(last) = trimmed.rsplit('.').next() {
|
||||||
{
|
|
||||||
if last.len() >= 2 {
|
if last.len() >= 2 {
|
||||||
push_candidate(seen, ordered, last);
|
push_candidate(seen, ordered, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if trimmed.contains('-') {
|
if trimmed.contains('-') {
|
||||||
push_candidate(seen, ordered, &trimmed.replace('-', "_"));
|
push_candidate(seen, ordered, &trimmed.replace('-', "_"));
|
||||||
|
|
@ -542,20 +546,13 @@ fn candidate_desktop_ids(context: &DesktopLookupContext<'_>) -> Vec<String> {
|
||||||
push_candidate(seen, ordered, &trimmed.replace('_', "-"));
|
push_candidate(seen, ordered, &trimmed.replace('_', "-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for token in
|
for token in trimmed.split(|c: char| matches!(c, '.' | '-' | '_' | '@' | ' ')) {
|
||||||
trimmed.split(|c: char| matches!(c, '.' | '-' | '_' | '@') || c.is_whitespace())
|
|
||||||
{
|
|
||||||
if token.len() >= 2 && token != trimmed {
|
if token.len() >= 2 && token != trimmed {
|
||||||
push_candidate(seen, ordered, token);
|
push_candidate(seen, ordered, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SUFFIXES: &[&str] = &[".desktop", ".Desktop", ".DESKTOP"];
|
|
||||||
|
|
||||||
let mut ordered = Vec::new();
|
|
||||||
let mut seen = HashSet::new();
|
|
||||||
|
|
||||||
add_variants(
|
add_variants(
|
||||||
&mut seen,
|
&mut seen,
|
||||||
&mut ordered,
|
&mut ordered,
|
||||||
|
|
@ -789,7 +786,7 @@ pub async fn spawn_desktop_exec<S, I, K, V>(
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| String::from("cosmic-term"));
|
.unwrap_or_else(|| String::from("cosmic-term"));
|
||||||
|
|
||||||
term_exec = format!("{term} -e {}", exec.as_ref());
|
term_exec = format!("{term} -- {}", exec.as_ref());
|
||||||
&term_exec
|
&term_exec
|
||||||
} else {
|
} else {
|
||||||
exec.as_ref()
|
exec.as_ref()
|
||||||
|
|
@ -918,20 +915,12 @@ mod tests {
|
||||||
let candidates = candidate_desktop_ids(&ctx);
|
let candidates = candidate_desktop_ids(&ctx);
|
||||||
|
|
||||||
assert_eq!(candidates.first().unwrap(), "com.example.App.desktop");
|
assert_eq!(candidates.first().unwrap(), "com.example.App.desktop");
|
||||||
for test in [
|
assert!(candidates.contains(&"com.example.App".to_string()));
|
||||||
"com.example.App",
|
assert!(candidates.contains(&"com-example-App".to_string()));
|
||||||
"com-example-App",
|
assert!(candidates.contains(&"com_example_App".to_string()));
|
||||||
"com_example_App",
|
assert!(candidates.contains(&"Example App".to_string()));
|
||||||
"Example App",
|
assert!(candidates.contains(&"Example".to_string()));
|
||||||
"Example",
|
assert!(candidates.contains(&"App".to_string()));
|
||||||
"App",
|
|
||||||
] {
|
|
||||||
assert!(
|
|
||||||
candidates
|
|
||||||
.iter()
|
|
||||||
.any(|c| c.to_ascii_lowercase() == test.to_ascii_lowercase()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -996,7 +985,7 @@ Icon=vmware-workstation\n\
|
||||||
|
|
||||||
let resolved = resolve_desktop_entry(&mut cache, &ctx, &DesktopResolveOptions::default());
|
let resolved = resolve_desktop_entry(&mut cache, &ctx, &DesktopResolveOptions::default());
|
||||||
|
|
||||||
assert_eq!(resolved.id(), "vmware-workstation");
|
assert_eq!(resolved.id(), "vmware-workstation.desktop");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,4 @@ impl iced::Executor for Executor {
|
||||||
let _guard = self.0.enter();
|
let _guard = self.0.enter();
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
|
|
||||||
self.0.block_on(future)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,4 @@ impl iced::Executor for Executor {
|
||||||
let _guard = self.0.enter();
|
let _guard = self.0.enter();
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
|
|
||||||
self.0.block_on(future)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
src/ext.rs
66
src/ext.rs
|
|
@ -19,6 +19,72 @@ impl<Message: 'static> ElementExt for crate::Element<'_, Message> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Additional methods for the [`Column`] and [`Row`] widgets.
|
||||||
|
pub trait CollectionWidget<'a, Message: 'a>:
|
||||||
|
Widget<Message, crate::Theme, crate::Renderer>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
/// Moves all the elements of `other` into `self`, leaving `other` empty.
|
||||||
|
#[must_use]
|
||||||
|
fn append<E>(self, other: &mut Vec<E>) -> Self
|
||||||
|
where
|
||||||
|
E: Into<crate::Element<'a, Message>>;
|
||||||
|
|
||||||
|
/// Appends all elements in an iterator to the widget.
|
||||||
|
#[must_use]
|
||||||
|
fn extend<E>(mut self, iterator: impl Iterator<Item = E>) -> Self
|
||||||
|
where
|
||||||
|
E: Into<crate::Element<'a, Message>>,
|
||||||
|
{
|
||||||
|
for item in iterator {
|
||||||
|
self = self.push(item.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes an element into the widget.
|
||||||
|
#[must_use]
|
||||||
|
fn push(self, element: impl Into<crate::Element<'a, Message>>) -> Self;
|
||||||
|
|
||||||
|
/// Conditionally pushes an element to the widget.
|
||||||
|
#[must_use]
|
||||||
|
fn push_maybe(self, element: Option<impl Into<crate::Element<'a, Message>>>) -> Self {
|
||||||
|
if let Some(element) = element {
|
||||||
|
self.push(element.into())
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message: 'a> CollectionWidget<'a, Message> for crate::widget::Column<'a, Message> {
|
||||||
|
fn append<E>(self, other: &mut Vec<E>) -> Self
|
||||||
|
where
|
||||||
|
E: Into<crate::Element<'a, Message>>,
|
||||||
|
{
|
||||||
|
self.extend(other.drain(..).map(Into::into))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(self, element: impl Into<crate::Element<'a, Message>>) -> Self {
|
||||||
|
self.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message: 'a> CollectionWidget<'a, Message> for crate::widget::Row<'a, Message> {
|
||||||
|
fn append<E>(self, other: &mut Vec<E>) -> Self
|
||||||
|
where
|
||||||
|
E: Into<crate::Element<'a, Message>>,
|
||||||
|
{
|
||||||
|
self.extend(other.drain(..).map(Into::into))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(self, element: impl Into<crate::Element<'a, Message>>) -> Self {
|
||||||
|
self.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ColorExt {
|
pub trait ColorExt {
|
||||||
/// Combines color with background to create appearance of transparency.
|
/// Combines color with background to create appearance of transparency.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
||||||
29
src/lib.rs
29
src/lib.rs
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
#![cfg_attr(target_os = "redox", feature(lazy_cell))]
|
#![cfg_attr(target_os = "redox", feature(lazy_cell))]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
|
|
||||||
/// Recommended default imports.
|
/// Recommended default imports.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|
@ -19,8 +18,6 @@ pub use apply::{Also, Apply};
|
||||||
pub mod action;
|
pub mod action;
|
||||||
pub use action::Action;
|
pub use action::Action;
|
||||||
|
|
||||||
pub mod anim;
|
|
||||||
|
|
||||||
#[cfg(feature = "winit")]
|
#[cfg(feature = "winit")]
|
||||||
pub mod app;
|
pub mod app;
|
||||||
#[cfg(feature = "winit")]
|
#[cfg(feature = "winit")]
|
||||||
|
|
@ -67,6 +64,29 @@ pub mod font;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use iced;
|
pub use iced;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use iced_core;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use iced_futures;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use iced_renderer;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use iced_runtime;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use iced_widget;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
#[cfg(feature = "winit")]
|
||||||
|
pub use iced_winit;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
pub use iced_wgpu;
|
||||||
|
|
||||||
pub mod icon_theme;
|
pub mod icon_theme;
|
||||||
pub mod keyboard_nav;
|
pub mod keyboard_nav;
|
||||||
|
|
||||||
|
|
@ -78,8 +98,7 @@ pub(crate) mod malloc;
|
||||||
#[cfg(all(feature = "process", not(windows)))]
|
#[cfg(all(feature = "process", not(windows)))]
|
||||||
pub mod process;
|
pub mod process;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[cfg(feature = "wayland")]
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
|
||||||
pub use cctk;
|
pub use cctk;
|
||||||
|
|
||||||
pub mod surface;
|
pub mod surface;
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,25 @@ use iced::window;
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
/// Used to produce a destroy popup message from within a widget.
|
/// Used to produce a destroy popup message from within a widget.
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn destroy_popup(id: iced_core::window::Id) -> Action {
|
pub fn destroy_popup(id: iced_core::window::Id) -> Action {
|
||||||
Action::DestroyPopup(id)
|
Action::DestroyPopup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn destroy_subsurface(id: iced_core::window::Id) -> Action {
|
pub fn destroy_subsurface(id: iced_core::window::Id) -> Action {
|
||||||
Action::DestroySubsurface(id)
|
Action::DestroySubsurface(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn destroy_window(id: iced_core::window::Id) -> Action {
|
pub fn destroy_window(id: iced_core::window::Id) -> Action {
|
||||||
Action::DestroyWindow(id)
|
Action::DestroyWindow(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn app_window<App: Application>(
|
pub fn app_window<App: Application>(
|
||||||
settings: impl Fn(&mut App) -> window::Settings + Send + Sync + 'static,
|
settings: impl Fn(&mut App) -> window::Settings + Send + Sync + 'static,
|
||||||
|
|
@ -60,7 +60,7 @@ pub fn app_window<App: Application>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to create a window message from within a widget.
|
/// Used to create a window message from within a widget.
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn simple_window<Message: 'static>(
|
pub fn simple_window<Message: 'static>(
|
||||||
settings: impl Fn() -> window::Settings + Send + Sync + 'static,
|
settings: impl Fn() -> window::Settings + Send + Sync + 'static,
|
||||||
|
|
@ -92,7 +92,7 @@ pub fn simple_window<Message: 'static>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn app_popup<App: Application>(
|
pub fn app_popup<App: Application>(
|
||||||
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
|
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
|
||||||
|
|
@ -126,7 +126,7 @@ pub fn app_popup<App: Application>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to create a subsurface message from within a widget.
|
/// Used to create a subsurface message from within a widget.
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn simple_subsurface<Message: 'static, V>(
|
pub fn simple_subsurface<Message: 'static, V>(
|
||||||
settings: impl Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
|
settings: impl Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
|
||||||
|
|
@ -155,7 +155,7 @@ pub fn simple_subsurface<Message: 'static, V>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to create a popup message from within a widget.
|
/// Used to create a popup message from within a widget.
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn simple_popup<Message: 'static>(
|
pub fn simple_popup<Message: 'static>(
|
||||||
settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
|
settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
|
||||||
|
|
@ -186,7 +186,7 @@ pub fn simple_popup<Message: 'static>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn subsurface<App: Application>(
|
pub fn subsurface<App: Application>(
|
||||||
settings: impl Fn(
|
settings: impl Fn(
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@ pub enum Action {
|
||||||
),
|
),
|
||||||
/// Destroy a subsurface with a view function
|
/// Destroy a subsurface with a view function
|
||||||
DestroyPopup(iced::window::Id),
|
DestroyPopup(iced::window::Id),
|
||||||
/// Destroys the global tooltip popup subsurface
|
|
||||||
DestroyTooltipPopup,
|
|
||||||
|
|
||||||
/// Create a window with a view function accepting the App as a parameter
|
/// Create a window with a view function accepting the App as a parameter
|
||||||
AppWindow(
|
AppWindow(
|
||||||
|
|
@ -87,7 +85,6 @@ impl std::fmt::Debug for Action {
|
||||||
}
|
}
|
||||||
Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(),
|
Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(),
|
||||||
Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(),
|
Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(),
|
||||||
Self::DestroyTooltipPopup => f.debug_tuple("DestroyTooltipPopup").finish(),
|
|
||||||
Self::ResponsiveMenuBar {
|
Self::ResponsiveMenuBar {
|
||||||
menu_bar,
|
menu_bar,
|
||||||
limits,
|
limits,
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,7 @@ impl DefaultStyle for Theme {
|
||||||
fn default_style(&self) -> Appearance {
|
fn default_style(&self) -> Appearance {
|
||||||
let cosmic = self.cosmic();
|
let cosmic = self.cosmic();
|
||||||
Appearance {
|
Appearance {
|
||||||
icon_color: cosmic.on_bg_color().into(),
|
icon_color: cosmic.bg_color().into(),
|
||||||
background_color: cosmic.bg_color().into(),
|
background_color: cosmic.bg_color().into(),
|
||||||
text_color: cosmic.on_bg_color().into(),
|
text_color: cosmic.on_bg_color().into(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@ pub enum Desktop {
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn desktop_settings() -> iced_futures::Subscription<Desktop> {
|
pub fn desktop_settings() -> iced_futures::Subscription<Desktop> {
|
||||||
iced_futures::Subscription::run(|| {
|
iced_futures::Subscription::run_with_id(
|
||||||
stream::channel(10, |mut tx: futures::channel::mpsc::Sender<Desktop>| {
|
std::any::TypeId::of::<Desktop>(),
|
||||||
|
stream::channel(10, |mut tx| {
|
||||||
async move {
|
async move {
|
||||||
let mut attempts = 0;
|
let mut attempts = 0;
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -98,6 +99,6 @@ pub fn desktop_settings() -> iced_futures::Subscription<Desktop> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use crate::theme::{CosmicComponent, TRANSPARENT_COMPONENT, Theme};
|
||||||
use cosmic_theme::composite::over;
|
use cosmic_theme::composite::over;
|
||||||
use iced::{
|
use iced::{
|
||||||
overlay::menu,
|
overlay::menu,
|
||||||
theme::Base,
|
|
||||||
widget::{
|
widget::{
|
||||||
button as iced_button, checkbox as iced_checkbox, combo_box, container as iced_container,
|
button as iced_button, checkbox as iced_checkbox, combo_box, container as iced_container,
|
||||||
pane_grid, pick_list, progress_bar, radio, rule, scrollable,
|
pane_grid, pick_list, progress_bar, radio, rule, scrollable,
|
||||||
|
|
@ -16,7 +15,7 @@ use iced::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use iced_core::{Background, Border, Color, Shadow, Vector};
|
use iced_core::{Background, Border, Color, Shadow, Vector};
|
||||||
use iced_widget::{pane_grid::Highlight, scrollable::AutoScroll, text_editor, text_input};
|
use iced_widget::{pane_grid::Highlight, text_editor, text_input};
|
||||||
use palette::WithAlpha;
|
use palette::WithAlpha;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
@ -37,13 +36,13 @@ pub mod application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style(theme: &Theme) -> iced::theme::Style {
|
pub fn appearance(theme: &Theme) -> Appearance {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
|
|
||||||
iced::theme::Style {
|
Appearance {
|
||||||
|
icon_color: cosmic.bg_color().into(),
|
||||||
background_color: cosmic.bg_color().into(),
|
background_color: cosmic.bg_color().into(),
|
||||||
text_color: cosmic.on_bg_color().into(),
|
text_color: cosmic.on_bg_color().into(),
|
||||||
icon_color: cosmic.on_bg_color().into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -423,7 +422,6 @@ impl<'a> Container<'a> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,7 +436,6 @@ impl<'a> Container<'a> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,7 +450,6 @@ impl<'a> Container<'a> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -497,7 +493,6 @@ impl iced_container::Catalog for Theme {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Container::List => {
|
Container::List => {
|
||||||
|
|
@ -511,7 +506,6 @@ impl iced_container::Catalog for Theme {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -558,7 +552,6 @@ impl iced_container::Catalog for Theme {
|
||||||
.into(),
|
.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
snap: true,
|
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -589,7 +582,6 @@ impl iced_container::Catalog for Theme {
|
||||||
radius: cosmic.corner_radii.radius_s.into(),
|
radius: cosmic.corner_radii.radius_s.into(),
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Container::Tooltip => iced_container::Style {
|
Container::Tooltip => iced_container::Style {
|
||||||
|
|
@ -601,7 +593,6 @@ impl iced_container::Catalog for Theme {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Container::Card => {
|
Container::Card => {
|
||||||
|
|
@ -619,7 +610,6 @@ impl iced_container::Catalog for Theme {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
cosmic_theme::Layer::Primary => iced_container::Style {
|
cosmic_theme::Layer::Primary => iced_container::Style {
|
||||||
icon_color: Some(Color::from(cosmic.primary.component.on)),
|
icon_color: Some(Color::from(cosmic.primary.component.on)),
|
||||||
|
|
@ -632,7 +622,6 @@ impl iced_container::Catalog for Theme {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
cosmic_theme::Layer::Secondary => iced_container::Style {
|
cosmic_theme::Layer::Secondary => iced_container::Style {
|
||||||
icon_color: Some(Color::from(cosmic.secondary.component.on)),
|
icon_color: Some(Color::from(cosmic.secondary.component.on)),
|
||||||
|
|
@ -645,7 +634,6 @@ impl iced_container::Catalog for Theme {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -664,7 +652,6 @@ impl iced_container::Catalog for Theme {
|
||||||
offset: Vector::new(0.0, 4.0),
|
offset: Vector::new(0.0, 4.0),
|
||||||
blur_radius: 16.0,
|
blur_radius: 16.0,
|
||||||
},
|
},
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -804,7 +791,6 @@ impl menu::Catalog for Theme {
|
||||||
},
|
},
|
||||||
selected_text_color: cosmic.accent_text_color().into(),
|
selected_text_color: cosmic.accent_text_color().into(),
|
||||||
selected_background: Background::Color(cosmic.background.component.hover.into()),
|
selected_background: Background::Color(cosmic.background.component.hover.into()),
|
||||||
shadow: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -844,7 +830,7 @@ impl pick_list::Catalog for Theme {
|
||||||
background: Background::Color(cosmic.background.base.into()),
|
background: Background::Color(cosmic.background.base.into()),
|
||||||
..appearance
|
..appearance
|
||||||
},
|
},
|
||||||
pick_list::Status::Opened { is_hovered: _ } => appearance,
|
pick_list::Status::Opened => appearance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -934,8 +920,6 @@ impl toggler::Catalog for Theme {
|
||||||
background_border_color: Color::TRANSPARENT,
|
background_border_color: Color::TRANSPARENT,
|
||||||
foreground_border_width: 0.0,
|
foreground_border_width: 0.0,
|
||||||
foreground_border_color: Color::TRANSPARENT,
|
foreground_border_color: Color::TRANSPARENT,
|
||||||
text_color: None,
|
|
||||||
padding_ratio: 0.0,
|
|
||||||
};
|
};
|
||||||
match status {
|
match status {
|
||||||
toggler::Status::Active { is_toggled } => active,
|
toggler::Status::Active { is_toggled } => active,
|
||||||
|
|
@ -958,9 +942,9 @@ impl toggler::Catalog for Theme {
|
||||||
..active
|
..active
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toggler::Status::Disabled { is_toggled } => {
|
toggler::Status::Disabled => {
|
||||||
active.background = active.background.scale_alpha(0.5);
|
active.background.a /= 2.;
|
||||||
active.foreground = active.foreground.scale_alpha(0.5);
|
active.foreground.a /= 2.;
|
||||||
active
|
active
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1102,21 +1086,21 @@ impl rule::Catalog for Theme {
|
||||||
match class {
|
match class {
|
||||||
Rule::Default => rule::Style {
|
Rule::Default => rule::Style {
|
||||||
color: self.current_container().divider.into(),
|
color: self.current_container().divider.into(),
|
||||||
|
width: 1,
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
fill_mode: rule::FillMode::Full,
|
fill_mode: rule::FillMode::Full,
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Rule::LightDivider => rule::Style {
|
Rule::LightDivider => rule::Style {
|
||||||
color: self.current_container().divider.into(),
|
color: self.current_container().divider.into(),
|
||||||
|
width: 1,
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
fill_mode: rule::FillMode::Padded(8),
|
fill_mode: rule::FillMode::Padded(8),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Rule::HeavyDivider => rule::Style {
|
Rule::HeavyDivider => rule::Style {
|
||||||
color: self.current_container().divider.into(),
|
color: self.current_container().divider.into(),
|
||||||
|
width: 4,
|
||||||
radius: 2.0.into(),
|
radius: 2.0.into(),
|
||||||
fill_mode: rule::FillMode::Full,
|
fill_mode: rule::FillMode::Full,
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Rule::Custom(f) => f(self),
|
Rule::Custom(f) => f(self),
|
||||||
}
|
}
|
||||||
|
|
@ -1142,10 +1126,7 @@ impl scrollable::Catalog for Theme {
|
||||||
|
|
||||||
fn style(&self, class: &Self::Class<'_>, status: scrollable::Status) -> scrollable::Style {
|
fn style(&self, class: &Self::Class<'_>, status: scrollable::Status) -> scrollable::Style {
|
||||||
match status {
|
match status {
|
||||||
scrollable::Status::Active {
|
scrollable::Status::Active => {
|
||||||
is_horizontal_scrollbar_disabled,
|
|
||||||
is_vertical_scrollbar_disabled,
|
|
||||||
} => {
|
|
||||||
let cosmic = self.cosmic();
|
let cosmic = self.cosmic();
|
||||||
let neutral_5 = cosmic.palette.neutral_5.with_alpha(0.7);
|
let neutral_5 = cosmic.palette.neutral_5.with_alpha(0.7);
|
||||||
let neutral_6 = cosmic.palette.neutral_6.with_alpha(0.7);
|
let neutral_6 = cosmic.palette.neutral_6.with_alpha(0.7);
|
||||||
|
|
@ -1158,7 +1139,7 @@ impl scrollable::Catalog for Theme {
|
||||||
},
|
},
|
||||||
background: None,
|
background: None,
|
||||||
scroller: scrollable::Scroller {
|
scroller: scrollable::Scroller {
|
||||||
background: if cosmic.is_dark {
|
color: if cosmic.is_dark {
|
||||||
neutral_6.into()
|
neutral_6.into()
|
||||||
} else {
|
} else {
|
||||||
neutral_5.into()
|
neutral_5.into()
|
||||||
|
|
@ -1176,7 +1157,7 @@ impl scrollable::Catalog for Theme {
|
||||||
},
|
},
|
||||||
background: None,
|
background: None,
|
||||||
scroller: scrollable::Scroller {
|
scroller: scrollable::Scroller {
|
||||||
background: if cosmic.is_dark {
|
color: if cosmic.is_dark {
|
||||||
neutral_6.into()
|
neutral_6.into()
|
||||||
} else {
|
} else {
|
||||||
neutral_5.into()
|
neutral_5.into()
|
||||||
|
|
@ -1188,13 +1169,6 @@ impl scrollable::Catalog for Theme {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gap: None,
|
gap: None,
|
||||||
// TODO: what is auto scroll?
|
|
||||||
auto_scroll: AutoScroll {
|
|
||||||
background: Color::TRANSPARENT.into(),
|
|
||||||
border: Border::default(),
|
|
||||||
shadow: Shadow::default(),
|
|
||||||
icon: Color::TRANSPARENT.into(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let small_widget_container = self.current_container().small_widget.with_alpha(0.7);
|
let small_widget_container = self.current_container().small_widget.with_alpha(0.7);
|
||||||
|
|
||||||
|
|
@ -1226,7 +1200,7 @@ impl scrollable::Catalog for Theme {
|
||||||
},
|
},
|
||||||
background: None,
|
background: None,
|
||||||
scroller: scrollable::Scroller {
|
scroller: scrollable::Scroller {
|
||||||
background: if cosmic.is_dark {
|
color: if cosmic.is_dark {
|
||||||
neutral_6.into()
|
neutral_6.into()
|
||||||
} else {
|
} else {
|
||||||
neutral_5.into()
|
neutral_5.into()
|
||||||
|
|
@ -1244,7 +1218,7 @@ impl scrollable::Catalog for Theme {
|
||||||
},
|
},
|
||||||
background: None,
|
background: None,
|
||||||
scroller: scrollable::Scroller {
|
scroller: scrollable::Scroller {
|
||||||
background: if cosmic.is_dark {
|
color: if cosmic.is_dark {
|
||||||
neutral_6.into()
|
neutral_6.into()
|
||||||
} else {
|
} else {
|
||||||
neutral_5.into()
|
neutral_5.into()
|
||||||
|
|
@ -1256,13 +1230,6 @@ impl scrollable::Catalog for Theme {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gap: None,
|
gap: None,
|
||||||
// TODO: what is auto scroll?
|
|
||||||
auto_scroll: AutoScroll {
|
|
||||||
background: Color::TRANSPARENT.into(),
|
|
||||||
border: Border::default(),
|
|
||||||
shadow: Shadow::default(),
|
|
||||||
icon: Color::TRANSPARENT.into(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(class, Scrollable::Permanent) {
|
if matches!(class, Scrollable::Permanent) {
|
||||||
|
|
@ -1433,7 +1400,7 @@ impl text_input::Catalog for Theme {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text_input::Status::Focused { is_hovered } => {
|
text_input::Status::Focused => {
|
||||||
let bg = self.current_container().small_widget.with_alpha(0.25);
|
let bg = self.current_container().small_widget.with_alpha(0.25);
|
||||||
|
|
||||||
match class {
|
match class {
|
||||||
|
|
@ -1510,8 +1477,7 @@ impl iced_widget::text_editor::Catalog for Theme {
|
||||||
let selection = cosmic.accent.base.into();
|
let selection = cosmic.accent.base.into();
|
||||||
let value = cosmic.palette.neutral_9.into();
|
let value = cosmic.palette.neutral_9.into();
|
||||||
let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
|
let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
|
||||||
let icon: Color = cosmic.background.on.into();
|
let icon = cosmic.background.on.into();
|
||||||
// TODO do we need to add icon color back?
|
|
||||||
|
|
||||||
match status {
|
match status {
|
||||||
iced_widget::text_editor::Status::Active
|
iced_widget::text_editor::Status::Active
|
||||||
|
|
@ -1523,23 +1489,23 @@ impl iced_widget::text_editor::Catalog for Theme {
|
||||||
width: f32::from(cosmic.space_xxxs()),
|
width: f32::from(cosmic.space_xxxs()),
|
||||||
color: iced::Color::from(cosmic.bg_divider()),
|
color: iced::Color::from(cosmic.bg_divider()),
|
||||||
},
|
},
|
||||||
|
icon,
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
selection,
|
selection,
|
||||||
},
|
},
|
||||||
iced_widget::text_editor::Status::Focused { is_hovered } => {
|
iced_widget::text_editor::Status::Focused => iced_widget::text_editor::Style {
|
||||||
iced_widget::text_editor::Style {
|
|
||||||
background: iced::Color::from(cosmic.bg_color()).into(),
|
background: iced::Color::from(cosmic.bg_color()).into(),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: cosmic.corner_radii.radius_0.into(),
|
radius: cosmic.corner_radii.radius_0.into(),
|
||||||
width: f32::from(cosmic.space_xxxs()),
|
width: f32::from(cosmic.space_xxxs()),
|
||||||
color: iced::Color::from(cosmic.accent.base),
|
color: iced::Color::from(cosmic.accent.base),
|
||||||
},
|
},
|
||||||
|
icon,
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
selection,
|
selection,
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1556,21 +1522,6 @@ impl iced_widget::markdown::Catalog for Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl iced_widget::table::Catalog for Theme {
|
|
||||||
type Class<'a> = iced_widget::table::StyleFn<'a, Self>;
|
|
||||||
|
|
||||||
fn default<'a>() -> Self::Class<'a> {
|
|
||||||
Box::new(|theme| iced_widget::table::Style {
|
|
||||||
separator_x: theme.current_container().divider.into(),
|
|
||||||
separator_y: theme.current_container().divider.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self, class: &Self::Class<'_>) -> iced_widget::table::Style {
|
|
||||||
class(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "qr_code")]
|
#[cfg(feature = "qr_code")]
|
||||||
impl iced_widget::qr_code::Catalog for Theme {
|
impl iced_widget::qr_code::Catalog for Theme {
|
||||||
type Class<'a> = iced_widget::qr_code::StyleFn<'a, Self>;
|
type Class<'a> = iced_widget::qr_code::StyleFn<'a, Self>;
|
||||||
|
|
@ -1588,50 +1539,3 @@ impl iced_widget::qr_code::Catalog for Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl combo_box::Catalog for Theme {}
|
impl combo_box::Catalog for Theme {}
|
||||||
|
|
||||||
impl Base for Theme {
|
|
||||||
fn default(preference: iced::theme::Mode) -> Self {
|
|
||||||
match preference {
|
|
||||||
iced::theme::Mode::Light => Theme::light(),
|
|
||||||
iced::theme::Mode::Dark | iced::theme::Mode::None => Theme::dark(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mode(&self) -> iced::theme::Mode {
|
|
||||||
if self.theme_type.is_dark() {
|
|
||||||
iced::theme::Mode::Dark
|
|
||||||
} else {
|
|
||||||
iced::theme::Mode::Light
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn base(&self) -> iced::theme::Style {
|
|
||||||
iced::theme::Style {
|
|
||||||
background_color: self.cosmic().bg_color().into(),
|
|
||||||
text_color: self.cosmic().on_bg_color().into(),
|
|
||||||
icon_color: self.cosmic().on_bg_color().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn palette(&self) -> Option<iced::theme::Palette> {
|
|
||||||
Some(iced::theme::Palette {
|
|
||||||
primary: self.cosmic().accent.base.into(),
|
|
||||||
success: self.cosmic().success.base.into(),
|
|
||||||
warning: self.cosmic().warning.base.into(),
|
|
||||||
danger: self.cosmic().destructive.base.into(),
|
|
||||||
background: iced::Color::from(self.cosmic().bg_color()),
|
|
||||||
text: iced::Color::from(self.cosmic().on_bg_color()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
match &self.theme_type {
|
|
||||||
crate::theme::ThemeType::Dark => "Cosmic Dark Theme",
|
|
||||||
crate::theme::ThemeType::Light => "Cosmic Light Theme",
|
|
||||||
crate::theme::ThemeType::HighContrastDark => "Cosmic High Contrast Dark Theme",
|
|
||||||
crate::theme::ThemeType::HighContrastLight => "Cosmic High Contrast Light Theme",
|
|
||||||
crate::theme::ThemeType::Custom(theme) => "Custom Cosmic Theme",
|
|
||||||
crate::theme::ThemeType::System { prefer_dark, theme } => &theme.name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ mod text_input;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use self::text_input::TextInput;
|
pub use self::text_input::TextInput;
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
pub mod tooltip;
|
pub mod tooltip;
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
#[cfg(all(feature = "wayland", feature = "winit"))]
|
||||||
pub use tooltip::Tooltip;
|
pub use tooltip::Tooltip;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Apply, Element, fl,
|
Apply, Element, fl,
|
||||||
iced::{Alignment, Length},
|
iced::{Alignment, Length},
|
||||||
widget::{self, space},
|
widget::{self, horizontal_space},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, derive_setters::Setters)]
|
#[derive(Debug, Default, Clone, derive_setters::Setters)]
|
||||||
|
|
@ -47,40 +47,32 @@ pub struct About {
|
||||||
fn add_contributors(contributors: Vec<(&str, &str)>) -> Vec<(String, String)> {
|
fn add_contributors(contributors: Vec<(&str, &str)>) -> Vec<(String, String)> {
|
||||||
contributors
|
contributors
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, email)| (name.into(), format!("mailto:{email}")))
|
.map(|(name, email)| (name.to_string(), format!("mailto:{email}")))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! set_contributors {
|
||||||
|
($field:ident, $doc:expr) => {
|
||||||
|
#[doc = $doc]
|
||||||
|
pub fn $field(mut self, contributors: impl Into<Vec<(&'a str, &'a str)>>) -> Self {
|
||||||
|
self.$field = add_contributors(contributors.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> About {
|
impl<'a> About {
|
||||||
/// Artists who contributed to the application.
|
set_contributors!(artists, "Artists who contributed to the application.");
|
||||||
pub fn artists(mut self, contributors: impl Into<Vec<(&'a str, &'a str)>>) -> Self {
|
set_contributors!(designers, "Designers who contributed to the application.");
|
||||||
self.artists = add_contributors(contributors.into());
|
set_contributors!(developers, "Developers who contributed to the application.");
|
||||||
self
|
set_contributors!(
|
||||||
}
|
documenters,
|
||||||
|
"Documenters who contributed to the application."
|
||||||
/// Designers who contributed to the application.
|
);
|
||||||
pub fn designers(mut self, contributors: impl Into<Vec<(&'a str, &'a str)>>) -> Self {
|
set_contributors!(
|
||||||
self.designers = add_contributors(contributors.into());
|
translators,
|
||||||
self
|
"Translators who contributed to the application."
|
||||||
}
|
);
|
||||||
|
|
||||||
/// Developers who contributed to the application.
|
|
||||||
pub fn developers(mut self, contributors: impl Into<Vec<(&'a str, &'a str)>>) -> Self {
|
|
||||||
self.developers = add_contributors(contributors.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Documenters who contributed to the application.
|
|
||||||
pub fn documenters(mut self, contributors: impl Into<Vec<(&'a str, &'a str)>>) -> Self {
|
|
||||||
self.documenters = add_contributors(contributors.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translators who contributed to the application.
|
|
||||||
pub fn translators(mut self, contributors: impl Into<Vec<(&'a str, &'a str)>>) -> Self {
|
|
||||||
self.translators = add_contributors(contributors.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Links associated with the application.
|
/// Links associated with the application.
|
||||||
pub fn links<K: Into<String>, V: Into<String>>(
|
pub fn links<K: Into<String>, V: Into<String>>(
|
||||||
|
|
@ -105,9 +97,9 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
} = crate::theme::spacing();
|
} = crate::theme::spacing();
|
||||||
|
|
||||||
let section_button = |name: &'a str, url: &'a str| -> Element<'a, Message> {
|
let section_button = |name: &'a str, url: &'a str| -> Element<'a, Message> {
|
||||||
widget::row::with_capacity(3)
|
widget::row()
|
||||||
.push(widget::text(name))
|
.push(widget::text(name))
|
||||||
.push(space::horizontal())
|
.push(horizontal_space())
|
||||||
.push_maybe(
|
.push_maybe(
|
||||||
(!url.is_empty()).then_some(crate::widget::icon::from_name("link-symbolic").icon()),
|
(!url.is_empty()).then_some(crate::widget::icon::from_name("link-symbolic").icon()),
|
||||||
)
|
)
|
||||||
|
|
@ -166,7 +158,7 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
let copyright = about.copyright.as_ref().map(widget::text::body);
|
let copyright = about.copyright.as_ref().map(widget::text::body);
|
||||||
let comments = about.comments.as_ref().map(widget::text::body);
|
let comments = about.comments.as_ref().map(widget::text::body);
|
||||||
|
|
||||||
widget::column::with_capacity(10)
|
widget::column()
|
||||||
.push_maybe(header)
|
.push_maybe(header)
|
||||||
.push_maybe(links_section)
|
.push_maybe(links_section)
|
||||||
.push_maybe(developers_section)
|
.push_maybe(developers_section)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use iced::Size;
|
use iced::Size;
|
||||||
use iced::widget::Container;
|
use iced::widget::Container;
|
||||||
use iced_core::event::Event;
|
use iced_core::event::{self, Event};
|
||||||
use iced_core::layout;
|
use iced_core::layout;
|
||||||
use iced_core::mouse;
|
use iced_core::mouse;
|
||||||
use iced_core::overlay;
|
use iced_core::overlay;
|
||||||
|
|
@ -172,7 +172,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -186,7 +186,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
|
@ -195,18 +195,18 @@ where
|
||||||
self.container.operate(tree, layout, renderer, operation);
|
self.container.operate(tree, layout, renderer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: mouse::Cursor,
|
cursor_position: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
self.container.update(
|
self.container.on_event(
|
||||||
tree,
|
tree,
|
||||||
event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
|
|
@ -254,13 +254,11 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
|
||||||
self.container
|
self.container.overlay(tree, layout, renderer, translation)
|
||||||
.overlay(tree, layout, renderer, viewport, translation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use iced_core::layout;
|
||||||
use iced_core::mouse;
|
use iced_core::mouse;
|
||||||
use iced_core::overlay;
|
use iced_core::overlay;
|
||||||
use iced_core::renderer;
|
use iced_core::renderer;
|
||||||
use iced_core::widget::{Id, Operation, Tree};
|
use iced_core::widget::{Id, Tree};
|
||||||
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
|
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
|
||||||
pub use iced_widget::container::{Catalog, Style};
|
pub use iced_widget::container::{Catalog, Style};
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
tree.diff_children(std::slice::from_mut(&mut self.content));
|
tree.children[0].diff(&mut self.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> iced_core::Size<Length> {
|
fn size(&self) -> iced_core::Size<Length> {
|
||||||
|
|
@ -115,7 +115,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -131,22 +131,21 @@ where
|
||||||
}
|
}
|
||||||
let node = self
|
let node = self
|
||||||
.content
|
.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, &my_limits);
|
.layout(&mut tree.children[0], renderer, &my_limits);
|
||||||
let size = node.size();
|
let size = node.size();
|
||||||
layout::Node::with_children(size, vec![node])
|
layout::Node::with_children(size, vec![node])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn iced_core::widget::Operation<()>,
|
||||||
) {
|
) {
|
||||||
operation.container(Some(&self.id), layout.bounds());
|
operation.container(Some(&self.id), layout.bounds(), &mut |operation| {
|
||||||
operation.traverse(&mut |operation| {
|
self.content.as_widget().operate(
|
||||||
self.content.as_widget_mut().operate(
|
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
layout
|
layout
|
||||||
.children()
|
.children()
|
||||||
|
|
@ -159,18 +158,18 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: mouse::Cursor,
|
cursor_position: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(feature = "wayland")]
|
||||||
if matches!(
|
if matches!(
|
||||||
event,
|
event,
|
||||||
Event::PlatformSpecific(event::PlatformSpecific::Wayland(
|
Event::PlatformSpecific(event::PlatformSpecific::Wayland(
|
||||||
|
|
@ -180,9 +179,9 @@ where
|
||||||
let bounds = layout.bounds().size();
|
let bounds = layout.bounds().size();
|
||||||
clipboard.request_logical_window_size(bounds.width.max(1.), bounds.height.max(1.));
|
clipboard.request_logical_window_size(bounds.width.max(1.), bounds.height.max(1.));
|
||||||
}
|
}
|
||||||
self.content.as_widget_mut().update(
|
self.content.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event,
|
event.clone(),
|
||||||
layout
|
layout
|
||||||
.children()
|
.children()
|
||||||
.next()
|
.next()
|
||||||
|
|
@ -193,7 +192,7 @@ where
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -239,9 +238,8 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
self.content.as_widget_mut().overlay(
|
self.content.as_widget_mut().overlay(
|
||||||
|
|
@ -252,7 +250,6 @@ where
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_virtual_offset(layout.virtual_offset()),
|
.with_virtual_offset(layout.virtual_offset()),
|
||||||
renderer,
|
renderer,
|
||||||
viewport,
|
|
||||||
translation,
|
translation,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
|
|
||||||
use super::{Builder, ButtonClass};
|
use super::{Builder, ButtonClass};
|
||||||
use crate::Element;
|
use crate::Element;
|
||||||
use crate::widget::{icon::Handle, tooltip};
|
use crate::widget::{
|
||||||
|
icon::{self, Handle},
|
||||||
|
tooltip,
|
||||||
|
};
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
use iced_core::{Alignment, Length, Padding, font::Weight, text::LineHeight, widget::Id};
|
use iced_core::{Alignment, Length, Padding, font::Weight, text::LineHeight, widget::Id};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
@ -130,7 +133,7 @@ impl<Message> Button<'_, Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message: Clone + 'static> From<Button<'a, Message>> for Element<'a, Message> {
|
impl<'a, Message: Clone + 'static> From<Button<'a, Message>> for Element<'a, Message> {
|
||||||
fn from(builder: Button<'a, Message>) -> Element<'a, Message> {
|
fn from(mut builder: Button<'a, Message>) -> Element<'a, Message> {
|
||||||
let mut content = Vec::with_capacity(2);
|
let mut content = Vec::with_capacity(2);
|
||||||
|
|
||||||
content.push(
|
content.push(
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -331,22 +331,21 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
self.padding,
|
self.padding,
|
||||||
|renderer, limits| {
|
|renderer, limits| {
|
||||||
self.content
|
self.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
operation: &mut dyn Operation<()>,
|
operation: &mut dyn Operation<()>,
|
||||||
) {
|
) {
|
||||||
operation.container(None, layout.bounds());
|
operation.container(None, layout.bounds(), &mut |operation| {
|
||||||
operation.traverse(&mut |operation| {
|
self.content.as_widget().operate(
|
||||||
self.content.as_widget_mut().operate(
|
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
layout
|
layout
|
||||||
.children()
|
.children()
|
||||||
|
|
@ -358,20 +357,20 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
operation.focusable(Some(&self.id), layout.bounds(), state);
|
operation.focusable(state, Some(&self.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
if let Variant::Image {
|
if let Variant::Image {
|
||||||
on_remove: Some(on_remove),
|
on_remove: Some(on_remove),
|
||||||
..
|
..
|
||||||
|
|
@ -384,8 +383,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
if let Some(position) = cursor.position() {
|
if let Some(position) = cursor.position() {
|
||||||
if removal_bounds(layout.bounds(), 4.0).contains(position) {
|
if removal_bounds(layout.bounds(), 4.0).contains(position) {
|
||||||
shell.publish(on_remove.clone());
|
shell.publish(on_remove.clone());
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -393,9 +391,10 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.content.as_widget_mut().update(
|
|
||||||
|
if self.content.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event,
|
event.clone(),
|
||||||
layout
|
layout
|
||||||
.children()
|
.children()
|
||||||
.next()
|
.next()
|
||||||
|
|
@ -406,9 +405,9 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
) == event::Status::Captured
|
||||||
if shell.is_event_captured() {
|
{
|
||||||
return;
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(
|
update(
|
||||||
|
|
@ -542,7 +541,6 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
selection_background,
|
selection_background,
|
||||||
);
|
);
|
||||||
|
|
@ -556,7 +554,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
y: bounds.y + (bounds.height - 18.0 - styling.border_width),
|
y: bounds.y + (bounds.height - 18.0 - styling.border_width),
|
||||||
};
|
};
|
||||||
if bounds.intersects(viewport) {
|
if bounds.intersects(viewport) {
|
||||||
iced_core::svg::Renderer::draw_svg(renderer, svg_handle, bounds, bounds);
|
iced_core::svg::Renderer::draw_svg(renderer, svg_handle, bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,7 +570,6 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
radius: c_rad.radius_m.into(),
|
radius: c_rad.radius_m.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
selection_background,
|
selection_background,
|
||||||
);
|
);
|
||||||
|
|
@ -586,12 +583,6 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
x: bounds.x + 4.0,
|
x: bounds.x + 4.0,
|
||||||
y: bounds.y + 4.0,
|
y: bounds.y + 4.0,
|
||||||
},
|
},
|
||||||
Rectangle {
|
|
||||||
width: 16.0,
|
|
||||||
height: 16.0,
|
|
||||||
x: bounds.x + 4.0,
|
|
||||||
y: bounds.y + 4.0,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -618,9 +609,8 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
mut translation: Vector,
|
mut translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
||||||
let position = layout.bounds().position();
|
let position = layout.bounds().position();
|
||||||
|
|
@ -634,7 +624,6 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_virtual_offset(layout.virtual_offset()),
|
.with_virtual_offset(layout.virtual_offset()),
|
||||||
renderer,
|
renderer,
|
||||||
viewport,
|
|
||||||
translation,
|
translation,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -649,7 +638,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
) -> iced_accessibility::A11yTree {
|
) -> iced_accessibility::A11yTree {
|
||||||
use iced_accessibility::{
|
use iced_accessibility::{
|
||||||
A11yNode, A11yTree,
|
A11yNode, A11yTree,
|
||||||
accesskit::{Action, Node, NodeId, Rect, Role},
|
accesskit::{Action, DefaultActionVerb, NodeBuilder, NodeId, Rect, Role},
|
||||||
};
|
};
|
||||||
// TODO why is state None sometimes?
|
// TODO why is state None sometimes?
|
||||||
if matches!(state.state, iced_core::widget::tree::State::None) {
|
if matches!(state.state, iced_core::widget::tree::State::None) {
|
||||||
|
|
@ -669,12 +658,12 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
let bounds = Rect::new(x as f64, y as f64, (x + width) as f64, (y + height) as f64);
|
let bounds = Rect::new(x as f64, y as f64, (x + width) as f64, (y + height) as f64);
|
||||||
let is_hovered = state.state.downcast_ref::<State>().is_hovered;
|
let is_hovered = state.state.downcast_ref::<State>().is_hovered;
|
||||||
|
|
||||||
let mut node = Node::new(Role::Button);
|
let mut node = NodeBuilder::new(Role::Button);
|
||||||
node.add_action(Action::Focus);
|
node.add_action(Action::Focus);
|
||||||
node.add_action(Action::Click);
|
node.add_action(Action::Default);
|
||||||
node.set_bounds(bounds);
|
node.set_bounds(bounds);
|
||||||
if let Some(name) = self.name.as_ref() {
|
if let Some(name) = self.name.as_ref() {
|
||||||
node.set_label(name.clone());
|
node.set_name(name.clone());
|
||||||
}
|
}
|
||||||
match self.description.as_ref() {
|
match self.description.as_ref() {
|
||||||
Some(iced_accessibility::Description::Id(id)) => {
|
Some(iced_accessibility::Description::Id(id)) => {
|
||||||
|
|
@ -693,10 +682,10 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
if self.on_press.is_none() {
|
if self.on_press.is_none() {
|
||||||
node.set_disabled();
|
node.set_disabled();
|
||||||
}
|
}
|
||||||
// TODO hover
|
if is_hovered {
|
||||||
// if is_hovered {
|
node.set_hovered();
|
||||||
// node.set_hovered();
|
}
|
||||||
// }
|
node.set_default_action_verb(DefaultActionVerb::Click);
|
||||||
|
|
||||||
if let Some(child_tree) = child_tree.map(|child_tree| {
|
if let Some(child_tree) = child_tree.map(|child_tree| {
|
||||||
self.content.as_widget().a11y_nodes(
|
self.content.as_widget().a11y_nodes(
|
||||||
|
|
@ -772,14 +761,14 @@ impl State {
|
||||||
#[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)]
|
#[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)]
|
||||||
pub fn update<'a, Message: Clone>(
|
pub fn update<'a, Message: Clone>(
|
||||||
_id: Id,
|
_id: Id,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
on_press: Option<&dyn Fn(Vector, Rectangle) -> Message>,
|
on_press: Option<&dyn Fn(Vector, Rectangle) -> Message>,
|
||||||
on_press_down: Option<&dyn Fn(Vector, Rectangle) -> Message>,
|
on_press_down: Option<&dyn Fn(Vector, Rectangle) -> Message>,
|
||||||
state: impl FnOnce() -> &'a mut State,
|
state: impl FnOnce() -> &'a mut State,
|
||||||
) {
|
) -> event::Status {
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
||||||
|
|
@ -798,8 +787,7 @@ pub fn update<'a, Message: Clone>(
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -818,8 +806,7 @@ pub fn update<'a, Message: Clone>(
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else if on_press_down.is_some() {
|
} else if on_press_down.is_some() {
|
||||||
let state = state();
|
let state = state();
|
||||||
|
|
@ -829,7 +816,7 @@ pub fn update<'a, Message: Clone>(
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
Event::A11y(event_id, iced_accessibility::accesskit::ActionRequest { action, .. }) => {
|
Event::A11y(event_id, iced_accessibility::accesskit::ActionRequest { action, .. }) => {
|
||||||
let state = state();
|
let state = state();
|
||||||
if let Some(on_press) = matches!(action, iced_accessibility::accesskit::Action::Click)
|
if let Some(on_press) = matches!(action, iced_accessibility::accesskit::Action::Default)
|
||||||
.then_some(on_press)
|
.then_some(on_press)
|
||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
|
|
@ -838,19 +825,17 @@ pub fn update<'a, Message: Clone>(
|
||||||
|
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
}
|
}
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
|
Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
|
||||||
if let Some(on_press) = on_press {
|
if let Some(on_press) = on_press {
|
||||||
let state = state();
|
let state = state();
|
||||||
if state.is_focused && *key == keyboard::Key::Named(keyboard::key::Named::Enter) {
|
if state.is_focused && key == keyboard::Key::Named(keyboard::key::Named::Enter) {
|
||||||
state.is_pressed = true;
|
state.is_pressed = true;
|
||||||
let msg = (on_press)(layout.virtual_offset(), layout.bounds());
|
let msg = (on_press)(layout.virtual_offset(), layout.bounds());
|
||||||
|
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -861,6 +846,8 @@ pub fn update<'a, Message: Clone>(
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event::Status::Ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|
@ -892,7 +879,6 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
|
||||||
radius: styling.border_radius,
|
radius: styling.border_radius,
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Color::TRANSPARENT,
|
Color::TRANSPARENT,
|
||||||
);
|
);
|
||||||
|
|
@ -914,7 +900,6 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Background::Color([0.0, 0.0, 0.0, 0.5].into()),
|
Background::Color([0.0, 0.0, 0.0, 0.5].into()),
|
||||||
);
|
);
|
||||||
|
|
@ -930,7 +915,6 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
background,
|
background,
|
||||||
);
|
);
|
||||||
|
|
@ -946,7 +930,6 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
overlay,
|
overlay,
|
||||||
);
|
);
|
||||||
|
|
@ -970,7 +953,6 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
|
||||||
radius: styling.border_radius,
|
radius: styling.border_radius,
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Color::TRANSPARENT,
|
Color::TRANSPARENT,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
//! A widget that displays an interactive calendar.
|
//! A widget that displays an interactive calendar.
|
||||||
|
|
||||||
use crate::fl;
|
use crate::fl;
|
||||||
|
use crate::iced_core::{Alignment, Length};
|
||||||
use crate::widget::{button, column, grid, icon, row, text};
|
use crate::widget::{button, column, grid, icon, row, text};
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
use iced::alignment::Vertical;
|
use iced::alignment::Vertical;
|
||||||
use iced_core::{Alignment, Length};
|
|
||||||
use jiff::{
|
use jiff::{
|
||||||
ToSpan,
|
ToSpan,
|
||||||
civil::{Date, Weekday},
|
civil::{Date, Weekday},
|
||||||
|
|
@ -212,10 +212,8 @@ where
|
||||||
|
|
||||||
let content_list = column::with_children([
|
let content_list = column::with_children([
|
||||||
row::with_children([
|
row::with_children([
|
||||||
column([date.into(), day.into()]).into(),
|
column().push(date).push(day).into(),
|
||||||
crate::widget::space::horizontal()
|
crate::widget::Space::with_width(Length::Fill).into(),
|
||||||
.width(Length::Fill)
|
|
||||||
.into(),
|
|
||||||
month_controls.into(),
|
month_controls.into(),
|
||||||
])
|
])
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
|
||||||
|
|
@ -1,586 +0,0 @@
|
||||||
//! An expandable stack of cards
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
anim,
|
|
||||||
widget::{
|
|
||||||
button,
|
|
||||||
card::style::Style,
|
|
||||||
column,
|
|
||||||
icon::{self, Handle},
|
|
||||||
row, text,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use float_cmp::approx_eq;
|
|
||||||
use iced::widget;
|
|
||||||
use iced_core::{
|
|
||||||
Border, Element, Event, Length, Shadow, Size, Vector, Widget, border::Radius, id::Id,
|
|
||||||
layout::Node, renderer::Quad, widget::Tree,
|
|
||||||
};
|
|
||||||
use iced_core::{widget::tree, window};
|
|
||||||
|
|
||||||
const ICON_SIZE: u16 = 16;
|
|
||||||
const TOP_SPACING: u16 = 4;
|
|
||||||
const VERTICAL_SPACING: f32 = 8.0;
|
|
||||||
const PADDING: u16 = 16;
|
|
||||||
const BG_CARD_VISIBLE_HEIGHT: f32 = 4.0;
|
|
||||||
const BG_CARD_BORDER_RADIUS: f32 = 8.0;
|
|
||||||
const BG_CARD_MARGIN_STEP: f32 = 8.0;
|
|
||||||
|
|
||||||
/// get an expandable stack of cards
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn cards<'a, Message, F, G>(
|
|
||||||
id: widget::Id,
|
|
||||||
card_inner_elements: Vec<Element<'a, Message, crate::Theme, crate::Renderer>>,
|
|
||||||
on_clear_all: Message,
|
|
||||||
on_show_more: Option<F>,
|
|
||||||
on_activate: Option<G>,
|
|
||||||
show_more_label: &'a str,
|
|
||||||
show_less_label: &'a str,
|
|
||||||
clear_all_label: &'a str,
|
|
||||||
show_less_icon: Option<Handle>,
|
|
||||||
expanded: bool,
|
|
||||||
) -> Cards<'a, Message, crate::Renderer>
|
|
||||||
where
|
|
||||||
Message: 'static + Clone,
|
|
||||||
F: 'a + Fn(bool) -> Message,
|
|
||||||
G: 'a + Fn(usize) -> Message,
|
|
||||||
{
|
|
||||||
Cards::new(
|
|
||||||
id,
|
|
||||||
card_inner_elements,
|
|
||||||
on_clear_all,
|
|
||||||
on_show_more,
|
|
||||||
on_activate,
|
|
||||||
show_more_label,
|
|
||||||
show_less_label,
|
|
||||||
clear_all_label,
|
|
||||||
show_less_icon,
|
|
||||||
expanded,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Renderer> Cards<'a, Message, Renderer>
|
|
||||||
where
|
|
||||||
Renderer: iced_core::text::Renderer,
|
|
||||||
{
|
|
||||||
fn fully_expanded(&self, t: f32) -> bool {
|
|
||||||
self.expanded && self.elements.len() > 1 && self.can_show_more && approx_eq!(f32, t, 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fully_unexpanded(&self, t: f32) -> bool {
|
|
||||||
self.elements.len() == 1
|
|
||||||
|| (!self.expanded && (!self.can_show_more || approx_eq!(f32, t, 0.0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An expandable stack of cards.
|
|
||||||
#[allow(missing_debug_implementations)]
|
|
||||||
pub struct Cards<'a, Message, Renderer = crate::Renderer>
|
|
||||||
where
|
|
||||||
Renderer: iced_core::text::Renderer,
|
|
||||||
{
|
|
||||||
id: Id,
|
|
||||||
show_less_button: Element<'a, Message, crate::Theme, Renderer>,
|
|
||||||
clear_all_button: Element<'a, Message, crate::Theme, Renderer>,
|
|
||||||
elements: Vec<Element<'a, Message, crate::Theme, Renderer>>,
|
|
||||||
expanded: bool,
|
|
||||||
can_show_more: bool,
|
|
||||||
width: Length,
|
|
||||||
anim_multiplier: f32,
|
|
||||||
duration: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message> Cards<'a, Message, crate::Renderer>
|
|
||||||
where
|
|
||||||
Message: Clone + 'static,
|
|
||||||
{
|
|
||||||
/// Get an expandable stack of cards
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new<F, G>(
|
|
||||||
id: widget::Id,
|
|
||||||
card_inner_elements: Vec<Element<'a, Message, crate::Theme, crate::Renderer>>,
|
|
||||||
on_clear_all: Message,
|
|
||||||
on_show_more: Option<F>,
|
|
||||||
on_activate: Option<G>,
|
|
||||||
show_more_label: &'a str,
|
|
||||||
show_less_label: &'a str,
|
|
||||||
clear_all_label: &'a str,
|
|
||||||
show_less_icon: Option<Handle>,
|
|
||||||
expanded: bool,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
F: 'a + Fn(bool) -> Message,
|
|
||||||
G: 'a + Fn(usize) -> Message,
|
|
||||||
{
|
|
||||||
let can_show_more = card_inner_elements.len() > 1 && on_show_more.is_some();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
can_show_more,
|
|
||||||
id: Id::unique(),
|
|
||||||
show_less_button: {
|
|
||||||
let mut show_less_children = Vec::with_capacity(3);
|
|
||||||
if let Some(source) = show_less_icon {
|
|
||||||
show_less_children.push(icon::icon(source).size(ICON_SIZE).into());
|
|
||||||
}
|
|
||||||
show_less_children.push(text::body(show_less_label).width(Length::Shrink).into());
|
|
||||||
show_less_children.push(
|
|
||||||
icon::from_name("pan-up-symbolic")
|
|
||||||
.size(ICON_SIZE)
|
|
||||||
.icon()
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let button_content = row::with_children(show_less_children)
|
|
||||||
.align_y(iced_core::Alignment::Center)
|
|
||||||
.spacing(TOP_SPACING)
|
|
||||||
.width(Length::Shrink);
|
|
||||||
|
|
||||||
Element::from(
|
|
||||||
button::custom(button_content)
|
|
||||||
.class(crate::theme::Button::Text)
|
|
||||||
.width(Length::Shrink)
|
|
||||||
.on_press_maybe(on_show_more.as_ref().map(|f| f(false)))
|
|
||||||
.padding([PADDING / 2, PADDING]),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
clear_all_button: Element::from(
|
|
||||||
button::custom(text(clear_all_label))
|
|
||||||
.class(crate::theme::Button::Text)
|
|
||||||
.width(Length::Shrink)
|
|
||||||
.on_press(on_clear_all)
|
|
||||||
.padding([PADDING / 2, PADDING]),
|
|
||||||
),
|
|
||||||
elements: card_inner_elements
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, w)| {
|
|
||||||
let custom_content = if i == 0 && !expanded && can_show_more {
|
|
||||||
column::with_capacity(2)
|
|
||||||
.push(w)
|
|
||||||
.push(text::caption(show_more_label))
|
|
||||||
.spacing(VERTICAL_SPACING)
|
|
||||||
.align_x(iced_core::Alignment::Center)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
w
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = crate::iced::widget::button(custom_content)
|
|
||||||
.class(crate::theme::iced::Button::Card)
|
|
||||||
.padding(PADDING);
|
|
||||||
if i == 0 && !expanded && can_show_more {
|
|
||||||
b.on_press_maybe(on_show_more.as_ref().map(|f| f(true)))
|
|
||||||
} else {
|
|
||||||
b.on_press_maybe(on_activate.as_ref().map(|f| f(i)))
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
// we will set the width of the container to shrink, then when laying out the top bar
|
|
||||||
// we will set the fill limit to the max of the shrink top bar width and the max shrink width of the
|
|
||||||
// cards
|
|
||||||
.collect(),
|
|
||||||
width: Length::Shrink,
|
|
||||||
anim_multiplier: 1.0,
|
|
||||||
expanded,
|
|
||||||
duration: Duration::from_millis(200),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the width of the cards stack
|
|
||||||
#[must_use]
|
|
||||||
pub fn width(mut self, width: Length) -> Self {
|
|
||||||
self.width = width;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// The default animation time is 100ms, to speed up the toggle
|
|
||||||
/// animation use a value less than 1.0, and to slow down the
|
|
||||||
/// animation use a value greater than 1.0.
|
|
||||||
pub fn anim_multiplier(mut self, multiplier: f32) -> Self {
|
|
||||||
self.anim_multiplier = multiplier;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn duration(mut self, dur: Duration) -> Self {
|
|
||||||
self.duration = dur;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(mut self, id: Id) -> Self {
|
|
||||||
self.id = id;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Renderer> Widget<Message, crate::Theme, Renderer> for Cards<'a, Message, Renderer>
|
|
||||||
where
|
|
||||||
Message: 'a + Clone,
|
|
||||||
Renderer: 'a + iced_core::Renderer + iced_core::text::Renderer,
|
|
||||||
{
|
|
||||||
fn children(&self) -> Vec<Tree> {
|
|
||||||
[&self.show_less_button, &self.clear_all_button]
|
|
||||||
.iter()
|
|
||||||
.map(|w| Tree::new(w.as_widget()))
|
|
||||||
.chain(self.elements.iter().map(|w| Tree::new(w.as_widget())))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
|
||||||
let mut children: Vec<_> = vec![
|
|
||||||
self.show_less_button.as_widget_mut(),
|
|
||||||
self.clear_all_button.as_widget_mut(),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.chain(
|
|
||||||
self.elements
|
|
||||||
.iter_mut()
|
|
||||||
.map(iced_core::Element::as_widget_mut),
|
|
||||||
)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
tree.diff_children(children.as_mut_slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
tree: &mut Tree,
|
|
||||||
renderer: &Renderer,
|
|
||||||
limits: &iced_core::layout::Limits,
|
|
||||||
) -> iced_core::layout::Node {
|
|
||||||
let my_state = tree.state.downcast_ref::<State>();
|
|
||||||
|
|
||||||
let mut children = Vec::with_capacity(1 + self.elements.len());
|
|
||||||
let mut size = Size::new(0.0, 0.0);
|
|
||||||
let tree_children = &mut tree.children;
|
|
||||||
let count = self.elements.len();
|
|
||||||
if self.elements.is_empty() {
|
|
||||||
return Node::with_children(Size::new(1., 1.), children);
|
|
||||||
}
|
|
||||||
let s = anim::smootherstep(my_state.anim.t(self.duration, self.expanded));
|
|
||||||
let fully_expanded: bool = self.fully_expanded(s);
|
|
||||||
let fully_unexpanded: bool = self.fully_unexpanded(s);
|
|
||||||
|
|
||||||
let show_less = &mut self.show_less_button;
|
|
||||||
let clear_all = &mut self.clear_all_button;
|
|
||||||
|
|
||||||
let show_less_node = if self.can_show_more {
|
|
||||||
show_less
|
|
||||||
.as_widget_mut()
|
|
||||||
.layout(&mut tree_children[0], renderer, limits)
|
|
||||||
} else {
|
|
||||||
Node::new(Size::default())
|
|
||||||
};
|
|
||||||
let clear_all_node =
|
|
||||||
clear_all
|
|
||||||
.as_widget_mut()
|
|
||||||
.layout(&mut tree_children[1], renderer, limits);
|
|
||||||
size.width += show_less_node.size().width + clear_all_node.size().width;
|
|
||||||
|
|
||||||
let custom_limits = limits.min_width(size.width);
|
|
||||||
for (c, t) in self.elements.iter_mut().zip(tree_children[2..].iter_mut()) {
|
|
||||||
let card_node = c.as_widget_mut().layout(t, renderer, &custom_limits);
|
|
||||||
size.width = size.width.max(card_node.size().width);
|
|
||||||
}
|
|
||||||
|
|
||||||
if fully_expanded {
|
|
||||||
let show_less = &mut self.show_less_button;
|
|
||||||
let clear_all = &mut self.clear_all_button;
|
|
||||||
|
|
||||||
let show_less_node = if self.can_show_more {
|
|
||||||
show_less
|
|
||||||
.as_widget_mut()
|
|
||||||
.layout(&mut tree_children[0], renderer, limits)
|
|
||||||
} else {
|
|
||||||
Node::new(Size::default())
|
|
||||||
};
|
|
||||||
let clear_all_node = if self.can_show_more {
|
|
||||||
let mut n =
|
|
||||||
clear_all
|
|
||||||
.as_widget_mut()
|
|
||||||
.layout(&mut tree_children[1], renderer, limits);
|
|
||||||
let clear_all_node_size = n.size();
|
|
||||||
n = clear_all_node
|
|
||||||
.translate(Vector::new(size.width - clear_all_node_size.width, 0.0));
|
|
||||||
size.height += show_less_node.size().height.max(n.size().height) + VERTICAL_SPACING;
|
|
||||||
n
|
|
||||||
} else {
|
|
||||||
Node::new(Size::default())
|
|
||||||
};
|
|
||||||
|
|
||||||
children.push(show_less_node);
|
|
||||||
children.push(clear_all_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
let custom_limits = limits
|
|
||||||
.min_width(size.width)
|
|
||||||
.max_width(size.width)
|
|
||||||
.width(Length::Fixed(size.width));
|
|
||||||
|
|
||||||
for (i, (c, t)) in self
|
|
||||||
.elements
|
|
||||||
.iter_mut()
|
|
||||||
.zip(tree_children[2..].iter_mut())
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
let progress = s * size.height;
|
|
||||||
let card_node = c
|
|
||||||
.as_widget_mut()
|
|
||||||
.layout(t, renderer, &custom_limits)
|
|
||||||
.translate(Vector::new(0.0, progress));
|
|
||||||
|
|
||||||
size.height = size.height.max(progress + card_node.size().height);
|
|
||||||
|
|
||||||
children.push(card_node);
|
|
||||||
|
|
||||||
if fully_unexpanded {
|
|
||||||
let width = children.last().unwrap().bounds().width;
|
|
||||||
|
|
||||||
// push the background card nodes
|
|
||||||
for i in 1..self.elements.len().min(3) {
|
|
||||||
// height must be 16px for 8px padding
|
|
||||||
// but we only want 4px visible
|
|
||||||
|
|
||||||
let margin = f32::from(u8::try_from(i).unwrap()) * BG_CARD_MARGIN_STEP;
|
|
||||||
let node =
|
|
||||||
Node::new(Size::new(width - 2.0 * margin, BG_CARD_BORDER_RADIUS * 2.0))
|
|
||||||
.translate(Vector::new(
|
|
||||||
margin,
|
|
||||||
size.height - BG_CARD_BORDER_RADIUS * 2.0 + BG_CARD_VISIBLE_HEIGHT,
|
|
||||||
));
|
|
||||||
size.height += BG_CARD_VISIBLE_HEIGHT;
|
|
||||||
children.push(node);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if i + 1 < count {
|
|
||||||
size.height += VERTICAL_SPACING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::with_children(size, children)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
state: &iced_core::widget::Tree,
|
|
||||||
renderer: &mut Renderer,
|
|
||||||
theme: &crate::Theme,
|
|
||||||
style: &iced_core::renderer::Style,
|
|
||||||
layout: iced_core::Layout<'_>,
|
|
||||||
cursor: iced_core::mouse::Cursor,
|
|
||||||
viewport: &iced_core::Rectangle,
|
|
||||||
) {
|
|
||||||
let my_state = state.state.downcast_ref::<State>();
|
|
||||||
|
|
||||||
// there are 4 cases for drawing
|
|
||||||
// 1. empty entries list
|
|
||||||
// Nothing to draw
|
|
||||||
// 2. un-expanded
|
|
||||||
// go through the layout, draw the card, the inner card, and the bg cards
|
|
||||||
// 3. expanding / unexpanding
|
|
||||||
// go through the layout. draw each card and its inner card
|
|
||||||
// 4. expanded =>
|
|
||||||
// go through the layout. draw the top bar, and do all of 3
|
|
||||||
// cards may be hovered
|
|
||||||
// any buttons may have a hover state as well
|
|
||||||
if self.elements.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = my_state.anim.t(self.duration, self.expanded);
|
|
||||||
let fully_unexpanded = self.fully_unexpanded(t);
|
|
||||||
let fully_expanded = self.fully_expanded(t);
|
|
||||||
|
|
||||||
let mut layout = layout.children();
|
|
||||||
let mut tree_children = state.children.iter();
|
|
||||||
|
|
||||||
if fully_expanded {
|
|
||||||
let show_less = &self.show_less_button;
|
|
||||||
let clear_all = &self.clear_all_button;
|
|
||||||
|
|
||||||
let show_less_layout = layout.next().unwrap();
|
|
||||||
let clear_all_layout = layout.next().unwrap();
|
|
||||||
|
|
||||||
show_less.as_widget().draw(
|
|
||||||
tree_children.next().unwrap(),
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
style,
|
|
||||||
show_less_layout,
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
);
|
|
||||||
|
|
||||||
clear_all.as_widget().draw(
|
|
||||||
tree_children.next().unwrap(),
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
style,
|
|
||||||
clear_all_layout,
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_ = tree_children.next();
|
|
||||||
_ = tree_children.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw first to appear behind
|
|
||||||
if fully_unexpanded {
|
|
||||||
let card_layout = layout.next().unwrap();
|
|
||||||
let appearance = Style::default();
|
|
||||||
let bg_layout = layout.collect::<Vec<_>>();
|
|
||||||
for (i, layout) in (0..2).zip(bg_layout.into_iter()).rev() {
|
|
||||||
renderer.fill_quad(
|
|
||||||
Quad {
|
|
||||||
bounds: layout.bounds(),
|
|
||||||
border: Border {
|
|
||||||
radius: Radius::from([
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
BG_CARD_BORDER_RADIUS,
|
|
||||||
BG_CARD_BORDER_RADIUS,
|
|
||||||
]),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
shadow: Shadow::default(),
|
|
||||||
snap: true,
|
|
||||||
},
|
|
||||||
if i == 0 {
|
|
||||||
appearance.card_1
|
|
||||||
} else {
|
|
||||||
appearance.card_2
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.elements[0].as_widget().draw(
|
|
||||||
tree_children.next().unwrap(),
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
style,
|
|
||||||
card_layout,
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let layout = layout.collect::<Vec<_>>();
|
|
||||||
// draw in reverse order so later cards appear behind earlier cards
|
|
||||||
for ((inner, layout), c_state) in self
|
|
||||||
.elements
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.zip(layout.into_iter().rev())
|
|
||||||
.zip(tree_children.rev())
|
|
||||||
{
|
|
||||||
inner
|
|
||||||
.as_widget()
|
|
||||||
.draw(c_state, renderer, theme, style, layout, cursor, viewport);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
|
||||||
&mut self,
|
|
||||||
state: &mut Tree,
|
|
||||||
event: &iced_core::Event,
|
|
||||||
layout: iced_core::Layout<'_>,
|
|
||||||
cursor: iced_core::mouse::Cursor,
|
|
||||||
renderer: &Renderer,
|
|
||||||
clipboard: &mut dyn iced_core::Clipboard,
|
|
||||||
shell: &mut iced_core::Shell<'_, Message>,
|
|
||||||
viewport: &iced_core::Rectangle,
|
|
||||||
) {
|
|
||||||
if self.elements.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Event::Window(window::Event::RedrawRequested(_)) = event {
|
|
||||||
let state = state.state.downcast_mut::<State>();
|
|
||||||
|
|
||||||
state.anim.anim_done(self.duration);
|
|
||||||
if state.anim.last_change.is_some() {
|
|
||||||
shell.request_redraw();
|
|
||||||
shell.invalidate_layout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let my_state = state.state.downcast_ref::<State>();
|
|
||||||
|
|
||||||
let mut layout = layout.children();
|
|
||||||
let mut tree_children = state.children.iter_mut();
|
|
||||||
let t = my_state.anim.t(self.duration, self.expanded);
|
|
||||||
let fully_expanded = self.fully_expanded(t);
|
|
||||||
let fully_unexpanded = self.fully_unexpanded(t);
|
|
||||||
let show_less_state = tree_children.next();
|
|
||||||
let clear_all_state = tree_children.next();
|
|
||||||
|
|
||||||
if fully_expanded {
|
|
||||||
let c_layout = layout.next().unwrap();
|
|
||||||
let state = show_less_state.unwrap();
|
|
||||||
self.show_less_button.as_widget_mut().update(
|
|
||||||
state, event, c_layout, cursor, renderer, clipboard, shell, viewport,
|
|
||||||
);
|
|
||||||
|
|
||||||
if shell.is_event_captured() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let c_layout = layout.next().unwrap();
|
|
||||||
let state = clear_all_state.unwrap();
|
|
||||||
self.clear_all_button.as_widget_mut().update(
|
|
||||||
state, &event, c_layout, cursor, renderer, clipboard, shell, viewport,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if shell.is_event_captured() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((inner, layout), c_state) in self.elements.iter_mut().zip(layout).zip(tree_children) {
|
|
||||||
inner.as_widget_mut().update(
|
|
||||||
c_state, &event, layout, cursor, renderer, clipboard, shell, viewport,
|
|
||||||
);
|
|
||||||
if shell.is_event_captured() || fully_unexpanded {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> Size<Length> {
|
|
||||||
Size::new(self.width, Length::Shrink)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tag(&self) -> tree::Tag {
|
|
||||||
tree::Tag::of::<State>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
|
||||||
tree::State::new(State::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn id(&self) -> Option<Id> {
|
|
||||||
Some(self.id.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_id(&mut self, id: Id) {
|
|
||||||
self.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message> From<Cards<'a, Message>> for Element<'a, Message, crate::Theme, crate::Renderer>
|
|
||||||
where
|
|
||||||
Message: Clone + 'a,
|
|
||||||
{
|
|
||||||
fn from(cards: Cards<'a, Message>) -> Self {
|
|
||||||
Self::new(cards)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct State {
|
|
||||||
anim: anim::State,
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
//! Widgets for selecting colors with a color picker.
|
//! Widgets for selecting colors with a color picker.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::iter;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
@ -25,10 +26,7 @@ use iced_core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use iced_widget::slider::HandleShape;
|
use iced_widget::slider::HandleShape;
|
||||||
use iced_widget::{
|
use iced_widget::{Row, canvas, column, horizontal_space, row, scrollable, vertical_space};
|
||||||
Row, canvas, column, row, scrollable,
|
|
||||||
space::{horizontal, vertical},
|
|
||||||
};
|
|
||||||
use palette::{FromColor, RgbHue};
|
use palette::{FromColor, RgbHue};
|
||||||
|
|
||||||
use super::divider::horizontal;
|
use super::divider::horizontal;
|
||||||
|
|
@ -92,6 +90,8 @@ pub struct ColorPickerModel {
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
active_color: palette::Hsv,
|
active_color: palette::Hsv,
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
|
save_next: Option<Color>,
|
||||||
|
#[setters(skip)]
|
||||||
input_color: String,
|
input_color: String,
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
applied_color: Option<Color>,
|
applied_color: Option<Color>,
|
||||||
|
|
@ -125,6 +125,7 @@ impl ColorPickerModel {
|
||||||
.insert(move |b| b.text(rgb.clone()))
|
.insert(move |b| b.text(rgb.clone()))
|
||||||
.build(),
|
.build(),
|
||||||
active_color: hsv,
|
active_color: hsv,
|
||||||
|
save_next: None,
|
||||||
input_color: color_to_string(hsv, true),
|
input_color: color_to_string(hsv, true),
|
||||||
applied_color: initial,
|
applied_color: initial,
|
||||||
fallback_color,
|
fallback_color,
|
||||||
|
|
@ -155,26 +156,22 @@ impl ColorPickerModel {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_recent_colors(&mut self, new_color: Color) {
|
|
||||||
if let Some(pos) = self.recent_colors.iter().position(|c| *c == new_color) {
|
|
||||||
self.recent_colors.remove(pos);
|
|
||||||
}
|
|
||||||
self.recent_colors.insert(0, new_color);
|
|
||||||
self.recent_colors.truncate(MAX_RECENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update<Message>(&mut self, update: ColorPickerUpdate) -> Task<Message> {
|
pub fn update<Message>(&mut self, update: ColorPickerUpdate) -> Task<Message> {
|
||||||
match update {
|
match update {
|
||||||
ColorPickerUpdate::ActiveColor(c) => {
|
ColorPickerUpdate::ActiveColor(c) => {
|
||||||
self.must_clear_cache.store(true, Ordering::SeqCst);
|
self.must_clear_cache.store(true, Ordering::SeqCst);
|
||||||
self.input_color = color_to_string(c, self.is_hex());
|
self.input_color = color_to_string(c, self.is_hex());
|
||||||
|
if let Some(to_save) = self.save_next.take() {
|
||||||
|
self.recent_colors.insert(0, to_save);
|
||||||
|
self.recent_colors.truncate(MAX_RECENT);
|
||||||
|
}
|
||||||
self.active_color = c;
|
self.active_color = c;
|
||||||
self.copied_at = None;
|
self.copied_at = None;
|
||||||
}
|
}
|
||||||
ColorPickerUpdate::AppliedColor | ColorPickerUpdate::ActionFinished => {
|
ColorPickerUpdate::AppliedColor => {
|
||||||
let srgb = palette::Srgb::from_color(self.active_color);
|
let srgb = palette::Srgb::from_color(self.active_color);
|
||||||
if let Some(applied_color) = self.applied_color.take() {
|
if let Some(applied_color) = self.applied_color.take() {
|
||||||
self.update_recent_colors(applied_color);
|
self.recent_colors.push(applied_color);
|
||||||
}
|
}
|
||||||
self.applied_color = Some(Color::from(srgb));
|
self.applied_color = Some(Color::from(srgb));
|
||||||
self.active = false;
|
self.active = false;
|
||||||
|
|
@ -215,12 +212,21 @@ impl ColorPickerModel {
|
||||||
palette::Hsv::from_color(palette::Srgb::new(c.red, c.green, c.blue));
|
palette::Hsv::from_color(palette::Srgb::new(c.red, c.green, c.blue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ColorPickerUpdate::ActionFinished => {
|
||||||
|
let srgb = palette::Srgb::from_color(self.active_color);
|
||||||
|
if let Some(applied_color) = self.applied_color.take() {
|
||||||
|
self.recent_colors.push(applied_color);
|
||||||
|
}
|
||||||
|
self.applied_color = Some(Color::from(srgb));
|
||||||
|
self.active = false;
|
||||||
|
self.save_next = Some(Color::from(srgb));
|
||||||
|
}
|
||||||
ColorPickerUpdate::ToggleColorPicker => {
|
ColorPickerUpdate::ToggleColorPicker => {
|
||||||
self.must_clear_cache.store(true, Ordering::SeqCst);
|
self.must_clear_cache.store(true, Ordering::SeqCst);
|
||||||
self.active = !self.active;
|
self.active = !self.active;
|
||||||
self.copied_at = None;
|
self.copied_at = None;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,7 +334,7 @@ where
|
||||||
.width(self.width),
|
.width(self.width),
|
||||||
// canvas with gradient for the current color
|
// canvas with gradient for the current color
|
||||||
// still needs the canvas and the handle to be drawn on it
|
// still needs the canvas and the handle to be drawn on it
|
||||||
container(vertical().height(self.height))
|
container(vertical_space().height(self.height))
|
||||||
.width(self.width)
|
.width(self.width)
|
||||||
.height(self.height),
|
.height(self.height),
|
||||||
slider(
|
slider(
|
||||||
|
|
@ -386,8 +392,7 @@ where
|
||||||
text_input("", self.input_color)
|
text_input("", self.input_color)
|
||||||
.on_input(move |s| on_update(ColorPickerUpdate::Input(s)))
|
.on_input(move |s| on_update(ColorPickerUpdate::Input(s)))
|
||||||
.on_paste(move |s| on_update(ColorPickerUpdate::Input(s)))
|
.on_paste(move |s| on_update(ColorPickerUpdate::Input(s)))
|
||||||
.on_submit(move |_| on_update(ColorPickerUpdate::ActionFinished))
|
.on_submit(move |_| on_update(ColorPickerUpdate::AppliedColor))
|
||||||
// .on_unfocus(on_update(ColorPickerUpdate::ActionFinished)) Somehow this is called even when the field wasn't previously focused
|
|
||||||
.leading_icon(
|
.leading_icon(
|
||||||
color_button(
|
color_button(
|
||||||
None,
|
None,
|
||||||
|
|
@ -543,13 +548,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
self.inner
|
self.inner
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -652,7 +657,6 @@ where
|
||||||
radius: (1.0 + handle_radius).into(),
|
radius: (1.0 + handle_radius).into(),
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Color::TRANSPARENT,
|
Color::TRANSPARENT,
|
||||||
);
|
);
|
||||||
|
|
@ -670,7 +674,6 @@ where
|
||||||
radius: handle_radius.into(),
|
radius: handle_radius.into(),
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: true,
|
|
||||||
},
|
},
|
||||||
Color::TRANSPARENT,
|
Color::TRANSPARENT,
|
||||||
);
|
);
|
||||||
|
|
@ -681,31 +684,26 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
state: &'b mut Tree,
|
state: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
||||||
self.inner.as_widget_mut().overlay(
|
self.inner
|
||||||
&mut state.children[0],
|
.as_widget_mut()
|
||||||
layout,
|
.overlay(&mut state.children[0], layout, renderer, translation)
|
||||||
renderer,
|
|
||||||
viewport,
|
|
||||||
translation,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
// if the pointer is performing a drag, intercept pointer motion and button events
|
// if the pointer is performing a drag, intercept pointer motion and button events
|
||||||
// else check if event is handled by child elements
|
// else check if event is handled by child elements
|
||||||
// if the event is not handled by a child element, check if it is over the canvas when pressing a button
|
// if the event is not handled by a child element, check if it is over the canvas when pressing a button
|
||||||
|
|
@ -734,26 +732,24 @@ where
|
||||||
shell.publish((self.on_update)(ColorPickerUpdate::ActionFinished));
|
shell.publish((self.on_update)(ColorPickerUpdate::ActionFinished));
|
||||||
state.dragging = false;
|
state.dragging = false;
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return event::Status::Ignored,
|
||||||
};
|
};
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let column_tree = &mut tree.children[0];
|
let column_tree = &mut tree.children[0];
|
||||||
self.inner.as_widget_mut().update(
|
if self.inner.as_widget_mut().on_event(
|
||||||
column_tree,
|
column_tree,
|
||||||
&event,
|
event.clone(),
|
||||||
column_layout,
|
column_layout,
|
||||||
cursor,
|
cursor,
|
||||||
renderer,
|
renderer,
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
) == event::Status::Captured
|
||||||
if shell.is_event_captured() {
|
{
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -768,10 +764,12 @@ where
|
||||||
state.dragging = true;
|
state.dragging = true;
|
||||||
let hsv: palette::Hsv = palette::Hsv::new(self.active_color.hue, s, v);
|
let hsv: palette::Hsv = palette::Hsv::new(self.active_color.hue, s, v);
|
||||||
shell.publish((self.on_update)(ColorPickerUpdate::ActiveColor(hsv)));
|
shell.publish((self.on_update)(ColorPickerUpdate::ActiveColor(hsv)));
|
||||||
shell.capture_event();
|
event::Status::Captured
|
||||||
|
} else {
|
||||||
|
event::Status::Ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => event::Status::Ignored,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -814,12 +812,12 @@ pub fn color_button<'a, Message: Clone + 'static>(
|
||||||
let spacing = THEME.lock().unwrap().cosmic().spacing;
|
let spacing = THEME.lock().unwrap().cosmic().spacing;
|
||||||
|
|
||||||
button::custom(if color.is_some() {
|
button::custom(if color.is_some() {
|
||||||
Element::from(vertical().height(Length::Fixed(f32::from(spacing.space_s))))
|
Element::from(vertical_space().height(Length::Fixed(f32::from(spacing.space_s))))
|
||||||
} else {
|
} else {
|
||||||
Element::from(column![
|
Element::from(column![
|
||||||
vertical().height(Length::FillPortion(6)),
|
vertical_space().height(Length::FillPortion(6)),
|
||||||
row![
|
row![
|
||||||
horizontal().width(Length::FillPortion(6)),
|
horizontal_space().width(Length::FillPortion(6)),
|
||||||
Icon::from(
|
Icon::from(
|
||||||
icon::from_name("list-add-symbolic")
|
icon::from_name("list-add-symbolic")
|
||||||
.prefer_svg(true)
|
.prefer_svg(true)
|
||||||
|
|
@ -829,11 +827,11 @@ pub fn color_button<'a, Message: Clone + 'static>(
|
||||||
.width(icon_portion)
|
.width(icon_portion)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.content_fit(iced_core::ContentFit::Contain),
|
.content_fit(iced_core::ContentFit::Contain),
|
||||||
horizontal().width(Length::FillPortion(6)),
|
horizontal_space().width(Length::FillPortion(6)),
|
||||||
]
|
]
|
||||||
.height(icon_portion)
|
.height(icon_portion)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
vertical().height(Length::FillPortion(6)),
|
vertical_space().height(Length::FillPortion(6)),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
.width(Length::Fixed(f32::from(spacing.space_s)))
|
.width(Length::Fixed(f32::from(spacing.space_s)))
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use iced::advanced::layout::{self, Layout};
|
||||||
use iced::advanced::widget::{self, Operation};
|
use iced::advanced::widget::{self, Operation};
|
||||||
use iced::advanced::{Clipboard, Shell};
|
use iced::advanced::{Clipboard, Shell};
|
||||||
use iced::advanced::{overlay, renderer};
|
use iced::advanced::{overlay, renderer};
|
||||||
use iced::{Event, Point, Size, mouse};
|
use iced::{Event, Point, Rectangle, Size, event, mouse};
|
||||||
use iced_core::{Renderer, touch};
|
use iced_core::Renderer;
|
||||||
|
|
||||||
pub(super) struct Overlay<'a, 'b, Message> {
|
pub(super) struct Overlay<'a, 'b, Message> {
|
||||||
pub(crate) position: Point,
|
pub(crate) position: Point,
|
||||||
|
|
@ -29,7 +29,7 @@ where
|
||||||
|
|
||||||
let node = self
|
let node = self
|
||||||
.content
|
.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(self.tree, renderer, &limits);
|
.layout(self.tree, renderer, &limits);
|
||||||
let node_size = node.size();
|
let node_size = node.size();
|
||||||
|
|
||||||
|
|
@ -47,16 +47,16 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
) {
|
) -> event::Status {
|
||||||
self.content.as_widget_mut().update(
|
self.content.as_widget_mut().on_event(
|
||||||
self.tree,
|
self.tree,
|
||||||
event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
|
|
@ -65,20 +65,7 @@ where
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
&layout.bounds(),
|
&layout.bounds(),
|
||||||
);
|
)
|
||||||
match event {
|
|
||||||
Event::Mouse(e) if !matches!(e, mouse::Event::CursorLeft) => {
|
|
||||||
if cursor.is_over(layout.bounds()) {
|
|
||||||
shell.capture_event();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Touch(e) if !matches!(e, touch::Event::FingerLost { .. }) => {
|
|
||||||
if cursor.is_over(layout.bounds()) {
|
|
||||||
shell.capture_event();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
@ -99,7 +86,7 @@ where
|
||||||
cursor,
|
cursor,
|
||||||
&layout.bounds(),
|
&layout.bounds(),
|
||||||
);
|
);
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
|
|
@ -117,35 +104,21 @@ where
|
||||||
&self,
|
&self,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
// TODO how to handle viewport here?
|
self.content
|
||||||
let viewport = &layout.bounds();
|
|
||||||
let interaction = self
|
|
||||||
.content
|
|
||||||
.as_widget()
|
.as_widget()
|
||||||
.mouse_interaction(self.tree, layout, cursor, viewport, renderer);
|
.mouse_interaction(self.tree, layout, cursor, viewport, renderer)
|
||||||
if let mouse::Interaction::None = interaction
|
|
||||||
&& cursor.is_over(layout.bounds())
|
|
||||||
{
|
|
||||||
return mouse::Interaction::Idle;
|
|
||||||
}
|
|
||||||
interaction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overlay<'c>(
|
fn overlay<'c>(
|
||||||
&'c mut self,
|
&'c mut self,
|
||||||
layout: Layout<'c>,
|
layout: Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
) -> Option<overlay::Element<'c, Message, crate::Theme, crate::Renderer>> {
|
) -> Option<overlay::Element<'c, Message, crate::Theme, crate::Renderer>> {
|
||||||
let viewport = &layout.bounds();
|
self.content
|
||||||
|
.as_widget_mut()
|
||||||
self.content.as_widget_mut().overlay(
|
.overlay(self.tree, layout, renderer, iced::Vector::default())
|
||||||
self.tree,
|
|
||||||
layout,
|
|
||||||
renderer,
|
|
||||||
viewport,
|
|
||||||
iced::Vector::default(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::{Apply, Element, Renderer, Theme, fl};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use iced_core::Alignment;
|
use iced_core::Alignment;
|
||||||
use iced_core::event::Event;
|
use iced_core::event::{self, Event};
|
||||||
use iced_core::widget::{Operation, Tree};
|
use iced_core::widget::{Operation, Tree};
|
||||||
use iced_core::{
|
use iced_core::{
|
||||||
Clipboard, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse,
|
Clipboard, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse,
|
||||||
|
|
@ -65,7 +65,7 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
} else {
|
} else {
|
||||||
let title = title
|
let title = title
|
||||||
.map(|title| text::title4(title).width(Length::Fill).apply(Element::from))
|
.map(|title| text::title4(title).width(Length::Fill).apply(Element::from))
|
||||||
.unwrap_or_else(|| widget::space::horizontal().apply(Element::from));
|
.unwrap_or_else(|| widget::horizontal_space().apply(Element::from));
|
||||||
(title, None)
|
(title, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -196,40 +196,40 @@ impl<Message: Clone> Widget<Message, crate::Theme, Renderer> for ContextDrawer<'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
self.content
|
self.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation<()>,
|
operation: &mut dyn Operation<()>,
|
||||||
) {
|
) {
|
||||||
self.content
|
self.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.operate(&mut tree.children[0], layout, renderer, operation);
|
.operate(&mut tree.children[0], layout, renderer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
self.content.as_widget_mut().update(
|
self.content.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
|
|
@ -238,7 +238,7 @@ impl<Message: Clone> Widget<Message, crate::Theme, Renderer> for ContextDrawer<'
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -282,9 +282,8 @@ impl<Message: Clone> Widget<Message, crate::Theme, Renderer> for ContextDrawer<'
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'b>,
|
layout: Layout<'_>,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
_viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<iced_overlay::Element<'b, Message, crate::Theme, Renderer>> {
|
) -> Option<iced_overlay::Element<'b, Message, crate::Theme, Renderer>> {
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,7 @@
|
||||||
|
|
||||||
//! A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation.
|
//! A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation.
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
|
||||||
feature = "wayland",
|
|
||||||
target_os = "linux",
|
|
||||||
feature = "winit",
|
|
||||||
feature = "surface-message"
|
|
||||||
))]
|
|
||||||
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
|
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
|
||||||
use crate::widget::menu::{
|
use crate::widget::menu::{
|
||||||
self, CloseCondition, Direction, ItemHeight, ItemWidth, MenuBarState, PathHighlight,
|
self, CloseCondition, Direction, ItemHeight, ItemWidth, MenuBarState, PathHighlight,
|
||||||
|
|
@ -18,7 +13,7 @@ use derive_setters::Setters;
|
||||||
use iced::touch::Finger;
|
use iced::touch::Finger;
|
||||||
use iced::{Event, Vector, keyboard, window};
|
use iced::{Event, Vector, keyboard, window};
|
||||||
use iced_core::widget::{Tree, Widget, tree};
|
use iced_core::widget::{Tree, Widget, tree};
|
||||||
use iced_core::{Length, Point, Size, mouse, touch};
|
use iced_core::{Length, Point, Size, event, mouse, touch};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -32,7 +27,7 @@ pub fn context_menu<'a, Message: 'static + Clone>(
|
||||||
content: content.into(),
|
content: content.into(),
|
||||||
context_menu: context_menu.map(|menus| {
|
context_menu: context_menu.map(|menus| {
|
||||||
vec![menu::Tree::with_children(
|
vec![menu::Tree::with_children(
|
||||||
crate::Element::from(crate::widget::Row::new()),
|
crate::Element::from(crate::widget::row::<'static, Message>()),
|
||||||
menus,
|
menus,
|
||||||
)]
|
)]
|
||||||
}),
|
}),
|
||||||
|
|
@ -64,12 +59,7 @@ pub struct ContextMenu<'a, Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message: Clone + 'static> ContextMenu<'_, Message> {
|
impl<Message: Clone + 'static> ContextMenu<'_, Message> {
|
||||||
#[cfg(all(
|
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
|
||||||
feature = "wayland",
|
|
||||||
target_os = "linux",
|
|
||||||
feature = "winit",
|
|
||||||
feature = "surface-message"
|
|
||||||
))]
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn create_popup(
|
fn create_popup(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -95,7 +85,6 @@ impl<Message: Clone + 'static> ContextMenu<'_, Message> {
|
||||||
// close existing popups
|
// close existing popups
|
||||||
state.menu_states.clear();
|
state.menu_states.clear();
|
||||||
state.active_root.clear();
|
state.active_root.clear();
|
||||||
|
|
||||||
shell.publish(self.on_surface_action.as_ref().unwrap()(destroy_popup(id)));
|
shell.publish(self.on_surface_action.as_ref().unwrap()(destroy_popup(id)));
|
||||||
state.view_cursor = view_cursor;
|
state.view_cursor = view_cursor;
|
||||||
(
|
(
|
||||||
|
|
@ -260,7 +249,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
tree.diff_children(std::slice::from_mut(&mut self.content));
|
tree.children[0].diff(self.content.as_widget_mut());
|
||||||
let state = tree.state.downcast_mut::<LocalState>();
|
let state = tree.state.downcast_mut::<LocalState>();
|
||||||
state.menu_bar_state.inner.with_data_mut(|inner| {
|
state.menu_bar_state.inner.with_data_mut(|inner| {
|
||||||
menu_roots_diff(self.context_menu.as_mut().unwrap(), &mut inner.tree);
|
menu_roots_diff(self.context_menu.as_mut().unwrap(), &mut inner.tree);
|
||||||
|
|
@ -281,13 +270,13 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
limits: &iced_core::layout::Limits,
|
limits: &iced_core::layout::Limits,
|
||||||
) -> iced_core::layout::Node {
|
) -> iced_core::layout::Node {
|
||||||
self.content
|
self.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,29 +302,29 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: iced_core::Layout<'_>,
|
layout: iced_core::Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
operation: &mut dyn iced_core::widget::Operation<()>,
|
operation: &mut dyn iced_core::widget::Operation<()>,
|
||||||
) {
|
) {
|
||||||
self.content
|
self.content
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.operate(&mut tree.children[0], layout, renderer, operation);
|
.operate(&mut tree.children[0], layout, renderer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &iced::Event,
|
event: iced::Event,
|
||||||
layout: iced_core::Layout<'_>,
|
layout: iced_core::Layout<'_>,
|
||||||
cursor: iced_core::mouse::Cursor,
|
cursor: iced_core::mouse::Cursor,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
clipboard: &mut dyn iced_core::Clipboard,
|
clipboard: &mut dyn iced_core::Clipboard,
|
||||||
shell: &mut iced_core::Shell<'_, Message>,
|
shell: &mut iced_core::Shell<'_, Message>,
|
||||||
viewport: &iced::Rectangle,
|
viewport: &iced::Rectangle,
|
||||||
) {
|
) -> iced_core::event::Status {
|
||||||
let state = tree.state.downcast_mut::<LocalState>();
|
let state = tree.state.downcast_mut::<LocalState>();
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
|
|
||||||
|
|
@ -347,13 +336,14 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
.with_data(|d| !d.open && !d.active_root.is_empty());
|
.with_data(|d| !d.open && !d.active_root.is_empty());
|
||||||
|
|
||||||
let open = state.menu_bar_state.inner.with_data_mut(|state| {
|
let open = state.menu_bar_state.inner.with_data_mut(|state| {
|
||||||
if reset
|
if reset {
|
||||||
&& let Some(popup_id) = state.popup_id.get(&self.window_id).copied()
|
if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
|
||||||
&& let Some(handler) = self.on_surface_action.as_ref()
|
if let Some(handler) = self.on_surface_action.as_ref() {
|
||||||
{
|
|
||||||
shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
|
shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
|
||||||
state.reset();
|
state.reset();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
state.open
|
state.open
|
||||||
});
|
});
|
||||||
let mut was_open = false;
|
let mut was_open = false;
|
||||||
|
|
@ -366,6 +356,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
mouse::Button::Right | mouse::Button::Left,
|
mouse::Button::Right | mouse::Button::Left,
|
||||||
))
|
))
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. })
|
| Event::Touch(touch::Event::FingerPressed { .. })
|
||||||
|
| Event::Window(window::Event::Focused)
|
||||||
if open )
|
if open )
|
||||||
{
|
{
|
||||||
state.menu_bar_state.inner.with_data_mut(|state| {
|
state.menu_bar_state.inner.with_data_mut(|state| {
|
||||||
|
|
@ -374,21 +365,17 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
state.active_root.clear();
|
state.active_root.clear();
|
||||||
state.open = false;
|
state.open = false;
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
|
||||||
feature = "wayland",
|
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
|
||||||
target_os = "linux",
|
if let Some(id) = state.popup_id.remove(&self.window_id) {
|
||||||
feature = "winit",
|
|
||||||
feature = "surface-message"
|
|
||||||
))]
|
|
||||||
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
|
|
||||||
&& let Some(id) = state.popup_id.remove(&self.window_id)
|
|
||||||
{
|
|
||||||
{
|
{
|
||||||
let surface_action = self.on_surface_action.as_ref().unwrap();
|
let surface_action = self.on_surface_action.as_ref().unwrap();
|
||||||
shell.publish(surface_action(crate::surface::action::destroy_popup(id)));
|
shell
|
||||||
|
.publish(surface_action(crate::surface::action::destroy_popup(id)));
|
||||||
}
|
}
|
||||||
state.view_cursor = cursor;
|
state.view_cursor = cursor;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,11 +384,11 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Touch(touch::Event::FingerPressed { id, .. }) => {
|
Event::Touch(touch::Event::FingerPressed { id, .. }) => {
|
||||||
state.fingers_pressed.insert(*id);
|
state.fingers_pressed.insert(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Touch(touch::Event::FingerLifted { id, .. }) => {
|
Event::Touch(touch::Event::FingerLifted { id, .. }) => {
|
||||||
state.fingers_pressed.remove(id);
|
state.fingers_pressed.remove(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
@ -410,7 +397,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
// Present a context menu on a right click event.
|
// Present a context menu on a right click event.
|
||||||
if !was_open
|
if !was_open
|
||||||
&& self.context_menu.is_some()
|
&& self.context_menu.is_some()
|
||||||
&& (right_button_released(event) || (touch_lifted(event) && fingers_pressed == 2))
|
&& (right_button_released(&event) || (touch_lifted(&event) && fingers_pressed == 2))
|
||||||
{
|
{
|
||||||
state.context_cursor = cursor.position().unwrap_or_default();
|
state.context_cursor = cursor.position().unwrap_or_default();
|
||||||
let state = tree.state.downcast_mut::<LocalState>();
|
let state = tree.state.downcast_mut::<LocalState>();
|
||||||
|
|
@ -418,21 +405,15 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
state.open = true;
|
state.open = true;
|
||||||
state.view_cursor = cursor;
|
state.view_cursor = cursor;
|
||||||
});
|
});
|
||||||
#[cfg(all(
|
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
|
||||||
feature = "wayland",
|
|
||||||
target_os = "linux",
|
|
||||||
feature = "winit",
|
|
||||||
feature = "surface-message"
|
|
||||||
))]
|
|
||||||
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
|
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
|
||||||
self.create_popup(layout, cursor, renderer, shell, viewport, state);
|
self.create_popup(layout, cursor, renderer, shell, viewport, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
} else if !was_open && right_button_released(&event)
|
||||||
} else if !was_open && right_button_released(event)
|
|| (touch_lifted(&event))
|
||||||
|| (touch_lifted(event))
|
|| left_button_released(&event)
|
||||||
|| left_button_released(event)
|
|
||||||
{
|
{
|
||||||
state.menu_bar_state.inner.with_data_mut(|state| {
|
state.menu_bar_state.inner.with_data_mut(|state| {
|
||||||
was_open = true;
|
was_open = true;
|
||||||
|
|
@ -442,24 +423,24 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "wayland",
|
feature = "wayland",
|
||||||
target_os = "linux",
|
|
||||||
feature = "winit",
|
feature = "winit",
|
||||||
feature = "surface-message"
|
feature = "surface-message"
|
||||||
))]
|
))]
|
||||||
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
|
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
|
||||||
&& let Some(id) = state.popup_id.remove(&self.window_id)
|
if let Some(id) = state.popup_id.remove(&self.window_id) {
|
||||||
{
|
|
||||||
{
|
{
|
||||||
let surface_action = self.on_surface_action.as_ref().unwrap();
|
let surface_action = self.on_surface_action.as_ref().unwrap();
|
||||||
shell
|
shell.publish(surface_action(
|
||||||
.publish(surface_action(crate::surface::action::destroy_popup(id)));
|
crate::surface::action::destroy_popup(id),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
state.view_cursor = cursor;
|
state.view_cursor = cursor;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.content.as_widget_mut().update(
|
self.content.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
|
|
@ -468,7 +449,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
|
|
@ -476,15 +457,9 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: iced_core::Layout<'_>,
|
layout: iced_core::Layout<'_>,
|
||||||
_renderer: &crate::Renderer,
|
_renderer: &crate::Renderer,
|
||||||
_viewport: &iced::Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
||||||
#[cfg(all(
|
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
|
||||||
feature = "wayland",
|
|
||||||
target_os = "linux",
|
|
||||||
feature = "winit",
|
|
||||||
feature = "surface-message"
|
|
||||||
))]
|
|
||||||
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
|
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
|
||||||
&& self.window_id != window::Id::NONE
|
&& self.window_id != window::Id::NONE
|
||||||
&& self.on_surface_action.is_some()
|
&& self.on_surface_action.is_some()
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
|
||||||
if let Some(body) = dialog.body {
|
if let Some(body) = dialog.body {
|
||||||
if should_space {
|
if should_space {
|
||||||
content_col = content_col
|
content_col = content_col
|
||||||
.push(widget::space::vertical().height(Length::Fixed(space_xxs.into())));
|
.push(widget::vertical_space().height(Length::Fixed(space_xxs.into())));
|
||||||
}
|
}
|
||||||
content_col = content_col.push(
|
content_col = content_col.push(
|
||||||
widget::container(widget::scrollable(widget::text::body(body))).max_height(300.),
|
widget::container(widget::scrollable(widget::text::body(body))).max_height(300.),
|
||||||
|
|
@ -133,7 +133,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
|
||||||
for control in dialog.controls {
|
for control in dialog.controls {
|
||||||
if should_space {
|
if should_space {
|
||||||
content_col = content_col
|
content_col = content_col
|
||||||
.push(widget::space::vertical().height(Length::Fixed(space_s.into())));
|
.push(widget::vertical_space().height(Length::Fixed(space_s.into())));
|
||||||
}
|
}
|
||||||
content_col = content_col.push(control);
|
content_col = content_col.push(control);
|
||||||
should_space = true;
|
should_space = true;
|
||||||
|
|
@ -149,7 +149,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
|
||||||
if let Some(button) = dialog.tertiary_action {
|
if let Some(button) = dialog.tertiary_action {
|
||||||
button_row = button_row.push(button);
|
button_row = button_row.push(button);
|
||||||
}
|
}
|
||||||
button_row = button_row.push(widget::space::horizontal());
|
button_row = button_row.push(widget::horizontal_space());
|
||||||
if let Some(button) = dialog.secondary_action {
|
if let Some(button) = dialog.secondary_action {
|
||||||
button_row = button_row.push(button);
|
button_row = button_row.push(button);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ use iced::Vector;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Element,
|
Element,
|
||||||
widget::{Id, Widget},
|
iced::{
|
||||||
};
|
|
||||||
|
|
||||||
use iced::{
|
|
||||||
Event, Length, Rectangle,
|
Event, Length, Rectangle,
|
||||||
clipboard::{
|
clipboard::{
|
||||||
dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent},
|
dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent},
|
||||||
|
|
@ -19,10 +16,12 @@ use iced::{
|
||||||
event,
|
event,
|
||||||
id::Internal,
|
id::Internal,
|
||||||
mouse, overlay,
|
mouse, overlay,
|
||||||
};
|
},
|
||||||
use iced_core::{
|
iced_core::{
|
||||||
self, Clipboard, Shell, layout,
|
self, Clipboard, Shell, layout,
|
||||||
widget::{Tree, tree},
|
widget::{Tree, tree},
|
||||||
|
},
|
||||||
|
widget::{Id, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn dnd_destination<'a, Message: 'static>(
|
pub fn dnd_destination<'a, Message: 'static>(
|
||||||
|
|
@ -292,7 +291,7 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
tree.diff_children(std::slice::from_mut(&mut self.container));
|
tree.children[0].diff(self.container.as_widget_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> iced_core::widget::tree::State {
|
fn state(&self) -> iced_core::widget::tree::State {
|
||||||
|
|
@ -304,43 +303,43 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
self.container
|
self.container
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: layout::Layout<'_>,
|
layout: layout::Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
operation: &mut dyn iced_core::widget::Operation<()>,
|
operation: &mut dyn iced_core::widget::Operation<()>,
|
||||||
) {
|
) {
|
||||||
self.container
|
self.container
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.operate(&mut tree.children[0], layout, renderer, operation);
|
.operate(&mut tree.children[0], layout, renderer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: layout::Layout<'_>,
|
layout: layout::Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
self.container.as_widget_mut().update(
|
let s = self.container.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event,
|
event.clone(),
|
||||||
layout,
|
layout,
|
||||||
cursor,
|
cursor,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -348,8 +347,8 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
if shell.is_event_captured() {
|
if matches!(s, event::Status::Captured) {
|
||||||
return;
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = tree.state.downcast_mut::<State<()>>();
|
let state = tree.state.downcast_mut::<State<()>>();
|
||||||
|
|
@ -368,23 +367,23 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
OfferEvent::Enter {
|
OfferEvent::Enter {
|
||||||
x, y, mime_types, ..
|
x, y, mime_types, ..
|
||||||
},
|
},
|
||||||
)) if *id == Some(my_id) => {
|
)) if id == Some(my_id) => {
|
||||||
if !self.mime_matches(&mime_types) {
|
if !self.mime_matches(&mime_types) {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: DND_DEST_LOG_TARGET,
|
target: DND_DEST_LOG_TARGET,
|
||||||
"offer enter id={my_id:?} ignored (mimes={mime_types:?} not in {:?})",
|
"offer enter id={my_id:?} ignored (mimes={mime_types:?} not in {:?})",
|
||||||
self.mime_types
|
self.mime_types
|
||||||
);
|
);
|
||||||
return;
|
return event::Status::Ignored;
|
||||||
}
|
}
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: DND_DEST_LOG_TARGET,
|
target: DND_DEST_LOG_TARGET,
|
||||||
"offer enter id={my_id:?} coords=({x},{y}) mimes={mime_types:?}"
|
"offer enter id={my_id:?} coords=({x},{y}) mimes={mime_types:?}"
|
||||||
);
|
);
|
||||||
if let Some(msg) = state.on_enter(
|
if let Some(msg) = state.on_enter(
|
||||||
*x,
|
x,
|
||||||
*y,
|
y,
|
||||||
mime_types.clone(),
|
mime_types,
|
||||||
self.on_enter.as_ref().map(std::convert::AsRef::as_ref),
|
self.on_enter.as_ref().map(std::convert::AsRef::as_ref),
|
||||||
(),
|
(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -392,13 +391,13 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
}
|
}
|
||||||
if self.forward_drag_as_cursor {
|
if self.forward_drag_as_cursor {
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
let drag_cursor = mouse::Cursor::Available((*x as f32, *y as f32).into());
|
let drag_cursor = mouse::Cursor::Available((x as f32, y as f32).into());
|
||||||
let event = Event::Mouse(mouse::Event::CursorMoved {
|
let event = Event::Mouse(mouse::Event::CursorMoved {
|
||||||
position: drag_cursor.position().unwrap(),
|
position: drag_cursor.position().unwrap(),
|
||||||
});
|
});
|
||||||
self.container.as_widget_mut().update(
|
self.container.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
&event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
drag_cursor,
|
drag_cursor,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -407,8 +406,7 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Offer(_, OfferEvent::Leave)) => {
|
Event::Dnd(DndEvent::Offer(_, OfferEvent::Leave)) => {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
|
|
@ -425,9 +423,9 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
if self.forward_drag_as_cursor {
|
if self.forward_drag_as_cursor {
|
||||||
let drag_cursor = mouse::Cursor::Unavailable;
|
let drag_cursor = mouse::Cursor::Unavailable;
|
||||||
let event = Event::Mouse(mouse::Event::CursorLeft);
|
let event = Event::Mouse(mouse::Event::CursorLeft);
|
||||||
self.container.as_widget_mut().update(
|
self.container.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
&event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
drag_cursor,
|
drag_cursor,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -436,16 +434,16 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return event::Status::Ignored;
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Offer(id, OfferEvent::Motion { x, y })) if *id == Some(my_id) => {
|
Event::Dnd(DndEvent::Offer(id, OfferEvent::Motion { x, y })) if id == Some(my_id) => {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: DND_DEST_LOG_TARGET,
|
target: DND_DEST_LOG_TARGET,
|
||||||
"offer motion id={my_id:?} coords=({x},{y})"
|
"offer motion id={my_id:?} coords=({x},{y})"
|
||||||
);
|
);
|
||||||
if let Some(msg) = state.on_motion(
|
if let Some(msg) = state.on_motion(
|
||||||
*x,
|
x,
|
||||||
*y,
|
y,
|
||||||
self.on_motion.as_ref().map(std::convert::AsRef::as_ref),
|
self.on_motion.as_ref().map(std::convert::AsRef::as_ref),
|
||||||
self.on_enter.as_ref().map(std::convert::AsRef::as_ref),
|
self.on_enter.as_ref().map(std::convert::AsRef::as_ref),
|
||||||
(),
|
(),
|
||||||
|
|
@ -455,13 +453,13 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
|
|
||||||
if self.forward_drag_as_cursor {
|
if self.forward_drag_as_cursor {
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
let drag_cursor = mouse::Cursor::Available((*x as f32, *y as f32).into());
|
let drag_cursor = mouse::Cursor::Available((x as f32, y as f32).into());
|
||||||
let event = Event::Mouse(mouse::Event::CursorMoved {
|
let event = Event::Mouse(mouse::Event::CursorMoved {
|
||||||
position: drag_cursor.position().unwrap(),
|
position: drag_cursor.position().unwrap(),
|
||||||
});
|
});
|
||||||
self.container.as_widget_mut().update(
|
self.container.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
&event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
drag_cursor,
|
drag_cursor,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -470,8 +468,7 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Offer(_, OfferEvent::LeaveDestination)) => {
|
Event::Dnd(DndEvent::Offer(_, OfferEvent::LeaveDestination)) => {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
|
|
@ -484,9 +481,9 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
{
|
{
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
}
|
}
|
||||||
return;
|
return event::Status::Ignored;
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Offer(id, OfferEvent::Drop)) if *id == Some(my_id) => {
|
Event::Dnd(DndEvent::Offer(id, OfferEvent::Drop)) if id == Some(my_id) => {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: DND_DEST_LOG_TARGET,
|
target: DND_DEST_LOG_TARGET,
|
||||||
"offer drop id={my_id:?}"
|
"offer drop id={my_id:?}"
|
||||||
|
|
@ -496,29 +493,27 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
{
|
{
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
}
|
}
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Offer(id, OfferEvent::SelectedAction(action)))
|
Event::Dnd(DndEvent::Offer(id, OfferEvent::SelectedAction(action)))
|
||||||
if *id == Some(my_id) =>
|
if id == Some(my_id) =>
|
||||||
{
|
{
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: DND_DEST_LOG_TARGET,
|
target: DND_DEST_LOG_TARGET,
|
||||||
"offer selected-action id={my_id:?} action={action:?}"
|
"offer selected-action id={my_id:?} action={action:?}"
|
||||||
);
|
);
|
||||||
if let Some(msg) = state.on_action_selected(
|
if let Some(msg) = state.on_action_selected(
|
||||||
*action,
|
action,
|
||||||
self.on_action_selected
|
self.on_action_selected
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(std::convert::AsRef::as_ref),
|
.map(std::convert::AsRef::as_ref),
|
||||||
) {
|
) {
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
}
|
}
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Offer(id, OfferEvent::Data { data, mime_type }))
|
Event::Dnd(DndEvent::Offer(id, OfferEvent::Data { data, mime_type }))
|
||||||
if *id == Some(my_id) =>
|
if id == Some(my_id) =>
|
||||||
{
|
{
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: DND_DEST_LOG_TARGET,
|
target: DND_DEST_LOG_TARGET,
|
||||||
|
|
@ -532,33 +527,25 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
&& let Ok(s) = String::from_utf8(data[..data.len() - 1].to_vec())
|
&& let Ok(s) = String::from_utf8(data[..data.len() - 1].to_vec())
|
||||||
{
|
{
|
||||||
shell.publish(f(s));
|
shell.publish(f(s));
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(msg), ret) = state.on_data_received(
|
if let (Some(msg), ret) = state.on_data_received(
|
||||||
mime_type.clone(),
|
mime_type,
|
||||||
data.clone(),
|
data,
|
||||||
self.on_data_received
|
self.on_data_received
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(std::convert::AsRef::as_ref),
|
.map(std::convert::AsRef::as_ref),
|
||||||
self.on_finish.as_ref().map(std::convert::AsRef::as_ref),
|
self.on_finish.as_ref().map(std::convert::AsRef::as_ref),
|
||||||
) {
|
) {
|
||||||
shell.publish(msg);
|
shell.publish(msg);
|
||||||
if ret == event::Status::Captured {
|
return ret;
|
||||||
log::trace!(
|
|
||||||
target: DND_DEST_LOG_TARGET,
|
|
||||||
"offer data id={my_id:?} captured"
|
|
||||||
);
|
|
||||||
shell.capture_event();
|
|
||||||
}
|
}
|
||||||
return;
|
return event::Status::Captured;
|
||||||
}
|
|
||||||
shell.capture_event();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
event::Status::Ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -602,18 +589,13 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: layout::Layout<'b>,
|
layout: layout::Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
||||||
self.container.as_widget_mut().overlay(
|
self.container
|
||||||
&mut tree.children[0],
|
.as_widget_mut()
|
||||||
layout,
|
.overlay(&mut tree.children[0], layout, renderer, translation)
|
||||||
renderer,
|
|
||||||
viewport,
|
|
||||||
translation,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drag_destinations(
|
fn drag_destinations(
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
use iced_core::{widget::Operation, window};
|
use iced_core::window;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Element,
|
Element,
|
||||||
widget::{Id, Widget, container},
|
iced::{
|
||||||
};
|
|
||||||
use iced::{
|
|
||||||
Event, Length, Point, Rectangle, Vector,
|
Event, Length, Point, Rectangle, Vector,
|
||||||
clipboard::dnd::{DndAction, DndEvent, SourceEvent},
|
clipboard::dnd::{DndAction, DndEvent, SourceEvent},
|
||||||
event, mouse, overlay,
|
event, mouse, overlay,
|
||||||
};
|
},
|
||||||
use iced_core::{
|
iced_core::{
|
||||||
self, Clipboard, Shell, layout, renderer,
|
self, Clipboard, Shell, layout, renderer,
|
||||||
widget::{Tree, tree},
|
widget::{Tree, tree},
|
||||||
|
},
|
||||||
|
widget::{Id, Widget, container},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn dnd_source<
|
pub fn dnd_source<
|
||||||
|
|
@ -131,25 +131,21 @@ impl<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn on_start(mut self, on_start: Option<Message>) -> Self {
|
pub fn on_start(mut self, on_start: Option<Message>) -> Self {
|
||||||
self.on_start = on_start;
|
self.on_start = on_start;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn on_cancel(mut self, on_cancelled: Option<Message>) -> Self {
|
pub fn on_cancel(mut self, on_cancelled: Option<Message>) -> Self {
|
||||||
self.on_cancelled = on_cancelled;
|
self.on_cancelled = on_cancelled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn on_finish(mut self, on_finish: Option<Message>) -> Self {
|
pub fn on_finish(mut self, on_finish: Option<Message>) -> Self {
|
||||||
self.on_finish = on_finish;
|
self.on_finish = on_finish;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn window(mut self, window: window::Id) -> Self {
|
pub fn window(mut self, window: window::Id) -> Self {
|
||||||
self.window = Some(window);
|
self.window = Some(window);
|
||||||
self
|
self
|
||||||
|
|
@ -168,7 +164,7 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
tree.diff_children(std::slice::from_mut(&mut self.container));
|
tree.children[0].diff(self.container.as_widget_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> iced_core::widget::tree::State {
|
fn state(&self) -> iced_core::widget::tree::State {
|
||||||
|
|
@ -180,7 +176,7 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -188,44 +184,41 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
let node = self
|
let node = self
|
||||||
.container
|
.container
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.layout(&mut tree.children[0], renderer, limits);
|
.layout(&mut tree.children[0], renderer, limits);
|
||||||
state.cached_bounds = node.bounds();
|
state.cached_bounds = node.bounds();
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: layout::Layout<'_>,
|
layout: layout::Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn iced_core::widget::Operation<()>,
|
||||||
) {
|
) {
|
||||||
operation.custom(
|
operation.custom((&mut tree.state) as &mut dyn Any, Some(&self.id));
|
||||||
Some(&self.id),
|
operation.container(Some(&self.id), layout.bounds(), &mut |operation| {
|
||||||
layout.bounds(),
|
|
||||||
(&mut tree.state) as &mut dyn Any,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.container
|
self.container
|
||||||
.as_widget_mut()
|
.as_widget()
|
||||||
.operate(&mut tree.children[0], layout, renderer, operation);
|
.operate(&mut tree.children[0], layout, renderer, operation)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: &Event,
|
event: Event,
|
||||||
layout: layout::Layout<'_>,
|
layout: layout::Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) -> event::Status {
|
||||||
self.container.as_widget_mut().update(
|
let ret = self.container.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event,
|
event.clone(),
|
||||||
layout,
|
layout,
|
||||||
cursor,
|
cursor,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -240,32 +233,32 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
Event::Mouse(mouse_event) => match mouse_event {
|
Event::Mouse(mouse_event) => match mouse_event {
|
||||||
mouse::Event::ButtonPressed(mouse::Button::Left) => {
|
mouse::Event::ButtonPressed(mouse::Button::Left) => {
|
||||||
if let Some(position) = cursor.position() {
|
if let Some(position) = cursor.position() {
|
||||||
if !cursor.is_over(layout.bounds()) {
|
if !state.hovered {
|
||||||
return;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.left_pressed_position = Some(position);
|
state.left_pressed_position = Some(position);
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mouse::Event::ButtonReleased(mouse::Button::Left)
|
mouse::Event::ButtonReleased(mouse::Button::Left)
|
||||||
if state.left_pressed_position.is_some() =>
|
if state.left_pressed_position.is_some() =>
|
||||||
{
|
{
|
||||||
state.left_pressed_position = None;
|
state.left_pressed_position = None;
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
mouse::Event::CursorMoved { .. } => {
|
mouse::Event::CursorMoved { .. } => {
|
||||||
if let Some(position) = cursor.position() {
|
if let Some(position) = cursor.position() {
|
||||||
|
if state.hovered {
|
||||||
// We ignore motion if we do not possess drag content by now.
|
// We ignore motion if we do not possess drag content by now.
|
||||||
if self.drag_content.is_none() {
|
if self.drag_content.is_none() {
|
||||||
state.left_pressed_position = None;
|
state.left_pressed_position = None;
|
||||||
return;
|
return ret;
|
||||||
}
|
}
|
||||||
if let Some(left_pressed_position) = state.left_pressed_position
|
if let Some(left_pressed_position) = state.left_pressed_position {
|
||||||
&& position.distance(left_pressed_position) > self.drag_threshold
|
if position.distance(left_pressed_position) > self.drag_threshold {
|
||||||
{
|
|
||||||
if let Some(on_start) = self.on_start.as_ref() {
|
if let Some(on_start) = self.on_start.as_ref() {
|
||||||
shell.publish(on_start.clone());
|
shell.publish(on_start.clone())
|
||||||
}
|
}
|
||||||
let offset = Vector::new(
|
let offset = Vector::new(
|
||||||
left_pressed_position.x - layout.bounds().x,
|
left_pressed_position.x - layout.bounds().x,
|
||||||
|
|
@ -275,13 +268,19 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
state.is_dragging = true;
|
state.is_dragging = true;
|
||||||
state.left_pressed_position = None;
|
state.left_pressed_position = None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if !cursor.is_over(layout.bounds()) {
|
if !cursor.is_over(layout.bounds()) {
|
||||||
return;
|
state.hovered = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
shell.capture_event();
|
} else if cursor.is_over(layout.bounds()) {
|
||||||
|
state.hovered = true;
|
||||||
|
}
|
||||||
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => return ret,
|
||||||
},
|
},
|
||||||
Event::Dnd(DndEvent::Source(SourceEvent::Cancelled)) => {
|
Event::Dnd(DndEvent::Source(SourceEvent::Cancelled)) => {
|
||||||
if state.is_dragging {
|
if state.is_dragging {
|
||||||
|
|
@ -289,8 +288,9 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
shell.publish(m.clone());
|
shell.publish(m.clone());
|
||||||
}
|
}
|
||||||
state.is_dragging = false;
|
state.is_dragging = false;
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Source(SourceEvent::Finished)) => {
|
Event::Dnd(DndEvent::Source(SourceEvent::Finished)) => {
|
||||||
if state.is_dragging {
|
if state.is_dragging {
|
||||||
|
|
@ -298,11 +298,13 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
shell.publish(m.clone());
|
shell.publish(m.clone());
|
||||||
}
|
}
|
||||||
state.is_dragging = false;
|
state.is_dragging = false;
|
||||||
shell.capture_event();
|
return event::Status::Captured;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => return ret,
|
||||||
}
|
}
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -350,18 +352,13 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: layout::Layout<'b>,
|
layout: layout::Layout<'_>,
|
||||||
renderer: &crate::Renderer,
|
renderer: &crate::Renderer,
|
||||||
viewport: &Rectangle,
|
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
|
||||||
self.container.as_widget_mut().overlay(
|
self.container
|
||||||
&mut tree.children[0],
|
.as_widget_mut()
|
||||||
layout,
|
.overlay(&mut tree.children[0], layout, renderer, translation)
|
||||||
renderer,
|
|
||||||
viewport,
|
|
||||||
translation,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drag_destinations(
|
fn drag_destinations(
|
||||||
|
|
@ -414,6 +411,7 @@ impl<
|
||||||
/// Local state of the [`MouseListener`].
|
/// Local state of the [`MouseListener`].
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct State {
|
struct State {
|
||||||
|
hovered: bool,
|
||||||
left_pressed_position: Option<Point>,
|
left_pressed_position: Option<Point>,
|
||||||
is_dragging: bool,
|
is_dragging: bool,
|
||||||
cached_bounds: Rectangle,
|
cached_bounds: Rectangle,
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue