Compare commits

..

22 commits

Author SHA1 Message Date
4636d93eba fix: adapt to cosmic-theme background(transparent) accessor
Some checks are pending
Continuous Integration / formatting (push) Waiting to run
Continuous Integration / linting (push) Waiting to run
Leyoda 2026 – GPLv3
2026-07-02 19:28:24 +02:00
7df503977d Fix status menu build after upstream rebase
Some checks failed
Continuous Integration / formatting (push) Has been cancelled
Continuous Integration / linting (push) Has been cancelled
2026-06-13 08:39:59 +02:00
43415d98b1 yoda: use local dbus settings bindings 2026-06-13 08:39:59 +02:00
c38dba6a52 chore: align applets with local stack 2026-06-13 08:39:59 +02:00
Hojjat
090503ab2b chore: update cosmic-freedesktop-icons to fix dropbox tray icon 2026-06-13 08:39:59 +02:00
Hosted Weblate
658c38e913 i18n: translation updates from weblate
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Isaac Subirana <isaacsubiranac@gmail.com>
Co-authored-by: therealmate <hellogaming91@gmail.com>
Co-authored-by: Димко <Dymkovych@proton.me>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-audio/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-battery/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-bluetooth/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-network/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-notifications/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-power/ca/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-power/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applet-tiling/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applets-desktop-entries/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-applets-desktop-entries/uk/
Translation: Pop OS/COSMIC Applets Desktop Entries
Translation: Pop OS/COSMIC Audio Applet
Translation: Pop OS/COSMIC Battery Applet
Translation: Pop OS/COSMIC Bluetooth Applet
Translation: Pop OS/COSMIC Network Applet
Translation: Pop OS/COSMIC Notifications Applet
Translation: Pop OS/COSMIC Power Applet
Translation: Pop OS/COSMIC Tiling Applet
2026-06-13 08:39:59 +02:00
Hojjat
3f530fd31f chore: update libcosmic 2026-06-13 08:39:59 +02:00
Hojjat
9c4299429e fix: search the theme path provided by the app to find status-area icons 2026-06-13 08:39:59 +02:00
da53a9f45f feat: add themed launcher icon catalog
Some checks failed
Continuous Integration / formatting (push) Has been cancelled
Continuous Integration / linting (push) Has been cancelled
2026-05-26 12:03:59 +02:00
339ac4e3e4 fix: open launcher editor in existing popup
Some checks are pending
Continuous Integration / formatting (push) Waiting to run
Continuous Integration / linting (push) Waiting to run
2026-05-26 11:49:46 +02:00
cc501c7637 feat: add editable dock launchers
Some checks are pending
Continuous Integration / formatting (push) Waiting to run
Continuous Integration / linting (push) Waiting to run
2026-05-26 10:39:01 +02:00
93ab0f391d chore: fix applets redeploy path
Some checks are pending
Continuous Integration / formatting (push) Waiting to run
Continuous Integration / linting (push) Waiting to run
2026-05-26 09:26:01 +02:00
4416a5e9ea chore: use local COSMIC support crates 2026-05-26 09:21:53 +02:00
6c7f31e1ae chore: use local cosmic-text checkout 2026-05-26 09:21:18 +02:00
f2f53fc4d2 yoda: use local dbus settings bindings 2026-05-26 09:21:18 +02:00
82e00a3e16 chore: align applets with local stack 2026-05-26 09:20:01 +02:00
Votre Nom
bcc8072a3b fix(audio): accumuler les rafales scroll Pixels au lieu de signum()
Avec Wayland axis_v120 (scroll haute-résolution sur souris HID modernes),
un cran physique génère 5–8 events ScrollDelta::Pixels (~15–20px chacun).
L'ancien code passait chaque sub-event par .signum() puis -1/+1 à sink_volume,
donc un seul cran physique faisait varier le volume de 5 à 40% — résultat :
scroll up sur l'icône audio panel / dock coupait le son si le volume était
déjà bas.

Fix : thread_local accumulator des deltas Pixels, émission seulement
au passage du seuil de 15px par cran logique. Lines (souris classique
sans axis_v120) reste proportionnel y * WHEEL_STEP. round() au lieu de
truncation finale pour ne pas perdre les fractions de pourcent.

Leyoda 2026 - GPLv3
2026-05-26 09:17:36 +02:00
Votre Nom
57a435e830 chore: add redeploy.sh for /usr/local/bin install
Builds workspace release, backs up existing binaries, installs
cosmic-applets/cosmic-app-list/cosmic-panel-button to /usr/local/bin
(precedence over pacman package via $PATH).

Leyoda 2026 – GPLv3
2026-05-26 09:17:36 +02:00
Votre Nom
6fe087f4fd fix(wayland): graceful exit on compositor disconnect
Replace 3 panicking unwrap() in cosmic-app-list/wayland_handler.rs
(event loop dispatch + 2 conn.flush in screencopy) with logged
errors that break/return None instead.

Wrap cosmic-applets/main.rs entry point in panic::catch_unwind to
catch panics propagating from libcosmic/iced/winit (which we cannot
patch locally without forking) when the COSMIC compositor closes
the Wayland connection at logout. This eliminates the cascade of
~12 SIGABRT coredumps observed at session shutdown.

Panic strategy is unwind (default), catch_unwind is sound here.

Leyoda 2026 – GPLv3
2026-05-26 09:17:36 +02:00
0fa93ba21f yoda: smooth animated fisheye for dock hover (phase B v3, closes b)
Inter-icon hover changes were snapping because icon_scale_for read the
hovered icon's real rectangle directly. This adds a small animation
layer so the bell center lerps toward the target and the whole effect
fades in/out at the dock's edges.

CosmicAppList gains three fields:
- anim_hover_center: Option<(f32, f32)> — virtual cursor position,
  chases the hovered icon's center across ticks.
- anim_hover_intensity: f32 (0..1) — global fade-in/out of the
  fisheye. Target 1.0 while a dock icon is hovered, 0.0 otherwise.
- anim_last_tick: Option<Instant> — for dt-based exponential smoothing
  (time-constant tau = 60ms, ~99% of target reached in ~120ms).

A new Message::AnimTick(Instant) is emitted at ~60 fps by a
conditional iced::time::every subscription — only active when the
pointer is over a dock icon OR intensity hasn't faded back to ~0 yet,
so the panel stays idle when no one is hovering the dock.

icon_scale_for now reads anim_hover_center instead of rectangles[hovered]
and multiplies the bell's peak by anim_hover_intensity. Behaviour:
- Pointer slides A → B: bell glides continuously, both icons animate.
- Pointer enters dock: icons inflate smoothly over ~120 ms.
- Pointer leaves dock: icons deflate smoothly over ~120 ms.

Fallback paths (first frame, missing rectangles) still respond
instantly so the feature never looks 'stuck' before the animation
kicks in.
2026-05-26 09:17:36 +02:00
8fc11581ad yoda: fisheye magnification for dock hover (phase B v2 / c)
Replaces the binary 1.3× hover with a true gaussian bell curve — the
hovered icon still peaks at ~1.35×, but the ±1 neighbours also bulge
noticeably, ±2 a bit, and ±3+ relax to 1.0×. Footprint ~5 icons wide,
matching the macOS Dock fisheye feel.

Implementation in fn icon_scale_for(id):
- Reads the hovered icon's and the current icon's bounds from
  self.rectangles (already populated by the existing RectangleTracker
  subscription — no new plumbing).
- Distance = |this_center - hovered_center| along the panel's long axis
  (horizontal for Top/Bottom anchors, vertical for Left/Right).
- sigma = hovered_extent * 1.4 so the bell's half-width matches one
  icon width (neighbors clearly pulled, far icons untouched).
- scale = 1.0 + PEAK * exp(-(d/sigma)²) with PEAK = 0.35.
- Falls back to binary 1.35×/1.0× when rectangle data isn't populated
  yet (first render / resize) — visibly responsive even before the
  tracker catches up.

No widget signature changes vs v1, just a smarter formula. All five
as_icon call sites already pass the result of icon_scale_for so this
update propagates everywhere.

Still on the TODO list: smooth animation (b). Right now icon→icon
transitions snap instantly; a smoothed_hover_center + tick subscription
would lerp it. Deferred to a follow-up commit.
2026-05-26 09:17:36 +02:00
d090e60370 yoda: dock icon hover magnification (macOS Tahoe-style, phase B v1)
First pass at the signature macOS Dock effect — the icon under the
pointer grows, adjacent icons stay at base size. Full fisheye (smooth
bell-curve scaling on neighbors) can be a later iteration.

Changes in cosmic-app-list/src/app.rs:
- CosmicAppList gains a hovered_dock_item: Option<DockItemId>
  auto-initialized to None via #[derive(Default)].
- New Message::DockItemHover(Option<DockItemId>) handled in update()
  by just writing the field; view() then reads it to decide scale.
- DockItem::as_icon gains an icon_scale: f32 parameter. Inside it the
  cosmic_icon width/height = (base_icon_size * icon_scale) clamped
  to u16; indicator dot and other surrounding layout stay at base
  size so only the icon visually bulges.
- New App::icon_scale_for(id) helper: 1.3 if Some(id) == hovered,
  1.0 otherwise. Single place to tune the magnification factor.
- The two main dock rows (favorites + filtered_active_list) wrap
  their rendered applet_tooltip in widget::mouse_area with
  on_enter(DockItemHover(Some(id))) / on_exit(DockItemHover(None))
  and call icon_scale_for before rendering.
- The three remaining as_icon call sites (DnD preview, favorites
  overflow popup, active overflow popup) pass icon_scale = 1.0 —
  hover magnification on those surfaces would look jittery and isn't
  needed anyway.

Build: cargo build --release -p cosmic-app-list (≈ 7s). Binary
installed at /usr/local/bin/cosmic-app-list, backup kept as
.pre-magnification.
2026-05-26 09:17:36 +02:00
49 changed files with 587 additions and 1265 deletions

544
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,6 @@ tokio = { version = "1.49.0", features = ["full"] }
# cosmic-config = { path = "../libcosmic/cosmic-config" }
cosmic-config = { path = "../libcosmic/cosmic-config" }
serde = { version = "1.0.228", features = ["derive"] }
zlink = "0.5.0"
[profile.release]
opt-level = 3

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-app-list"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-app-list-config"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,8 +0,0 @@
run = Bixebitîne
run-on = Li ser { $gpu } bixebitîne
run-on-default = (Berdest)
pin = Bo destgeha sepanan bi dar bixe
quit = Biqedîne
quit-all = Hemûyan biqedîne
new-window = Çarçoveya nû
cosmic-app-list = Destgeha sepanan

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-a11y"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
[dependencies]

View file

@ -2,5 +2,5 @@ screen-reader = スクリーンリーダー
magnifier = 拡大鏡
settings = アクセシビリティ設定…
invert-colors = 色を反転
filter-colors = カラーフィルター
filter-colors = 色のフィルター
high-contrast = ハイコントラスト

View file

@ -1,6 +0,0 @@
screen-reader = Xwînerê dîmenderê
magnifier = Mezinker
invert-colors = Rengan berevajî bike
settings = Sazkariyên gihîştinê...
filter-colors = Rengan parzûn bike
high-contrast = Dijbiriya bilind

View file

@ -1,11 +1,10 @@
[package]
name = "cosmic-applet-audio"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"
[dependencies]
futures.workspace = true
i18n-embed-fl.workspace = true
i18n-embed.workspace = true
cosmic.workspace = true
@ -19,9 +18,6 @@ tracing.workspace = true
url = "2"
urlencoding = "2.1.3"
zbus.workspace = true
zlink.workspace = true
intmap = "3.1.3"
[dependencies.cosmic-settings-audio-client]
path = "../../cosmic-settings-daemon/audio-client"
features = ["codec"]
[dependencies.cosmic-settings-sound-subscription]
path = "../../cosmic-settings/subscriptions/sound"

View file

@ -1,7 +0,0 @@
output = Deran
input = Ketan
unknown-artist = Nenas
sound-settings = Sazkariyên dengê...
disconnected = Girêdana PulseAudio qut bû
no-device = Tu amûr nehatine hilbijartin
show-media-controls = Kontrolên mediyayê li ser destgehê nîşan bide

View file

@ -2,13 +2,12 @@
// SPDX-License-Identifier: GPL-3.0-only
mod localize;
mod model;
mod mouse_area;
use crate::localize::localize;
use config::{AudioAppletConfig, amplification_sink, amplification_source};
use cosmic::{
Apply, Element, Renderer, Task, Theme, app,
Element, Renderer, Task, Theme, app,
applet::{
column as applet_column,
cosmic_panel_config::PanelAnchor,
@ -19,24 +18,18 @@ use cosmic::{
cosmic_config::CosmicConfigEntry,
cosmic_theme::Spacing,
iced::{
self, Alignment, Length, Rectangle, Subscription,
self, Alignment, Length, Subscription,
futures::StreamExt,
widget::{self, column, row, slider},
window,
},
surface, theme,
widget::{
Row, button, container, divider, icon,
rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription},
space, text, toggler,
},
widget::{Row, button, container, divider, icon, space, text, toggler},
};
use cosmic_settings_audio_client::{self as audio_client, CosmicAudioProxy};
use futures::SinkExt;
use cosmic_settings_sound_subscription as css;
use iced::platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup};
use mpris_subscription::{MprisRequest, MprisUpdate};
use mpris2_zbus::player::PlaybackStatus;
use std::{cell::RefCell, rc::Rc, sync::Arc};
mod config;
mod mpris_subscription;
@ -57,10 +50,8 @@ pub struct Audio {
core: cosmic::app::Core,
/// Track the applet's popup window.
popup: Option<window::Id>,
/// Varlink connection to `com.system76.CosmicSettings.Audio`.
audio_client: Option<Rc<RefCell<audio_client::Client>>>,
/// Known audio device state
model: model::Model,
/// The model from cosmic-settings for managing pipewire devices.
model: css::Model,
/// Whether to expand the revealer of a source or sink device.
is_open: IsOpen,
/// Max slider volume for the sink device, as determined by the amplification property.
@ -77,14 +68,12 @@ pub struct Audio {
player_status: Option<mpris_subscription::PlayerStatus>,
/// Used to request an activation token for opening cosmic-settings.
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
rectangle_tracker: Option<RectangleTracker<u32>>,
rectangle: Option<iced::Rectangle>,
}
impl Audio {
fn output_icon_name(&self) -> &'static str {
let volume = self.model.active_sink.volume;
let mute = self.model.active_sink.mute;
let volume = self.model.sink_volume;
let mute = self.model.sink_mute;
if mute || volume == 0 {
"audio-volume-muted-symbolic"
} else if volume < 33 {
@ -99,8 +88,8 @@ impl Audio {
}
fn input_icon_name(&self) -> &'static str {
let volume = self.model.active_source.volume;
let mute = self.model.active_source.mute;
let volume = self.model.source_volume;
let mute = self.model.source_mute;
if mute || volume == 0 {
"microphone-sensitivity-muted-symbolic"
} else if volume < 33 {
@ -121,10 +110,8 @@ enum IsOpen {
Input,
}
#[derive(Clone, Debug)]
#[derive(Debug, Clone)]
pub enum Message {
/// Connection to `com.system76.CosmicSettings`.
Client(Arc<audio_client::Client>),
Ignore,
SetSinkVolume(u32),
SetSourceVolume(u32),
@ -142,9 +129,8 @@ pub enum Message {
MprisRequest(MprisRequest),
Token(TokenUpdate),
OpenSettings,
Subscription(audio_client::Event),
Subscription(css::Message),
Surface(surface::Action),
Rectangle(RectangleUpdate<u32>),
}
// TODO
@ -256,9 +242,15 @@ impl cosmic::Application for Audio {
const APP_ID: &'static str = "com.system76.CosmicAppletAudio";
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task<Message>) {
let mut model = css::Model::default();
model.unplugged_text = "Unplugged".into();
model.hd_audio_text = "HD Audio".into();
model.usb_audio_text = "USB Audio".into();
(
Self {
core,
model,
..Default::default()
},
Task::none(),
@ -279,14 +271,6 @@ impl cosmic::Application for Audio {
fn update(&mut self, message: Message) -> app::Task<Message> {
match message {
Message::Rectangle(u) => match u {
RectangleUpdate::Rectangle(r) => {
self.rectangle = Some(r.1);
}
RectangleUpdate::Init(tracker) => {
self.rectangle_tracker.replace(tracker);
}
},
Message::Ignore => {}
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
@ -307,21 +291,14 @@ impl cosmic::Application for Audio {
(100, &[][..])
};
let mut popup_settings = self.core.applet.get_popup_settings(
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
if let Some(r) = self.rectangle {
popup_settings.positioner.anchor_rect = Rectangle {
x: r.x as i32,
y: r.y as i32,
width: r.width as i32,
height: r.height as i32,
};
}
return get_popup(popup_settings);
}
}
@ -341,74 +318,52 @@ impl cosmic::Application for Audio {
}
}
Message::Subscription(message) => {
self.model.update(message);
return self
.model
.update(message)
.map(|message| Message::Subscription(message).into());
}
Message::SetDefaultSink(pos) => {
if let Some(&pos) = self.model.sinks.sorted_index.get(pos)
&& let Some(&node_id) = self.model.sinks.id.get(pos as usize)
&& let Some(client) = self.audio_client.as_mut()
{
futures::executor::block_on(async {
_ = client.borrow_mut().conn.set_default(node_id, true).await;
});
}
return self
.model
.set_default_sink(pos)
.map(|message| Message::Subscription(message).into());
}
Message::SetDefaultSource(pos) => {
if let Some(&pos) = self.model.sources.sorted_index.get(pos)
&& let Some(&node_id) = self.model.sources.id.get(pos as usize)
&& let Some(client) = self.audio_client.as_mut()
{
futures::executor::block_on(async {
_ = client.borrow_mut().conn.set_default(node_id, true).await;
});
}
return self
.model
.set_default_source(pos)
.map(|message| Message::Subscription(message).into());
}
Message::ToggleSinkMute => {
if let Some(ref mut client) = self.audio_client {
futures::executor::block_on(async {
_ = client.borrow_mut().conn.sink_mute_toggle().await;
});
}
}
Message::ToggleSinkMute => self.model.toggle_sink_mute(),
Message::ToggleSourceMute => {
if let Some(ref mut client) = self.audio_client {
futures::executor::block_on(async {
_ = client.borrow_mut().conn.source_mute_toggle().await;
});
}
}
Message::ToggleSourceMute => self.model.toggle_source_mute(),
Message::SetSinkVolume(volume) => {
if let Some(ref mut client) = self.audio_client {
self.model.active_sink.volume = volume;
self.model.active_sink.volume_text = volume.to_string();
futures::executor::block_on(async {
_ = client.borrow_mut().conn.set_sink_volume(volume).await;
});
}
return self
.model
.set_sink_volume(volume)
.map(|message| Message::Subscription(message).into());
}
Message::SetSourceVolume(volume) => {
if let Some(ref mut client) = self.audio_client {
self.model.active_source.volume = volume;
self.model.active_source.volume_text = volume.to_string();
futures::executor::block_on(async {
_ = client.borrow_mut().conn.set_source_volume(volume).await;
});
}
return self
.model
.set_source_volume(volume)
.map(|message| Message::Subscription(message).into());
}
Message::ToggleMediaControlsInTopPanel(enabled) => {
self.config.show_media_controls_in_top_panel = enabled;
if let Ok(helper) =
cosmic::cosmic_config::Config::new(Self::APP_ID, AudioAppletConfig::VERSION)
&& let Err(err) = self.config.write_entry(&helper)
{
tracing::error!(?err, "Error writing config");
if let Err(err) = self.config.write_entry(&helper) {
tracing::error!(?err, "Error writing config");
}
}
}
Message::CloseRequested(id) => {
@ -506,12 +461,6 @@ impl cosmic::Application for Audio {
cosmic::app::Action::Surface(a),
));
}
Message::Client(client) => {
if let Some(client) = Arc::into_inner(client) {
self.audio_client = Some(Rc::new(RefCell::new(client)));
self.model = model::Model::default();
}
}
}
Task::none()
@ -527,50 +476,7 @@ impl cosmic::Application for Audio {
}),
mpris_subscription::mpris_subscription(0).map(Message::Mpris),
activation_token_subscription(0).map(Message::Token),
Subscription::run(|| {
iced::stream::channel(
1,
move |mut emitter: futures::channel::mpsc::Sender<_>| async move {
loop {
let mut client = match audio_client::connect().await {
Ok(client) => client,
Err(why) => {
if let zlink::Error::Io(ref why) = why
&& why.kind() == std::io::ErrorKind::NotFound
{
tracing::error!(
"cosmic-settings-daemon varlink service not found."
);
} else {
tracing::error!(
?why,
"failed to connect to cosmic-settings's varlink service"
);
}
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
continue;
}
};
if let Ok(Ok(mut stream)) = client.recv_events().await {
_ = emitter.send(Message::Client(Arc::new(client))).await;
while let Some(message) = stream.next().await {
match message {
Ok(event) => {
_ = emitter.send(Message::Subscription(event)).await;
}
Err(why) => {
tracing::error!(?why, "event error");
}
}
}
}
}
},
)
}),
rectangle_tracker_subscription(0).map(|update| Message::Rectangle(update.1)),
Subscription::run(|| css::watch().map(Message::Subscription)),
])
}
@ -611,64 +517,53 @@ impl cosmic::Application for Audio {
return Message::Ignore;
}
let new_volume = (self.model.active_sink.volume as f64 + (scroll_vector as f64))
let new_volume = (self.model.sink_volume as f64 + (scroll_vector as f64))
.clamp(0.0, self.max_sink_volume as f64);
Message::SetSinkVolume(new_volume.round() as u32)
});
let mut has_playback_buttons = false;
let playback_buttons = (!self.core.applet.suggested_bounds.as_ref().is_some_and(|c| {
// if we have a configure for width and height, we're in a overflow popup
c.width > 0. && c.height > 0.
}))
.then(|| self.playback_buttons());
let mut ret = if let Some(playback_buttons) = playback_buttons
&& !playback_buttons.is_empty()
{
has_playback_buttons = true;
Element::from(match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Element::from(
applet_column::Column::with_children(playback_buttons)
.push(btn)
.align_x(Alignment::Center)
.height(Length::Shrink)
// TODO configurable variable from the panel?
.spacing(
-(self.core.applet.suggested_padding(true).0 as f32)
* self.core.applet.padding_overlap,
self.core
.applet
.autosize_window(
if let Some(playback_buttons) = playback_buttons
&& !playback_buttons.is_empty()
{
match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Element::from(
applet_column::Column::with_children(playback_buttons)
.push(btn)
.align_x(Alignment::Center)
.height(Length::Shrink)
// TODO configurable variable from the panel?
.spacing(
-(self.core.applet.suggested_padding(true).0 as f32)
* self.core.applet.padding_overlap,
),
),
),
PanelAnchor::Top | PanelAnchor::Bottom => {
applet_row::Row::with_children(playback_buttons)
.push(btn)
.align_y(Alignment::Center)
.width(Length::Shrink)
// TODO configurable variable from the panel?
.spacing(
-(self.core.applet.suggested_padding(true).0 as f32)
* self.core.applet.padding_overlap,
)
.into()
}
})
} else {
btn.into()
};
if let Some(tracker) = self.rectangle_tracker.as_ref()
&& has_playback_buttons
{
ret = tracker.container(0, ret).into()
}
if !self.core.applet.suggested_bounds.as_ref().is_some_and(|c| {
// if we have a configure for width and height, we're in a overflow popup
c.width > 0. && c.height > 0.
}) {
ret = self.core.applet.autosize_window(ret).into();
}
ret
PanelAnchor::Top | PanelAnchor::Bottom => {
applet_row::Row::with_children(playback_buttons)
.push(btn)
.align_y(Alignment::Center)
.width(Length::Shrink)
// TODO configurable variable from the panel?
.spacing(
-(self.core.applet.suggested_padding(true).0 as f32)
* self.core.applet.padding_overlap,
)
.into()
}
}
} else {
btn.into()
},
)
.into()
}
fn view_window(&self, _id: window::Id) -> Element<'_, Message> {
@ -676,21 +571,19 @@ impl cosmic::Application for Audio {
space_xxs, space_s, ..
} = theme::active().cosmic().spacing;
let sink: Option<&str> = self
let sink = self
.model
.sinks
.active()
.map(|pos| self.model.sinks.sorted_display[pos].as_ref());
let source: Option<&str> = self
.active_sink()
.and_then(|pos| self.model.sinks().get(pos));
let source = self
.model
.sources
.active()
.map(|pos| self.model.sources.sorted_display[pos].as_ref());
.active_source()
.and_then(|pos| self.model.sources().get(pos));
let mut audio_content = {
let output_slider = slider(
0..=self.max_sink_volume,
self.model.active_sink.volume,
self.model.sink_volume,
Message::SetSinkVolume,
)
.width(Length::FillPortion(5))
@ -698,7 +591,7 @@ impl cosmic::Application for Audio {
let input_slider = slider(
0..=self.max_source_volume,
self.model.active_source.volume,
self.model.source_volume,
Message::SetSourceVolume,
)
.width(Length::FillPortion(5))
@ -717,7 +610,7 @@ impl cosmic::Application for Audio {
.line_height(24)
.on_press(Message::ToggleSinkMute),
output_slider,
container(text(&self.model.active_sink.volume_text).size(16))
container(text(&self.model.sink_volume_text).size(16))
.width(Length::FillPortion(1))
.align_x(Alignment::End)
]
@ -736,7 +629,7 @@ impl cosmic::Application for Audio {
.line_height(24)
.on_press(Message::ToggleSourceMute),
input_slider,
container(text(&self.model.active_source.volume_text).size(16))
container(text(&self.model.source_volume_text).size(16))
.width(Length::FillPortion(1))
.align_x(Alignment::End)
]
@ -751,7 +644,7 @@ impl cosmic::Application for Audio {
Some(sink) => sink.to_owned(),
None => fl!("no-device"),
},
&self.model.sinks.sorted_display,
self.model.sinks(),
Message::OutputToggle,
Message::SetDefaultSink,
),
@ -762,7 +655,7 @@ impl cosmic::Application for Audio {
Some(source) => source.to_owned(),
None => fl!("no-device"),
},
&self.model.sources.sorted_display,
self.model.sources(),
Message::InputToggle,
Message::SetDefaultSource,
)
@ -818,15 +711,19 @@ impl cosmic::Application for Audio {
}
if let Some(play) = self.is_play() {
control_elements.push(
button::icon(icon::from_name(if play { PLAY } else { PAUSE }).symbolic(true))
.extra_small()
.class(cosmic::theme::Button::AppletIcon)
.on_press(if play {
Message::MprisRequest(MprisRequest::Play)
} else {
Message::MprisRequest(MprisRequest::Pause)
})
.into(),
button::icon(
icon::from_name(if play { PLAY } else { PAUSE })
.size(32)
.symbolic(true),
)
.extra_small()
.class(cosmic::theme::Button::AppletIcon)
.on_press(if play {
Message::MprisRequest(MprisRequest::Play)
} else {
Message::MprisRequest(MprisRequest::Pause)
})
.into(),
);
}
if let Some(go_next) = self.go_next(32) {
@ -877,21 +774,20 @@ impl cosmic::Application for Audio {
}
}
fn revealer<'a>(
fn revealer(
open: bool,
title: String,
selected: String,
devices: &'a [Arc<str>],
devices: &[String],
toggle: Message,
mut change: impl FnMut(usize) -> Message + 'static,
) -> widget::Column<'a, Message, crate::Theme, Renderer> {
) -> widget::Column<'static, Message, crate::Theme, Renderer> {
if open {
devices.iter().enumerate().fold(
devices.iter().cloned().enumerate().fold(
column![revealer_head(open, title, selected, toggle)].width(Length::Fill),
move |col, (id, name)| {
|col, (id, name)| {
col.push(
text::body(name.as_ref())
.apply(menu_button)
menu_button(text::body(name))
.on_press(change(id))
.width(Length::Fill)
.padding([8, 48]),
@ -909,9 +805,9 @@ fn revealer_head(
selected: String,
toggle: Message,
) -> cosmic::widget::Button<'static, Message> {
cosmic::widget::column::with_capacity(2)
.push(text::body(title).width(Length::Fill))
.push(text::caption(selected))
.apply(menu_button)
.on_press(toggle)
menu_button(column![
text::body(title).width(Length::Fill),
text::caption(selected),
])
.on_press(toggle)
}

View file

@ -1,393 +0,0 @@
// Copyright 2026 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::sync::Arc;
use cosmic_settings_audio_client::{self as audio_client, Availability, RouteInfo};
use intmap::IntMap;
pub type DeviceId = u32;
pub type NodeId = u32;
#[derive(Debug, Default)]
pub struct Model {
pub device_routes: IntMap<DeviceId, Vec<RouteInfo>>,
pub node_devices: IntMap<NodeId, Option<u32>>,
pub sinks: Nodes,
pub sources: Nodes,
pub active_sink: ActiveNode,
pub active_source: ActiveNode,
pub default_sink: Option<NodeId>,
pub default_source: Option<NodeId>,
}
#[derive(Debug, Default)]
pub struct Nodes {
active: Option<usize>,
pub sorted_display: Box<[Arc<str>]>,
pub sorted_index: Box<[u16]>,
pub balance: Vec<Option<f32>>,
pub card_profile_device: Vec<Option<u32>>,
pub description: Vec<String>,
pub devices: Vec<Option<NodeId>>,
pub display: Vec<Arc<str>>,
pub mute: Vec<bool>,
pub name: Vec<String>,
pub id: Vec<NodeId>,
pub volume: Vec<u32>,
}
impl Nodes {
pub fn active(&self) -> Option<usize> {
self.active.and_then(|active| {
self.sorted_index
.iter()
.position(|idx| *idx as usize == active)
})
}
pub fn dropdown_sort(&mut self) {
let mut enumerated_displays = self
.display
.clone()
.into_iter()
.enumerate()
.map(|(index, display)| (index as u16, display))
.collect::<Vec<_>>();
enumerated_displays.sort_by_key(|v| v.1.clone());
let (indexes, displays): (Vec<_>, Vec<_>) = enumerated_displays.into_iter().unzip();
self.sorted_display = displays.into_boxed_slice();
self.sorted_index = indexes.into_boxed_slice();
}
pub fn remove(&mut self, node_id: u32) -> bool {
let Some(pos) = self.id.iter().position(|id| node_id == *id) else {
return false;
};
self.balance.remove(pos);
self.card_profile_device.remove(pos);
self.description.remove(pos);
self.devices.remove(pos);
self.display.remove(pos);
self.mute.remove(pos);
self.name.remove(pos);
self.id.remove(pos);
self.volume.remove(pos);
self.dropdown_sort();
if self.active == Some(pos) {
self.active = None;
}
true
}
}
#[derive(Debug, Default)]
pub struct ActiveNode {
pub volume_text: String,
pub volume: u32,
pub mute: bool,
}
impl Model {
pub fn update(&mut self, event: audio_client::Event) {
tracing::debug!(?event, "update");
match event {
audio_client::Event::NodeMute(node_id, mute) => {
if let Some(pos) = self.sinks.id.iter().position(|id| node_id == *id) {
self.sinks.mute[pos] = mute;
if self.sinks.active == Some(pos) {
self.active_sink.mute = mute;
}
} else if let Some(pos) = self.sources.id.iter().position(|id| node_id == *id) {
self.sources.mute[pos] = mute;
if self.sources.active == Some(pos) {
self.active_source.mute = mute;
}
}
}
audio_client::Event::NodeVolume(node_id, volume, balance) => {
if let Some(pos) = self.sinks.id.iter().position(|id| node_id == *id) {
self.sinks.volume[pos] = volume;
self.sinks.balance[pos] = balance;
if self.default_sink.as_ref().is_some_and(|&id| id == node_id)
&& let Some(pos) = self.sinks.active
{
self.active_sink.mute = self.sinks.mute[pos];
self.active_sink.volume = self.sinks.volume[pos];
self.active_sink.volume_text = self.active_sink.volume.to_string();
}
} else if let Some(pos) = self.sources.id.iter().position(|id| node_id == *id) {
self.sources.volume[pos] = volume;
self.sources.balance[pos] = balance;
if self
.default_source
.as_ref()
.is_some_and(|&id| id == node_id)
&& let Some(pos) = self.sources.active
{
self.active_source.mute = self.sources.mute[pos];
self.active_source.volume = self.sources.volume[pos];
self.active_source.volume_text = self.active_source.volume.to_string();
}
}
}
audio_client::Event::DefaultSink(node_id) => {
self.default_sink = Some(node_id);
if let Some(pos) = self.sinks.id.iter().position(|&id| id == node_id) {
self.sinks.active = Some(pos);
self.active_sink.mute = self.sinks.mute[pos];
self.active_sink.volume = self.sinks.volume[pos];
self.active_sink.volume_text = self.active_sink.volume.to_string();
}
}
audio_client::Event::DefaultSource(node_id) => {
self.default_source = Some(node_id);
if let Some(pos) = self.sources.id.iter().position(|&id| id == node_id) {
self.sources.active = Some(pos);
self.active_source.mute = self.sources.mute[pos];
self.active_source.volume = self.sources.volume[pos];
self.active_source.volume_text = self.active_source.volume.to_string();
}
}
audio_client::Event::Node(node_id, node) => {
self.node_devices.insert(node_id, node.device_id);
if node.is_sink {
let pos = if let Some(pos) = self.sinks.id.iter().position(|&id| id == node_id)
{
self.sinks.description[pos] = self.translate(&node.description);
self.sinks.name[pos] = node.name;
self.sinks.card_profile_device[pos] = node.card_profile_device;
pos
} else {
self.sinks.display.push(Arc::default());
self.sinks
.description
.push(self.translate(&node.description));
self.sinks.id.push(node_id);
self.sinks.volume.push(0);
self.sinks.balance.push(None);
self.sinks.mute.push(false);
self.sinks.name.push(node.name);
self.sinks.devices.push(node.device_id);
self.sinks
.card_profile_device
.push(node.card_profile_device);
self.sinks.id.len() - 1
};
self.sinks.display[pos] = node
.device_id
.zip(node.card_profile_device)
.and_then(|(device_id, node_card_profile_device)| {
let routes = self.device_routes.get(device_id)?;
for route in routes {
if matches!(route.availability, Availability::No) || !route.is_sink
{
continue;
}
if route.devices.contains(&node_card_profile_device) {
return Some(node_name(
&self.translate(&route.description),
&self.sinks.description[pos],
));
}
}
None
})
.unwrap_or_else(|| {
node_name(
&node.device_profile_description,
&self.sinks.description[pos],
)
});
self.sinks.dropdown_sort();
if let Some(default_node_id) = self.default_sink
&& default_node_id == node_id
{
self.sinks.active = Some(pos);
self.active_sink.mute = self.sinks.mute[pos];
self.active_sink.volume = self.sinks.volume[pos];
self.active_sink.volume_text = self.active_sink.volume.to_string();
}
} else {
let pos =
if let Some(pos) = self.sources.id.iter().position(|&id| id == node_id) {
self.sources.description[pos] = self.translate(&node.description);
self.sources.name[pos] = node.name;
self.sources.card_profile_device[pos] = node.card_profile_device;
pos
} else {
self.sources
.description
.push(self.translate(&node.description));
self.sources.display.push(Arc::default());
self.sources.id.push(node_id);
self.sources.volume.push(0);
self.sources.balance.push(None);
self.sources.mute.push(false);
self.sources.name.push(node.name);
self.sources.devices.push(node.device_id);
self.sources
.card_profile_device
.push(node.card_profile_device);
self.sources.id.len() - 1
};
if let Some(name) = node
.device_id
.zip(node.card_profile_device)
.map(|(device_id, node_card_profile_device)| {
let routes = self.device_routes.get(device_id)?;
for route in routes {
if route.is_sink || matches!(route.availability, Availability::No) {
continue;
}
if route.devices.contains(&node_card_profile_device) {
return Some(node_name(
&self.translate(&route.description),
&self.sources.description[pos],
));
}
}
None
})
.unwrap_or_else(|| {
Some(node_name(
&node.device_profile_description,
&self.sources.description[pos],
))
})
{
self.sources.display[pos] = name;
self.sources.dropdown_sort();
} else {
// Remove sources that are unplugged.
self.sources.remove(node_id);
return;
}
if let Some(default_node_id) = self.default_source
&& default_node_id == node_id
{
self.sources.active = Some(pos);
self.active_source.mute = self.sources.mute[pos];
self.active_source.volume = self.sources.volume[pos];
self.active_source.volume_text = self.active_source.volume.to_string();
}
}
}
audio_client::Event::ActiveRoute(device_id, _index, route) => {
self.update_device_names(device_id, &route);
}
audio_client::Event::Route(device_id, index, route) => {
let routes = self.device_routes.entry(device_id).or_default();
if index == 0 {
*routes = vec![route];
} else {
if routes.len() < index as usize + 1 {
let additional = (index as usize + 1) - routes.capacity();
routes.reserve_exact(additional);
routes.extend(std::iter::repeat_n(RouteInfo::default(), additional));
}
routes[index as usize] = route;
}
}
audio_client::Event::RemoveNode(node_id) => {
self.node_devices.remove(node_id);
if !self.sinks.remove(node_id) {
self.sources.remove(node_id);
}
}
audio_client::Event::RemoveDevice(device_id) => {
self.device_routes.remove(device_id);
}
_ => (),
}
}
fn update_device_names(&mut self, device_id: DeviceId, route: &RouteInfo) {
if matches!(route.availability, Availability::No) {
return;
}
let compatible_nodes = self.node_devices.iter().filter_map(|(node, &dev_id)| {
if dev_id? == device_id {
Some(node)
} else {
None
}
});
if route.is_sink {
for n_id in compatible_nodes {
let Some(pos) = self.sinks.id.iter().position(|&node| node == n_id) else {
continue;
};
let Some(card_profile_device) = self.sinks.card_profile_device[pos] else {
continue;
};
if route.devices.contains(&card_profile_device) {
self.sinks.display[pos] = node_name(
&self.translate(&route.description),
&self.sinks.description[pos],
);
self.sinks.dropdown_sort();
break;
}
}
} else {
for n_id in compatible_nodes {
let Some(pos) = self.sources.id.iter().position(|&node| node == n_id) else {
continue;
};
let Some(card_profile_device) = self.sources.card_profile_device[pos] else {
continue;
};
if route.devices.contains(&card_profile_device) {
self.sources.display[pos] = node_name(
&self.translate(&route.description),
&self.sources.description[pos],
);
self.sources.dropdown_sort();
break;
}
}
}
}
pub fn translate(&self, description: &str) -> String {
description
.replace("High Definition", "HD")
.replace("DisplayPort", "DP")
.replace("Controller", "")
}
}
fn node_name(route: &str, node: &str) -> Arc<str> {
if route.is_empty() {
node.to_owned()
} else {
[route, " - ", node].concat()
}
.into()
}

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-battery"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,14 +0,0 @@
battery = Betarî
seconds = ç
minutes = x
hours = d
until-empty = Heya vala dibe
power-settings = Sazkariyên hêz û betariyê...
battery-desc = Bikaranîna hêzê kêmkirî û performans.
balanced = Hevseng
balanced-desc = Performansa standard û bikaranîna betariyê.
performance = Performansa bilind
performance-desc = Performansa bilind û bikaranîna hêzê.
max-charge = Jiyana betariyê xwe dirêj bike bi riya danîna sînorê herî mezin yê tijîkrinê %80
dgpu-running = YKG a veqetandî çalak e û dikare jiyana betariyê kêm bike
dgpu-applications = Sepanên ku YKG a veqetandî ya { $gpu_name } bi kar tînin

View file

@ -169,20 +169,6 @@ impl CosmicBatteryApplet {
self.charging_limit = Some(limit);
self.update_battery(self.battery_percent, self.on_battery);
}
fn panel_icon_name(&self) -> &str {
if self.no_battery {
if self.screen_brightness.is_some() {
self.display_icon_name.as_str()
} else if self.kbd_brightness.is_some() {
"keyboard-brightness-symbolic"
} else {
"plugged-into-power-symbolic"
}
} else {
self.icon_name.as_str()
}
}
}
#[derive(Debug, Clone)]
@ -536,9 +522,19 @@ impl cosmic::Application for CosmicBatteryApplet {
let applet_padding = self.core.applet.suggested_padding(true);
let mut children = vec![
icon::from_name(self.panel_icon_name())
.size(suggested_size.0)
.into(),
icon::from_name(if self.no_battery {
if self.screen_brightness.is_some() {
self.display_icon_name.as_str()
} else if self.kbd_brightness.is_some() {
"keyboard-brightness-symbolic"
} else {
self.icon_name.as_str()
}
} else {
self.icon_name.as_str()
})
.size(suggested_size.0)
.into(),
];
if self.config.show_percentage && !self.no_battery {

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-bluetooth"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,12 +0,0 @@
cancel = Têk bibe
bluetooth = Bluetooth
connected = Hat girêdan
other-devices = Amûrên Bluetooth ên din
settings = Sazkariyên Bluetooth...
confirm-pin = Tika ye piştrast bike ku PIN a jêrîn bi ya ku li ser { $deviceName } tê xuyakirin re li hev tê
confirm = Bipejirîne
check-device = Piştrast bike ku { $deviceName } çalak e, di hundir rêjeyê de ye, û amade ye ji bo cotkirinê.
try-again = Dîsa hewl bide
discoverable = Dîtbar
pairable = Cotbar
unsuccessful = Cotkirin têkçûyî ye

View file

@ -21,7 +21,7 @@ use cosmic::{
widget::{Column, column, container, row},
},
theme,
widget::{button, divider, icon, indeterminate_circular, scrollable, text},
widget::{button, divider, icon, scrollable, text},
};
use futures::FutureExt;
use std::{collections::HashMap, time::Duration};
@ -368,12 +368,14 @@ impl cosmic::Application for CosmicBluetoothApplet {
);
}
BluerDeviceStatus::Paired => {}
BluerDeviceStatus::Connecting
| BluerDeviceStatus::Disconnecting
| BluerDeviceStatus::Pairing => {
row = row.push(indeterminate_circular().size(24.0));
BluerDeviceStatus::Connecting | BluerDeviceStatus::Disconnecting => {
row = row.push(
icon::from_name("process-working-symbolic")
.size(24)
.symbolic(true),
);
}
BluerDeviceStatus::Disconnected => continue,
BluerDeviceStatus::Disconnected | BluerDeviceStatus::Pairing => continue,
}
known_bluetooth.push(

View file

@ -567,21 +567,22 @@ impl BluerSessionState {
tick(&mut interval).await;
let new_status = adapter_clone.is_powered().await.unwrap_or_default();
devices = build_device_list(devices, &adapter_clone).await;
let power_changed = new_status != status;
status = new_status;
let state = BluerState {
devices: devices.clone(),
bluetooth_enabled: status,
};
if power_changed && state.bluetooth_enabled {
for d in &state.devices {
if d.paired_and_trusted() {
_ = req_tx.send(BluerRequest::ConnectDevice(d.address)).await;
if new_status != status {
status = new_status;
let state = BluerState {
devices: devices.clone(),
bluetooth_enabled: status,
};
if state.bluetooth_enabled {
for d in &state.devices {
if d.paired_and_trusted() {
_ = req_tx.send(BluerRequest::ConnectDevice(d.address)).await;
}
}
}
_ = wake_up_discover_tx.send(()).await;
let _ = tx.send(BluerSessionEvent::ChangesProcessed(state)).await;
}
let _ = tx.send(BluerSessionEvent::ChangesProcessed(state)).await;
}
});
}
@ -672,9 +673,6 @@ impl BluerSessionState {
}
}
// Always refresh power state from the adapter
// to avoid stale cached is_powered values
is_powered = adapter_clone.is_powered().await.unwrap_or_default();
let _ = tx
.send(BluerSessionEvent::ChangesProcessed(BluerState {
devices: devices.clone(),
@ -854,7 +852,13 @@ async fn bluer_state(adapter: &Adapter) -> BluerState {
#[inline(never)]
async fn build_device_list(mut devices: Vec<BluerDevice>, adapter: &Adapter) -> Vec<BluerDevice> {
let addrs = adapter.device_addresses().await.unwrap_or_default();
let addrs: Vec<Address> = adapter
.device_addresses()
.await
.unwrap_or_default()
.into_iter()
.filter(|addr| !devices.iter().any(|d| d.address == *addr))
.collect();
devices.clear();
if addrs.len() > devices.capacity() {

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-input-sources"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,2 +0,0 @@
show-keyboard-layout = Awaya kilîtdankê nîşan bide...
keyboard-settings = Sazkariyên kilîtdankê...

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-minimize"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -121,7 +121,6 @@ struct AppData {
toplevel_info_state: ToplevelInfoState,
toplevel_manager_state: ToplevelManagerState,
seat_state: SeatState,
captured_toplevels: std::collections::HashSet<ExtForeignToplevelHandleV1>,
}
struct CaptureData {
@ -352,36 +351,6 @@ impl AppData {
}
});
}
fn handle_toplevel(&mut self, toplevel: &ExtForeignToplevelHandleV1, is_new: bool) {
let Some(info) = self.toplevel_info_state.info(toplevel) else {
return;
};
if info
.state
.contains(&zcosmic_toplevel_handle_v1::State::Minimized)
{
// Capture a thumbnail once, on the transition into minimized.
if self.captured_toplevels.insert(toplevel.clone()) {
self.send_image(toplevel.clone());
}
let update = if is_new {
ToplevelUpdate::Add(info.clone())
} else {
ToplevelUpdate::Update(info.clone())
};
let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(update)));
} else {
self.remove_toplevel(toplevel);
}
}
fn remove_toplevel(&mut self, toplevel: &ExtForeignToplevelHandleV1) {
self.captured_toplevels.remove(toplevel);
let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(
ToplevelUpdate::Remove(toplevel.clone()),
)));
}
}
impl ToplevelInfoHandler for AppData {
@ -395,7 +364,23 @@ impl ToplevelInfoHandler for AppData {
_qh: &QueueHandle<Self>,
toplevel: &ExtForeignToplevelHandleV1,
) {
self.handle_toplevel(toplevel, true);
if let Some(info) = self.toplevel_info_state.info(toplevel) {
if info
.state
.contains(&zcosmic_toplevel_handle_v1::State::Minimized)
{
// spawn thread for sending the image
self.send_image(toplevel.clone());
let _ = futures::executor::block_on(
self.tx
.send(WaylandUpdate::Toplevel(ToplevelUpdate::Add(info.clone()))),
);
} else {
let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(
ToplevelUpdate::Remove(toplevel.clone()),
)));
}
}
}
fn update_toplevel(
@ -404,7 +389,21 @@ impl ToplevelInfoHandler for AppData {
_qh: &QueueHandle<Self>,
toplevel: &ExtForeignToplevelHandleV1,
) {
self.handle_toplevel(toplevel, false);
if let Some(info) = self.toplevel_info_state.info(toplevel) {
if info
.state
.contains(&zcosmic_toplevel_handle_v1::State::Minimized)
{
self.send_image(toplevel.clone());
let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(
ToplevelUpdate::Update(info.clone()),
)));
} else {
let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(
ToplevelUpdate::Remove(toplevel.clone()),
)));
}
}
}
fn toplevel_closed(
@ -413,7 +412,9 @@ impl ToplevelInfoHandler for AppData {
_qh: &QueueHandle<Self>,
toplevel: &ExtForeignToplevelHandleV1,
) {
self.remove_toplevel(toplevel);
let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(
ToplevelUpdate::Remove(toplevel.clone()),
)));
}
}
@ -481,7 +482,6 @@ pub(crate) fn wayland_handler(
toplevel_manager_state: ToplevelManagerState::new(&registry_state, &qh),
queue_handle: qh,
registry_state,
captured_toplevels: std::collections::HashSet::new(),
};
loop {

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-network"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-or-later"

View file

@ -1,8 +0,0 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy context="default">
<allow own_prefix="com.system76.CosmicSettings.Applet"/>
</policy>
</busconfig>

View file

@ -1,24 +0,0 @@
cancel = Têk bibe
connect = Girê bide
connected = Hat girêdan
network = Tor
airplane-mode = Awaya balafirê
airplane-mode-on = Awaya balafirê vekirî ye
turn-off-airplane-mode = Ji bo çalakirina Wi-Fi, Bluetooth û torên mobayîl, awaya balafirê bigire.
wifi = Wi-Fi
identity = Nasname
ipv4 = IPv4
ipv6 = IPv6
mac = MAC
megabits-per-second = Mbps
gigabits-per-second = Gbps
terabits-per-second = Tbps
connecting = Tê girêdan
settings = Sazkariyên torê...
visible-wireless-networks = Torên bêtêl ên xuya
vpn-connections = Girêdanên VPN
enter-password = Borînpeyvê yan jî kilîtê şîfrekirinê têxîne
router-wps-button = Tu dikarî bi pêlkirina bişkoka "WPS" ya li ser routerê jî girê bidî
unable-to-connect = Nikare bi torê ve were girêdan
check-wifi-connection = Piştrast bike ku Wi-Fi bi înternetê ve girêdayî ye û borînpeyv rast e
reset = Ji nû ve saz bike

View file

@ -35,7 +35,7 @@ use cosmic::{
widget::{
Id, button, column, container, divider,
icon::{self, from_name},
indeterminate_circular, row, scrollable, secure_input, text, text_input, toggler,
row, scrollable, secure_input, text, text_input, toggler,
},
};
use cosmic_dbus_networkmanager::interface::enums::{
@ -405,7 +405,6 @@ fn secret_agent_task(identifier: String) -> Task<NmAgentEvent> {
cosmic::Task::stream(async_fn_stream::fn_stream(move |emitter| async move {
let registration = SecretAgent::builder()
.with_identifier(identifier)
.with_object_path("/org/freedesktop/NetworkManager/SecretAgent")
.with_capabilities(SecretAgentCapabilities::VPN_HINTS)
.register()
.await;
@ -665,13 +664,6 @@ fn load_vpns(_conn: zbus::Connection) -> Task<crate::app::Message> {
let mut map: IndexMap<UUID, ConnectionSettings> = IndexMap::new();
for c in saved {
// Skip in-memory-only NM connections — assumed connections that NM
// auto-generated from externally-managed interfaces (e.g. one
// brought up by wg-quick@wg0.service) report unsaved=true and
// evaporate on deactivate, leaving the applet's toggle dead.
if c.unsaved {
continue;
}
let uuid: UUID = Arc::from(c.uuid.as_str());
let entry = match c.summary {
SettingsSummary::WireGuard { .. } => ConnectionSettings::Wireguard { id: c.id },
@ -734,10 +726,9 @@ impl cosmic::Application for CosmicNetworkApplet {
tasks.push(update_state(conn.clone()));
tasks.push(update_devices(conn.clone()));
tasks.push(load_vpns(conn));
let uuid = uuid::Uuid::new_v4().to_string().replace("-", "_");
let my_id = format!(
"com.system76.CosmicSettings.Applet._{uuid}.NetworkManager.SecretAgent",
"com.system76.CosmicSettings.Applet.{}.NetworkManager.SecretAgent",
uuid::Uuid::new_v4()
);
tasks.push(secret_agent_task(my_id).map(Message::SecretAgent));
}
@ -1516,7 +1507,12 @@ impl cosmic::Application for CosmicNetworkApplet {
];
match state {
ActiveConnectionState::Activating | ActiveConnectionState::Deactivating => {
btn_content.push(indeterminate_circular().size(24.0).into());
btn_content.push(
icon::from_name("process-working-symbolic")
.size(24)
.symbolic(true)
.into(),
);
}
ActiveConnectionState::Activated => btn_content.push(
text::body(fl!("connected"))
@ -1724,7 +1720,12 @@ impl cosmic::Application for CosmicNetworkApplet {
.into(),
);
btn_content.push(ssid.into());
btn_content.push(indeterminate_circular().size(24.0).into());
btn_content.push(
icon::from_name("process-working-symbolic")
.size(24)
.symbolic(true)
.into(),
);
} else if matches!(known.state, DeviceState::Unavailable) {
btn_content.push(
icon::from_name("network-wireless-disconnected-symbolic")
@ -1896,7 +1897,10 @@ impl cosmic::Application for CosmicNetworkApplet {
let connecting = padded_control(
row::with_children([
Element::from(id),
indeterminate_circular().size(24.0).into(),
icon::from_name("process-working-symbolic")
.size(24)
.symbolic(true)
.into(),
])
.spacing(8),
);

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-notifications"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,19 +0,0 @@
hours-ago =
{ $duration ->
[0] Niha
[one] 1 demjimêr berê
*[other] { $duration } demjimêr berê
}
minutes-ago =
{ $duration ->
[0] Niha
[one] 1 xulek berê
*[other] { $duration } xulek berê
}
show-less = Kêmtir nîşan bide
show-more = Bêtir { $more } nîşan bide
clear-group = Komê pak bike
clear-all = Hemû agahdariyan pak bike
do-not-disturb = Rawestandina balkêşiyê
notification-settings = Sazkariyên agahdariyê....
no-notifications = Agahdarî tune ne

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-power"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,36 +0,0 @@
cancel = Têk bibe
confirm = Bipejirîne
power = Hêz
restart = Ji nû ve bide destpêkirin
settings = Sazkarî...
shutdown = Vemrîne
suspend = Rawestîne
log-out = Derkeve
lock-screen = Dîmenderê kilît bike
lock-screen-shortcut = Super + Escape
log-out-shortcut = Super + Shift + Escape
confirm-button =
{ $action ->
[restart] { restart }
[suspend] { suspend }
[shutdown] Vemrîne
[log-out] { log-out }
*[other] { confirm }
}
confirm-title =
{ $action ->
[restart] { restart }
[suspend] { suspend }
[shutdown] { shutdown }
[log-out] Hemû sepanan bigire û derkeve
*[other] Çalakiyê hilbijartî bisepîne
} niha?
confirm-body =
Pergal wê xweber were { $action ->
[restart] jinûvedestpêkkirin
[suspend] rawestandin
[shutdown] vemirandin
[lock-screen] kilîtkirin
[log-out] derketin
*[other] çalakiyê hilbijartî bisepîne
}di { $countdown } çirke de.

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-status-area"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -9,14 +9,10 @@ use cosmic::{
},
cctk::sctk::reexports::calloop,
iced::{
self,
Event::Mouse,
Length, Subscription, event,
mouse::{self, ScrollDelta},
self, Length, Subscription,
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
window,
},
scroll::{DiscreteScrollDelta, DiscreteScrollState},
surface,
widget::{container, mouse_area},
};
@ -37,8 +33,7 @@ pub enum Msg {
StatusMenu((usize, status_menu::Msg)),
StatusNotifier(status_notifier_watcher::Event),
TogglePopup(usize),
Hovered(Option<usize>),
WheelScrolled(ScrollDelta),
Hovered(usize),
Surface(surface::Action),
ToggleOverflow,
HoveredOverflow,
@ -54,8 +49,6 @@ pub(crate) struct App {
max_menu_id: usize,
popup: Option<window::Id>,
overflow_popup: Option<window::Id>,
hovered_menu: Option<usize>,
scroll_states: BTreeMap<usize, DiscreteScrollState>,
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
}
@ -110,10 +103,9 @@ impl App {
let overflow_index = self.overflow_index().unwrap_or(0);
let children = self.menus.iter().skip(overflow_index).map(|(id, menu)| {
mouse_area(
menu_icon_button(&self.core.applet, menu).on_press_down(Msg::TogglePopup(*id)),
menu_icon_button(&self.core.applet, &menu).on_press_down(Msg::TogglePopup(*id)),
)
.on_enter(Msg::Hovered(Some(*id)))
.on_exit(Msg::Hovered(None))
.on_enter(Msg::Hovered(*id))
.into()
});
@ -218,7 +210,6 @@ impl cosmic::Application for App {
{
let id = *id;
self.menus.remove(&id);
self.scroll_states.remove(&id);
if self.open_menu == Some(id) {
self.open_menu = None;
if let Some(popup_id) = self.popup {
@ -321,10 +312,6 @@ impl cosmic::Application for App {
}
},
Msg::Hovered(id) => {
self.hovered_menu = id;
let Some(id) = id else {
return Task::none();
};
let mut cmds = Vec::new();
if let Some(old_id) = self.open_menu.take() {
if old_id != id {
@ -374,19 +361,6 @@ impl cosmic::Application for App {
cmds.push(get_popup(popup_settings));
Task::batch(cmds)
}
Msg::WheelScrolled(delta) => {
let Some(id) = self.hovered_menu else {
return Task::none();
};
let discrete_delta = self.scroll_states.entry(id).or_default().update(delta);
let Some((delta, orientation)) = discrete_scroll_delta(discrete_delta) else {
return Task::none();
};
let Some(menu) = self.menus.get(&id) else {
return Task::none();
};
scroll(id, menu.item.item_proxy().clone(), delta, orientation)
}
Msg::Surface(a) => {
return cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(a),
@ -431,7 +405,6 @@ impl cosmic::Application for App {
}
}
Msg::HoveredOverflow => {
self.hovered_menu = None;
let mut cmds = Vec::new();
if self.overflow_popup.is_some() {
// If we already have an overflow popup, do nothing
@ -487,12 +460,6 @@ impl cosmic::Application for App {
subscriptions.push(menu.subscription(is_open).with(*id).map(Msg::StatusMenu));
}
subscriptions.push(activation_token_subscription(0).map(Msg::Token));
subscriptions.push(event::listen_with(|e, status, _| match (e, status) {
(Mouse(mouse::Event::WheelScrolled { delta }), event::Status::Ignored) => {
Some(Msg::WheelScrolled(delta))
}
_ => None,
}));
iced::Subscription::batch(subscriptions)
}
@ -505,10 +472,9 @@ impl cosmic::Application for App {
.iter()
.take(overflow_index.unwrap_or(self.menus.len()))
.map(|(id, menu)| {
mouse_area(menu_icon_button(&self.core.applet, menu).on_press(Msg::Activate(*id)))
mouse_area(menu_icon_button(&self.core.applet, &menu).on_press(Msg::Activate(*id)))
.on_right_press(Msg::TogglePopup(*id))
.on_enter(Msg::Hovered(Some(*id)))
.on_exit(Msg::Hovered(None))
.on_enter(Msg::Hovered(*id))
.into()
});
@ -604,33 +570,6 @@ fn activate(
})
}
fn scroll(
id: usize,
item_proxy: crate::subscriptions::status_notifier_item::StatusNotifierItemProxy<'static>,
delta: i32,
orientation: &'static str,
) -> Task<cosmic::Action<Msg>> {
Task::future(async move {
match item_proxy.scroll(delta, orientation).await {
Ok(_) => cosmic::action::app(Msg::None),
Err(err) => {
tracing::error!("Scroll failed for {}: {}", id, err);
cosmic::action::app(Msg::None)
}
}
})
}
fn discrete_scroll_delta(delta: DiscreteScrollDelta) -> Option<(i32, &'static str)> {
if delta.y != 0 {
Some((delta.y as i32, "vertical"))
} else if delta.x != 0 {
Some((delta.x as i32, "horizontal"))
} else {
None
}
}
fn menu_icon_button<'a>(
applet: &'a cosmic::applet::Context,
menu: &'a status_menu::State,
@ -675,43 +614,3 @@ fn menu_icon_button<'a>(
pub fn main() -> iced::Result {
cosmic::applet::run::<App>(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn discrete_scroll_prefers_vertical_delta() {
assert_eq!(
discrete_scroll_delta(cosmic::scroll::DiscreteScrollDelta { x: 4, y: -2 }),
Some((-2, "vertical"))
);
}
#[test]
fn discrete_scroll_uses_horizontal_delta_when_vertical_is_zero() {
assert_eq!(
discrete_scroll_delta(cosmic::scroll::DiscreteScrollDelta { x: 3, y: 0 }),
Some((3, "horizontal"))
);
}
#[test]
fn discrete_scroll_ignores_zero_delta() {
assert_eq!(
discrete_scroll_delta(cosmic::scroll::DiscreteScrollDelta { x: 0, y: 0 }),
None
);
}
#[test]
fn pixel_scroll_accumulates_before_emitting_discrete_delta() {
let mut state = cosmic::scroll::DiscreteScrollState::default();
let first = state.update(ScrollDelta::Pixels { x: 0.0, y: 12.0 });
assert_eq!(discrete_scroll_delta(first), None);
let second = state.update(ScrollDelta::Pixels { x: 0.0, y: 12.0 });
assert_eq!(discrete_scroll_delta(second), Some((1, "vertical")));
}
}

View file

@ -202,8 +202,6 @@ pub trait StatusNotifierItem {
fn activate(&self, x: i32, y: i32) -> zbus::Result<()>;
fn secondary_activate(&self, x: i32, y: i32) -> zbus::Result<()>;
fn scroll(&self, delta: i32, orientation: &str) -> zbus::Result<()>;
}
#[derive(Clone, Debug)]

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-tiling"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,20 +0,0 @@
tile-windows = Çarçoveyan xweber berhev bike
tile-current = Qada xebatê heyî berhev bike
shortcuts = Kurterê
navigate-windows = Di nav çarçoveyan de bigere
move-window = Çarçoveyê bilivîne
toggle-floating-window = Rewşê çarçoveyê herikbar biguhêrîne
view-all-shortcuts = Hemû kurteriyan nîşan bide...
active-hint = Pêşniyara çalak
gaps = Valahî
floating-window-exceptions = Awarteyên çarçoveyê herikbar...
window-management-settings = Sazkariyên rêveberiya çarçoveyê...
all-workspaces = Hemû qadên xebatê
per-workspace = Li gorî qada xebatê
super = Super
shift = Shift
arrow-keys = tîr
tiled = Berhevkirî
floating = Herikbar
autotile-behavior = Li ser qadên xebatê çarçoveyan berhev bike
new-workspace = Tevgera qada xebatê ya nû

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-time"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1 +0,0 @@
datetime-settings = Sazkariyên dîrok, dem û salnameyê...

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applet-workspaces"
version = "1.0.15"
version = "1.0.2"
authors = ["Ashley Wulber <ashley@system76.com>"]
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1 +0,0 @@
cosmic-applet-workspaces = Qadên xebatê yên COSMIC

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applets-config"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
[dependencies]

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-applets"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-panel-button"
version = "1.0.15"
version = "1.0.2"
edition = "2024"
license = "GPL-3.0-only"

6
debian/changelog vendored
View file

@ -1,9 +1,3 @@
cosmic-applets (1.0.15) noble; urgency=medium
* Update changelog
-- Michael Murphy <mmstick@pop-os> Thu, 28 May 2026 19:33:02 +0200
cosmic-applets (1.0.12) noble; urgency=medium
* Update changelog

View file

@ -1 +0,0 @@
cosmic-applet-a11y = アクセシビリティ

View file

@ -1,51 +0,0 @@
cosmic-applet-audio = Deng
cosmic-app-list = Destgeha sepanan
cosmic-applet-a11y = Gihîştin
cosmic-applet-bluetooth = Bluetooth
cosmic-applet-bluetooth-comment = Amûrên Bluetooth bi rê ve bibe
cosmic-applet-bluetooth-keywords = COSMIC;Sepanok;Bluetooth;
cosmic-applet-input-sources = Çavkaniyên ketanê
cosmic-applet-input-sources-comment = Di navbera çavkaniyên ketanê de biguhêrîne
cosmic-applet-input-sources-keywords = COSMIC;Sepanok;Ketan;Çavkanî;
cosmic-app-list-comment = Sepanên darxistî veke û çarçoveyên vekirî bi rê ve bibe
cosmic-app-list-keywords = COSMIC;Sepanok;Sepan;Destgeh;Lîste;Erk;Bend;
cosmic-applet-a11y-comment = Sazkariyên gihîştinê ji destgehê saz bike
cosmic-applet-a11y-keywords = COSMIC;Sepanok;Gihîştin;Dîmender;A11y;Xwîner;Dîmender;Mezinker;Bijberî;Reng;
cosmic-applet-audio-comment = Hilbijartina amûra dengê, kontrola dengê, û kontrolên mediyaya MPRIS
cosmic-applet-audio-keywords = COSMIC;Sepanok;Deng;Deng;MPRIS;
cosmic-applet-battery = Hêz & Betarî
cosmic-applet-battery-comment = Awayên hêzê û vebijêrkên parastina hêzê
cosmic-applet-battery-keywords = COSMIC;Sepanok;Hêz;Betarî;
cosmic-applet-minimize = Çarçoveyên biçûk
cosmic-applet-minimize-comment = Çarçoveyên biçûk bi rê ve bibe
cosmic-applet-minimize-keywords = COSMIC;Sepanok;Biçûkkirin;
cosmic-applet-network = Tor
cosmic-applet-network-comment = Girêdanên torê bi rê ve bibe
cosmic-applet-network-keywords = COSMIC;Sepanok;Tor;
cosmic-applet-notifications = Navenda Agahdariyan
cosmic-applet-notifications-comment = Agahdarî û rawestandina balkişiyê bi rê ve bibe
cosmic-applet-notifications-keywords = COSMIC;Sepanok;Agahdarî;
cosmic-applet-power = Danaşîna bikarhêner
cosmic-applet-power-comment = Dîmenderê kilît bike, derkeve, rawestîne, ji nû ve bide destpêkirin, û vemrîne
cosmic-applet-power-keywords = COSMIC;Sepanok;Bikarhêner;Danaşîn;Kilîtkirin;Derketin;Destpêkirin;Vemirandin;Rawestandin;
cosmic-applet-status-area = Desgeha agahdariyan
cosmic-applet-status-area-comment = Nîşanderên sepanan ên ku dibe ku menuyan bo destgehê derxînin
cosmic-applet-status-area-keywords = COSMIC;Sepanok;Sepan;Nîşander;Agahdarî;Destgeh;Rewş;
cosmic-applet-tiling = Berhevkirin
cosmic-applet-tiling-comment = Berhevkirina xweber a çalak, ya niha û ya her qada xebatê bi rê ve bibe
cosmic-applet-tiling-keywords = COSMIC;Sepanok;Berhevkirin;Nîşander;Navenda xebatê;
cosmic-applet-time = Dîrok, dem & Salname
cosmic-applet-time-comment = Demê niha li ser destgehê bi salnameyeke vebûyî nîşan bide
cosmic-applet-time-keywords = COSMIC;Sepanok;Dîrok;Dem;Salname;
cosmic-applet-workspaces = Qada xebatê jimarkirî
cosmic-applet-workspaces-comment = Di navbera qada xebatê jimarkirî de di destgehê de biguherîne
cosmic-applet-workspaces-keywords = COSMIC;Sepanok;Qada xebatê;
cosmic-panel-app-button = Bişkoka pirtûkxaneya sepanan
cosmic-panel-app-button-comment = Pirtûkxaneya sepanan ji bo vekirina sepanên sazkirî veke
cosmic-panel-app-button-keywords = COSMIC;Sepanok;Sepan;Pirtûkxane;Destgeh;
cosmic-panel-launcher-button = Bişkoka destpêker
cosmic-panel-launcher-button-comment = Destpêkerê ji bo lêgerîna sepanan û xebitandina fermanan veke
cosmic-panel-launcher-button-keywords = COSMIC;Sepanok;Destpêker;Rêveber;
cosmic-panel-workspaces-button = Bişkoka qadên xebatê
cosmic-panel-workspaces-button-comment = Pêşdîtina qadên xebatê veke ji bo bi rê ve bibî û biguhêrînî
cosmic-panel-workspaces-button-keywords = COSMIC;Sepan;Qada xebatê;Pêşdîtin;

View file

@ -46,6 +46,3 @@ cosmic-panel-launcher-button-keywords = COSMIC;애플릿;런처;실행기;
cosmic-panel-workspaces-button = 작업 공간 버튼
cosmic-panel-workspaces-button-comment = 작업 공간 관리 및 전환을 위해 작업 공간 개요 열기
cosmic-panel-workspaces-button-keywords = COSMIC;애플릿;작업 공간;개요;
cosmic-applet-workspaces-comment = 패널에서 번호가 지정된 작업 공간 간 전환
cosmic-applet-tiling-comment = 활성 창 강조, 현재 및 작업 공간별 자동 타일링 관리
cosmic-applet-workspaces = 번호가 지정된 작업 공간

View file

@ -20,8 +20,3 @@ cosmic-applet-workspaces-comment = Alternar entre áreas de trabalho numeradas n
cosmic-applet-workspaces-keywords = COSMIC;Miniaplicação;Applet;Área;Trabalho;
cosmic-panel-workspaces-button-comment = Abrir a visão geral das áreas de trabalho para gerir e alternar entre elas
cosmic-panel-workspaces-button-keywords = COSMIC;Miniaplicação;Applet;Área;Trabalho;Visão;Geral;
cosmic-applet-a11y-comment = Configurar definições de acessibilidade a partir do painel
cosmic-applet-battery = Energia e Bateria
cosmic-app-list-keywords = COSMIC;Miniaplicação;Applet;Aplicação;Bandeja;Lista;Tarefa;Barra;
cosmic-applet-a11y-keywords = COSMIC;Miniaplicação;Applet;Acessibilidade;A11y;Ecrã;Leitor;Lupa;Ampliador;Contraste;Cor;
cosmic-applet-audio-keywords = COSMIC;Miniaplicação;Applet;Som;Áudio;MPRIS;

View file

@ -59,16 +59,13 @@ _install_status_notifier_watcher:
sed "s|@bindir@|{{ prefixdir }}|" cosmic-applet-status-area/data/com.system76.CosmicStatusNotifierWatcher.service.in > cosmic-applet-status-area/data/com.system76.CosmicStatusNotifierWatcher.service
install -Dm0644 cosmic-applet-status-area/data/com.system76.CosmicStatusNotifierWatcher.service {{ libdir }}/systemd/user/com.system76.CosmicStatusNotifierWatcher.service
_install_secret_agent_policy:
install -Dm0644 cosmic-applet-network/data/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf {{ sharedir }}/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf
# Installs files into the system
install: (_install_bin 'cosmic-applets') (_link_applet 'cosmic-panel-button') (_install_applet 'com.system76.CosmicAppList' 'cosmic-app-list') (_install_default_schema 'cosmic-app-list') (_install_applet 'com.system76.CosmicAppletA11y' 'cosmic-applet-a11y') (_install_applet 'com.system76.CosmicAppletAudio' 'cosmic-applet-audio') (_install_applet 'com.system76.CosmicAppletInputSources' 'cosmic-applet-input-sources') (_install_applet 'com.system76.CosmicAppletBattery' 'cosmic-applet-battery') (_install_applet 'com.system76.CosmicAppletBluetooth' 'cosmic-applet-bluetooth') (_install_applet 'com.system76.CosmicAppletMinimize' 'cosmic-applet-minimize') (_install_applet 'com.system76.CosmicAppletNetwork' 'cosmic-applet-network') (_install_applet 'com.system76.CosmicAppletNotifications' 'cosmic-applet-notifications') (_install_applet 'com.system76.CosmicAppletPower' 'cosmic-applet-power') (_install_applet 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') (_install_applet 'com.system76.CosmicAppletTiling' 'cosmic-applet-tiling') (_install_applet 'com.system76.CosmicAppletTime' 'cosmic-applet-time') (_install_applet 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') (_install_button 'com.system76.CosmicPanelAppButton' 'cosmic-panel-app-button') (_install_button 'com.system76.CosmicPanelLauncherButton' 'cosmic-panel-launcher-button') (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') _install_metainfo _install_status_notifier_watcher _install_secret_agent_policy
install: (_install_bin 'cosmic-applets') (_link_applet 'cosmic-panel-button') (_install_applet 'com.system76.CosmicAppList' 'cosmic-app-list') (_install_default_schema 'cosmic-app-list') (_install_applet 'com.system76.CosmicAppletA11y' 'cosmic-applet-a11y') (_install_applet 'com.system76.CosmicAppletAudio' 'cosmic-applet-audio') (_install_applet 'com.system76.CosmicAppletInputSources' 'cosmic-applet-input-sources') (_install_applet 'com.system76.CosmicAppletBattery' 'cosmic-applet-battery') (_install_applet 'com.system76.CosmicAppletBluetooth' 'cosmic-applet-bluetooth') (_install_applet 'com.system76.CosmicAppletMinimize' 'cosmic-applet-minimize') (_install_applet 'com.system76.CosmicAppletNetwork' 'cosmic-applet-network') (_install_applet 'com.system76.CosmicAppletNotifications' 'cosmic-applet-notifications') (_install_applet 'com.system76.CosmicAppletPower' 'cosmic-applet-power') (_install_applet 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') (_install_applet 'com.system76.CosmicAppletTiling' 'cosmic-applet-tiling') (_install_applet 'com.system76.CosmicAppletTime' 'cosmic-applet-time') (_install_applet 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') (_install_button 'com.system76.CosmicPanelAppButton' 'cosmic-panel-app-button') (_install_button 'com.system76.CosmicPanelLauncherButton' 'cosmic-panel-launcher-button') (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') _install_metainfo _install_status_notifier_watcher
# Vendor Cargo dependencies locally
vendor:
mkdir -p .cargo
cargo vendor --locked | head -n -1 > .cargo/config
cargo vendor | head -n -1 > .cargo/config
echo 'directory = "vendor"' >> .cargo/config
tar pcf vendor.tar vendor
rm -rf vendor