Merge remote-tracking branch 'upstream/master' into yoda-dock-magnification

# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	cosmic-app-list/src/app.rs
#	cosmic-applet-audio/src/lib.rs
#	cosmic-applet-input-sources/Cargo.toml
#	cosmic-applet-input-sources/src/lib.rs
#	cosmic-applet-status-area/src/components/app.rs
#	cosmic-panel-button/src/lib.rs
This commit is contained in:
Lionel DARNIS 2026-08-19 22:04:42 +02:00
commit 673547dc52
119 changed files with 2247 additions and 1190 deletions

1152
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -75,13 +75,9 @@ ignored = ["libcosmic"]
# cosmic-config = { path = "../libcosmic/cosmic-config" }
# libcosmic = { path = "../libcosmic" }
# iced_futures = { path = "../libcosmic/iced/futures" }
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//" }
# libcosmic = { git = "https://github.com/pop-os/libcosmic//" }
# iced_futures = { git = "https://github.com/pop-os/libcosmic//" }
# [patch."https://github.com/pop-os/winit.git"]
# winit = { git = "https://github.com/rust-windowing/winit.git", rev = "241b7a80bba96c91fa3901729cd5dec66abb9be4" }
# winit = { path = "../winit" }
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "theme-v2" }
# libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "theme-v2" }
# iced_futures = { git = "https://github.com/pop-os/libcosmic//", branch = "theme-v2" }
[patch."https://github.com/pop-os/libcosmic"]
cosmic-config = { path = "/home/lionel/Projets/COSMIC/libcosmic/cosmic-config" }

View file

@ -1,6 +1,6 @@
cosmic-app-list = دُرج التطبيقات
pin = ثبِّت في درج التطبيقات
quit = أنهِ
quit = إنهِ
quit-all = أنهِ الكل
new-window = نافذة جديدة
run = شغِّل

View file

@ -1,5 +1,5 @@
cosmic-app-list = Panel aplikací
pin = Připnout do panelu aplikací
cosmic-app-list = Oblast aplikací
pin = Připnout do oblasti aplikací
quit = Ukončit
quit-all = Ukončit vše
new-window = Nové okno

View file

@ -0,0 +1,8 @@
run = Kør
run-on = Kør på { $gpu }
run-on-default = (Standard)
cosmic-app-list = Programbakke
new-window = Nyt Vindue
quit = Afslut
quit-all = Forlad Alle
pin = Fastgør til app-skuffe

View file

@ -1,5 +1,5 @@
new-window = Νέο παράθυρο
quit = Έξοδος
quit = Τερματισμός
cosmic-app-list = Περιοχή εφαρμογών
run = Εκτέλεση
run-on = Εκτέλεση με { $gpu }

View file

@ -0,0 +1,8 @@
cosmic-app-list = App Tray
new-window = New Window
quit = Quit
run = Run
run-on = Run on { $gpu }
quit-all = Quit All
run-on-default = (Default)
pin = Pin to app tray

View file

@ -1,8 +1,8 @@
cosmic-app-list = سینی برنامه‌ها
pin = سنجاق کردن به سینی برنامه‌ها
quit = بستن
quit-all = بستن همه
cosmic-app-list = نوار برنامه‌ها
pin = افزودن به نوار برنامه‌ها
quit = خروج
quit-all = خروج از همه
new-window = پنجره جدید
run = اجرا
run-on = اجرا روی {$gpu}
run-on-default = (پیش‌فرض)
run-on = اجرا با { $gpu }
run-on-default = (پیش‌فرض)

View file

@ -0,0 +1,8 @@
quit = Pechar
run = Executar
cosmic-app-list = Bandexa de aplicacións
pin = Fixar na bandexa de aplicacións
quit-all = Quitar todo
new-window = Nova Xanela
run-on = Executar en { $gpu }
run-on-default = (Por defecto)

View file

@ -1,8 +1,8 @@
cosmic-app-list = ऐप ट्रे
pin = ऐप ट्रे में पिन करें
quit = बंद करें
quit = बाहर जाएँ
quit-all = सभी बंद करें
new-window = नई विंडो
run = चलाएँ
run-on = {$gpu} पर चलाएँ
run-on = { $gpu } पर चलाएँ
run-on-default = (डिफ़ॉल्ट)

View file

@ -1,8 +1,8 @@
cosmic-app-list = Barra applicazioni
cosmic-app-list = Area applicazioni
pin = Fissa nella barra delle applicazioni
quit = Esci
quit-all = Chiudi tutto
new-window = Nuova finestra
run = Esegui
run-on = Esegui usando la GPU: { $gpu }
run-on-default = (default)
run-on = Esegui su { $gpu }
run-on-default = (Predefinito)

View file

@ -1,5 +1,5 @@
new-window = Naujas Langas
quit = Išeiti
quit = Išjungti
run = Paleisti
run-on = Paleisti naudojantis { $gpu }
run-on-default = (Numatytas)

View file

@ -0,0 +1 @@
cosmic-app-list = Cinta d’aplicacions

View file

@ -1,4 +1,4 @@
quit = ਬਾਹਰ
quit = ਸਮਾਪਤ
cosmic-app-list = ਐਪ ਟਰੇ
pin = ਐਪ ਟਰੇ ਵਿੱਚ ਟੰਗੋ
quit-all = ਸਭ ਵਿੱਚੋਂ ਬਾਹਰ

View file

@ -1,6 +1,6 @@
cosmic-app-list = Zasobnik aplikacji
pin = Przypnij do zasobnika aplikacji
quit = Zamknij
quit = Zakończ
quit-all = Zamknij wszystkie
new-window = Nowe okno
run = Uruchom

View file

@ -1,6 +1,6 @@
cosmic-app-list = Bandeja de aplicativos
pin = Fixar na bandeja de aplicativos
quit = Sair
quit = Encerrar
quit-all = Sair de Todos
new-window = Nova Janela
run = Executar

View file

@ -1,6 +1,6 @@
cosmic-app-list = Область приложений
pin = Закрепить на панели приложений
quit = Выйти
quit = Завершить
quit-all = Завершить все
new-window = Новое окно
run = Выполнить

View file

@ -0,0 +1,2 @@
run-on = { $gpu } ᱨᱮ ᱫᱟᱹᱲ ᱟᱭ
run-on-default = (ᱢᱩᱞ)

View file

@ -1,6 +1,6 @@
cosmic-app-list = Панель застосунків
pin = Закріпити
quit = Вийти
quit = Завершити
quit-all = Закрити всі
new-window = Нове вікно
run = Виконати

View file

@ -1,8 +1,8 @@
cosmic-app-list = 應用程式匣盤
pin = 釘選至應用程式匣盤
quit = 退出
cosmic-app-list = 應用程式托盤
pin = 釘選至應用程式托盤
quit = 結束
quit-all = 全部退出
new-window = 新建視窗
new-window = 新增視窗
run = 執行
run-on = 在 { $gpu } 上執行
run-on-default = (預設)

View file

@ -28,25 +28,28 @@ use cosmic::{
},
cosmic_config::{Config, CosmicConfigEntry},
desktop::IconSourceExt,
iced::runtime::{core::event, dnd::peek_dnd},
iced::{
self, Alignment, Background, Border, Length, Limits, Padding, Subscription,
advanced::text::{Ellipsize, EllipsizeHeightLimit},
clipboard::mime::{AllowedMimeTypes, AsMimeTypes},
event::listen_with,
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
runtime::{core::event, dnd::peek_dnd, platform_specific::wayland::CornerRadius},
widget::{
Column, Row, column, mouse_area, row, rule::vertical as vertical_rule,
space::horizontal as horizontal_space, space::vertical as vertical_space, stack,
Column, Row, column, mouse_area, row,
rule::vertical as vertical_rule,
space::{horizontal as horizontal_space, vertical as vertical_space},
stack,
},
window,
},
surface,
surface::{self, action::LiveSettings},
theme::{self, Button, Container},
widget::{
DndDestination, Image, button, container, divider, dnd_source, grid,
icon::{self, from_name},
image::Handle,
menu,
rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription},
scrollable, svg, text, text_input,
},
@ -1249,44 +1252,54 @@ impl cosmic::Application for CosmicAppList {
.iter()
.chain(self.pinned_list.iter())
.find(|t| t.id == id)
.cloned()
{
let Some(rectangle) = self.rectangles.get(&toplevel_group.id.into()) else {
let Some(rectangle) = self.rectangles.get(&toplevel_group.id.into()).copied()
else {
tracing::error!("No rectangle found for toplevel group");
return Task::none();
};
let corners = self.core.system_theme().cosmic().corner_radii.radius_s;
let popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
move |_| LiveSettings::default(),
move |app: &mut Self| {
let new_id = window::Id::unique();
app.popup = Some(Popup {
parent: parent_window_id,
id: new_id,
dock_item: toplevel_group.clone(),
popup_type: PopupType::RightClickMenu,
});
let new_id = window::Id::unique();
self.popup = Some(Popup {
parent: parent_window_id,
id: new_id,
dock_item: toplevel_group.clone(),
popup_type: PopupType::RightClickMenu,
});
let mut popup_settings = self.core.applet.get_popup_settings(
parent_window_id,
new_id,
None,
None,
None,
);
let iced::Rectangle {
x,
y,
width,
height,
} = *rectangle;
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: x as i32,
y: y as i32,
width: width as i32,
height: height as i32,
};
let mut popup_settings = app.core.applet.get_popup_settings(
parent_window_id,
new_id,
None,
None,
None,
);
let iced::Rectangle {
x,
y,
width,
height,
} = rectangle;
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: x as i32,
y: y as i32,
width: width as i32,
height: height as i32,
};
popup_settings
},
None,
));
let gpu_update = Task::perform(try_get_gpus(), |gpus| {
cosmic::Action::App(Message::GpuRequest(gpus))
});
return Task::batch([gpu_update, get_popup(popup_settings)]);
return Task::batch([gpu_update, popup_task]);
}
}
Message::ToplevelListPopup(id, parent_window_id) => {
@ -1317,10 +1330,10 @@ impl cosmic::Application for CosmicAppList {
}
}
let Some(rectangle) = self.rectangles.get(&toplevel_group.id.into()) else {
let Some(rectangle) = self.rectangles.get(&toplevel_group.id.into()).copied()
else {
return Task::none();
};
let new_id = window::Id::unique();
self.popup = Some(Popup {
parent: parent_window_id,
@ -1328,48 +1341,56 @@ impl cosmic::Application for CosmicAppList {
dock_item: toplevel_group.clone(),
popup_type: PopupType::ToplevelList,
});
let popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let mut popup_settings = app.core.applet.get_popup_settings(
parent_window_id,
new_id,
None,
None,
None,
);
let iced::Rectangle {
x,
y,
width,
height,
} = rectangle;
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: x as i32,
y: y as i32,
width: width as i32,
height: height as i32,
};
let max_windows = 7.0;
let window_spacing = 8.0;
popup_settings.positioner.size_limits = match app.core.applet.anchor
{
PanelAnchor::Right | PanelAnchor::Left => Limits::NONE
.min_width(100.0)
.min_height(30.0)
.max_width(window_spacing * 2.0 + TOPLEVEL_BUTTON_WIDTH)
.max_height(
TOPLEVEL_BUTTON_HEIGHT * max_windows
+ window_spacing * (max_windows + 1.0),
),
PanelAnchor::Bottom | PanelAnchor::Top => Limits::NONE
.min_width(30.0)
.min_height(100.0)
.max_width(
TOPLEVEL_BUTTON_WIDTH * max_windows
+ window_spacing * (max_windows + 1.0),
)
.max_height(window_spacing * 2.0 + TOPLEVEL_BUTTON_HEIGHT),
};
popup_settings
},
None,
));
let mut popup_settings = self.core.applet.get_popup_settings(
parent_window_id,
new_id,
None,
None,
None,
);
let iced::Rectangle {
x,
y,
width,
height,
} = *rectangle;
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: x as i32,
y: y as i32,
width: width as i32,
height: height as i32,
};
let max_windows = 7.0;
let window_spacing = 8.0;
popup_settings.positioner.size_limits = match self.core.applet.anchor {
PanelAnchor::Right | PanelAnchor::Left => Limits::NONE
.min_width(100.0)
.min_height(30.0)
.max_width(window_spacing * 2.0 + TOPLEVEL_BUTTON_WIDTH)
.max_height(
TOPLEVEL_BUTTON_HEIGHT * max_windows
+ window_spacing * (max_windows + 1.0),
),
PanelAnchor::Bottom | PanelAnchor::Top => Limits::NONE
.min_width(30.0)
.min_height(100.0)
.max_width(
TOPLEVEL_BUTTON_WIDTH * max_windows
+ window_spacing * (max_windows + 1.0),
)
.max_height(window_spacing * 2.0 + TOPLEVEL_BUTTON_HEIGHT),
};
return get_popup(popup_settings);
return popup_task;
}
}
Message::ToplevelHoverChanged(handle, entering) => {
@ -2148,52 +2169,63 @@ impl cosmic::Application for CosmicAppList {
if create_new {
let new_id = window::Id::unique();
self.overflow_active_popup = Some(new_id);
let rectangle = self.rectangles.get(&DockItemId::ActiveOverflow);
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
if let Some(iced::Rectangle {
let Some(iced::Rectangle {
x,
y,
width,
height,
}) = rectangle
{
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: *x as i32,
y: *y as i32,
width: *width as i32,
height: *height as i32,
};
}
let applet_suggested_size = self.core.applet.suggested_size(false).0
+ 2 * self.core.applet.suggested_padding(false).0;
let (_favorite_popup_cutoff, active_popup_cutoff) =
self.panel_overflow_lengths();
let popup_applet_count =
self.active_list.len().saturating_sub(
(active_popup_cutoff.unwrap_or_default()).saturating_sub(1),
) as f32;
let popup_applet_size = applet_suggested_size as f32 * popup_applet_count
+ 4.0 * (popup_applet_count - 1.);
let (max_width, max_height) = match self.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => {
(popup_applet_size, applet_suggested_size as f32)
}
PanelAnchor::Left | PanelAnchor::Right => {
(applet_suggested_size as f32, popup_applet_size)
}
}) = self.rectangles.get(&DockItemId::ActiveOverflow).copied()
else {
return Task::none();
};
popup_settings.positioner.size_limits = Limits::NONE
.max_width(max_width)
.min_width(1.)
.max_height(max_height)
.min_height(1.);
cmds.push(get_popup(popup_settings));
let popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: x as i32,
y: y as i32,
width: width as i32,
height: height as i32,
};
let applet_suggested_size = app.core.applet.suggested_size(false).0
+ 2 * app.core.applet.suggested_padding(false).0;
let (_favorite_popup_cutoff, active_popup_cutoff) =
app.panel_overflow_lengths();
let popup_applet_count = app.active_list.len().saturating_sub(
(active_popup_cutoff.unwrap_or_default()).saturating_sub(1),
) as f32;
let popup_applet_size = applet_suggested_size as f32
* popup_applet_count
+ 4.0 * (popup_applet_count - 1.);
let (max_width, max_height) = match app.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => {
(popup_applet_size, applet_suggested_size as f32)
}
PanelAnchor::Left | PanelAnchor::Right => {
(applet_suggested_size as f32, popup_applet_size)
}
};
popup_settings.positioner.size_limits = Limits::NONE
.max_width(max_width)
.min_width(1.)
.max_height(max_height)
.min_height(1.);
popup_settings
},
None,
));
cmds.push(popup_task);
}
return Task::batch(cmds);
}
@ -2205,52 +2237,63 @@ impl cosmic::Application for CosmicAppList {
if create_new {
let new_id = window::Id::unique();
self.overflow_favorites_popup = Some(new_id);
let rectangle = self.rectangles.get(&DockItemId::FavoritesOverflow);
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
if let Some(iced::Rectangle {
let Some(iced::Rectangle {
x,
y,
width,
height,
}) = rectangle
{
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: *x as i32,
y: *y as i32,
width: *width as i32,
height: *height as i32,
};
}
let applet_suggested_size = self.core.applet.suggested_size(false).0
+ 2 * self.core.applet.suggested_padding(false).0;
let (favorite_popup_cutoff, _active_popup_cutoff) =
self.panel_overflow_lengths();
let popup_applet_count =
self.pinned_list.len().saturating_sub(
favorite_popup_cutoff.unwrap_or_default().saturating_sub(1),
) as f32;
let popup_applet_size = applet_suggested_size as f32 * popup_applet_count
+ 4.0 * (popup_applet_count - 1.);
let (max_width, max_height) = match self.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => {
(popup_applet_size, applet_suggested_size as f32)
}
PanelAnchor::Left | PanelAnchor::Right => {
(applet_suggested_size as f32, popup_applet_size)
}
}) = self.rectangles.get(&DockItemId::FavoritesOverflow).copied()
else {
return Task::none();
};
popup_settings.positioner.size_limits = Limits::NONE
.max_width(max_width)
.min_width(1.)
.max_height(max_height)
.min_height(1.);
cmds.push(get_popup(popup_settings));
let popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings.positioner.anchor_rect = iced::Rectangle::<i32> {
x: x as i32,
y: y as i32,
width: width as i32,
height: height as i32,
};
let applet_suggested_size = app.core.applet.suggested_size(false).0
+ 2 * app.core.applet.suggested_padding(false).0;
let (favorite_popup_cutoff, _active_popup_cutoff) =
app.panel_overflow_lengths();
let popup_applet_count = app.pinned_list.len().saturating_sub(
favorite_popup_cutoff.unwrap_or_default().saturating_sub(1),
) as f32;
let popup_applet_size = applet_suggested_size as f32
* popup_applet_count
+ 4.0 * (popup_applet_count - 1.);
let (max_width, max_height) = match app.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => {
(popup_applet_size, applet_suggested_size as f32)
}
PanelAnchor::Left | PanelAnchor::Right => {
(applet_suggested_size as f32, popup_applet_size)
}
};
popup_settings.positioner.size_limits = Limits::NONE
.max_width(max_width)
.min_width(1.)
.max_height(max_height)
.min_height(1.);
popup_settings
},
None,
));
cmds.push(popup_task);
}
return Task::batch(cmds);
}
@ -2660,7 +2703,8 @@ impl cosmic::Application for CosmicAppList {
.width(Length::Fill)
}
let mut content = column![].align_x(Alignment::Center);
let mut content =
menu::menu_column::MenuColumn::with_capacity(4).align_x(Alignment::Center);
if let Some(exec) = desktop_info.exec() {
if !toplevels.is_empty() {
@ -2784,22 +2828,6 @@ impl cosmic::Application for CosmicAppList {
.popup_container(
container(content)
.padding(1)
//TODO: move style to libcosmic
.class(theme::Container::custom(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background(false).component;
container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())),
border: Border {
radius: cosmic.radius_s().into(),
width: 1.0,
color: component.divider.into(),
},
..Default::default()
}
}))
.height(Length::Shrink)
.width(Length::Fill),
)

View file

@ -0,0 +1,6 @@
magnifier = Lupa
screen-reader = Narrador
invert-colors = Colors invertits
settings = Configuració d'accessibilitat...
filter-colors = Filtres de colors
high-contrast = Mode d'alt contrast

View file

@ -0,0 +1,6 @@
screen-reader = Skærmlæser
invert-colors = Invertér farver
high-contrast = Høj kontrast
filter-colors = Filtrér farver
settings = Tilgængelighedsindstillinger...
magnifier = Forstørrelsesglas

View file

@ -1,6 +1,6 @@
screen-reader = Bildschirmleser
magnifier = Vergrößerungsglas
invert-colors = Farben invertieren
settings = Barrierefreiheitseinstellungen…
settings = Barrierefreiheitseinstellungen ...
filter-colors = Farben filtern
high-contrast = Hoher Kontrast

View file

@ -0,0 +1,6 @@
screen-reader = Screen reader
invert-colors = Invert colours
high-contrast = High contrast
filter-colors = Filter colours
settings = Accessibility settings...
magnifier = Magnifier

View file

@ -1,6 +1,6 @@
screen-reader = صفحه‌خوان
magnifier = ذره‌بین
invert-colors = وارونه‌سازی رنگ‌ها
invert-colors = وارونه کردن رنگ‌ها
settings = تنظیمات دسترسی‌پذیری...
filter-colors = فیلتر رنگ‌ها
high-contrast = کنتراست بالا

View file

@ -0,0 +1,6 @@
screen-reader = Lector de pantalla
magnifier = Lupa
invert-colors = Invertir cores
settings = Configuración de accesibilidade...
filter-colors = Filtro de cores
high-contrast = Alto contraste

View file

@ -1,4 +1,6 @@
screen-reader = स्क्रीन रीडर
filter-colors = रंग फ़िल्टर
settings = सुलभता सेटिंग..।
magnifier = आवर्धक
filter-colors = रंग फ़िल्टर करें
settings = एक्सेसिबिलिटी सेटिंग्स..।
magnifier = मैग्नीफायर
high-contrast = उच्च कंट्रास्ट
invert-colors = रंग उल्टे करें

View file

@ -2,5 +2,5 @@ screen-reader = Lettore dello schermo
magnifier = Lente di ingrandimento
invert-colors = Inverti colori
settings = Impostazioni accessibilità...
filter-colors = Filtra colori
filter-colors = Filtri colore
high-contrast = Contrasto elevato

View file

@ -0,0 +1,2 @@
screen-reader = ᱥᱠᱨᱤᱱ ᱨᱤᱰᱟᱨ
invert-colors = ᱨᱚᱝ ᱩᱞᱴᱟᱹ ᱪᱷᱚ

View file

@ -133,20 +133,27 @@ impl cosmic::Application for CosmicA11yApplet {
}
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
return destroy_popup(p);
return cosmic::surface::surface_task(cosmic::surface::action::destroy_popup(
p,
));
} else {
let new_id = window::Id::unique();
self.popup.replace(new_id);
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut CosmicA11yApplet| {
let new_id = window::Id::unique();
app.popup.replace(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
Some((1, 1)),
let popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
Some((1, 1)),
None,
None,
);
popup_settings
},
None,
None,
);
return get_popup(popup_settings);
));
}
}
Message::CloseRequested(id) => {

View file

@ -0,0 +1,7 @@
output = Sortida
show-media-controls = Mostra els controls multimèdia a la barra
disconnected = PulseAudio desconnectat
no-device = No s'ha seleccionat cap dispositiu
input = Entrada
unknown-artist = Desconegut
sound-settings = Configuració de so...

View file

@ -0,0 +1,7 @@
show-media-controls = Vis mediekontrol på panel
disconnected = PulseAudio Afbrudt
no-device = Ingen enhed valgt
input = Indgang
output = Udgang
unknown-artist = Ukendt
sound-settings = Lydindstillinger...

View file

@ -1,7 +1,7 @@
output = Ausgabe
input = Eingabe
show-media-controls = Mediensteuerung im Panel anzeigen
sound-settings = Klang-Einstellungen...
sound-settings = Klang-Einstellungen ...
disconnected = PulseAudio getrennt
no-device = Kein Gerät ausgewählt
unknown-artist = Unbekannt

View file

@ -0,0 +1,7 @@
show-media-controls = Show media controls on panel
disconnected = PulseAudio Disconnected
no-device = No device selected
input = Input
output = Output
unknown-artist = Unknown
sound-settings = Sound Settings...

View file

@ -0,0 +1,7 @@
output = Saída
input = Entrada
show-media-controls = Mostrar os controis multimedia no panel
sound-settings = Axustes do son...
disconnected = PulseAudio Desconectado
no-device = Ningún dipositivo seleccionado
unknown-artist = Descoñecido

View file

@ -4,4 +4,4 @@ show-media-controls = Mostra controlli multimediali nel pannello in alto
sound-settings = Impostazioni audio...
disconnected = PulseAudio Disconnesso
no-device = Nessun dispositivo selezionato
unknown-artist = Sconosciuto
unknown-artist = Sconosciuto

View file

@ -4,4 +4,4 @@ no-device = Nepasirinktas įrenginys
input = Įvestis
output = Išvestis
unknown-artist = Nežinomas atlikėjas
sound-settings = Garso Nustatymai...
sound-settings = Garso Nuostatos...

View file

@ -0,0 +1 @@
output = ᱟᱨᱡᱟᱣ

View file

@ -33,10 +33,15 @@ use cosmic::{
};
use cosmic_settings_audio_client::{self as audio_client, CosmicAudioProxy};
use futures::SinkExt;
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};
use std::{
sync::{
Arc,
atomic::{self, AtomicU32},
},
time::Duration,
};
mod config;
mod mpris_subscription;
@ -58,11 +63,15 @@ pub struct Audio {
/// 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>>>,
audio_client: Option<audio_client::Client>,
/// Known audio device state
model: model::Model,
/// Whether to expand the revealer of a source or sink device.
is_open: IsOpen,
/// Used to debounce sink volume updates.
applied_sink_volume: Arc<AtomicU32>,
/// Used to deboune source volume updates.
applied_source_volume: Arc<AtomicU32>,
/// Max slider volume for the sink device, as determined by the amplification property.
max_sink_volume: u32,
/// Max slider volume for the source device, as determined by the amplification property.
@ -140,26 +149,27 @@ enum IsOpen {
pub enum Message {
/// Connection to `com.system76.CosmicSettings`.
Client(Arc<audio_client::Client>),
Ignore,
SetSinkVolume(u32),
SetSourceVolume(u32),
ToggleSinkMute,
ToggleSourceMute,
SetDefaultSink(usize),
SetDefaultSource(usize),
OutputToggle,
InputToggle,
TogglePopup,
CloseRequested(window::Id),
ToggleMediaControlsInTopPanel(bool),
ConfigChanged(AudioAppletConfig),
Ignore,
InputToggle,
Mpris(mpris_subscription::MprisUpdate),
MprisRequest(MprisRequest),
Token(TokenUpdate),
OpenSettings,
OutputToggle,
ReattachClient(Arc<audio_client::Client>),
Rectangle(RectangleUpdate<u32>),
SetDefaultSink(usize),
SetDefaultSource(usize),
SetSinkVolume(u32),
SetSourceVolume(u32),
Subscription(audio_client::Event),
Surface(surface::Action),
Rectangle(RectangleUpdate<u32>),
ToggleMediaControlsInTopPanel(bool),
TogglePopup,
ToggleSinkMute,
ToggleSourceMute,
Token(TokenUpdate),
}
// TODO
@ -302,32 +312,45 @@ impl cosmic::Application for Audio {
self.rectangle_tracker.replace(tracker);
}
},
Message::ReattachClient(client) => {
if let Some(client) = Arc::into_inner(client) {
self.audio_client = Some(client);
}
}
Message::Ignore => {}
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
return destroy_popup(p);
return cosmic::surface::surface_task(cosmic::surface::action::destroy_popup(
p,
));
} else {
let new_id = window::Id::unique();
self.popup.replace(new_id);
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Self| {
let new_id = window::Id::unique();
app.popup.replace(new_id);
self.refresh_volume_limits();
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
if let Some(r) = app.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,
};
}
popup_settings
},
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);
));
}
}
@ -355,7 +378,7 @@ impl cosmic::Application for Audio {
&& let Some(client) = self.audio_client.as_mut()
{
futures::executor::block_on(async {
_ = client.borrow_mut().conn.set_default(node_id, true).await;
_ = client.conn.set_default(node_id, true).await;
});
}
}
@ -366,7 +389,7 @@ impl cosmic::Application for Audio {
&& let Some(client) = self.audio_client.as_mut()
{
futures::executor::block_on(async {
_ = client.borrow_mut().conn.set_default(node_id, true).await;
_ = client.conn.set_default(node_id, true).await;
});
}
}
@ -374,7 +397,7 @@ impl cosmic::Application for Audio {
Message::ToggleSinkMute => {
if let Some(ref mut client) = self.audio_client {
futures::executor::block_on(async {
_ = client.borrow_mut().conn.sink_mute_toggle().await;
_ = client.conn.sink_mute_toggle().await;
});
}
}
@ -382,27 +405,43 @@ impl cosmic::Application for Audio {
Message::ToggleSourceMute => {
if let Some(ref mut client) = self.audio_client {
futures::executor::block_on(async {
_ = client.borrow_mut().conn.source_mute_toggle().await;
_ = client.conn.source_mute_toggle().await;
});
}
}
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;
self.applied_sink_volume
.store(volume, atomic::Ordering::Relaxed);
self.model.active_sink.volume = volume;
self.model.active_sink.volume_text = volume.to_string();
if let Some(mut client) = self.audio_client.take() {
let volume = Arc::clone(&self.applied_sink_volume);
return cosmic::Task::future(async move {
tokio::time::sleep(Duration::from_millis(128)).await;
_ = client
.conn
.set_sink_volume(volume.load(atomic::Ordering::Relaxed))
.await;
Message::ReattachClient(Arc::new(client)).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;
self.applied_source_volume
.store(volume, atomic::Ordering::Relaxed);
self.model.active_source.volume = volume;
self.model.active_source.volume_text = volume.to_string();
if let Some(mut client) = self.audio_client.take() {
let volume = Arc::clone(&self.applied_source_volume);
return cosmic::Task::future(async move {
tokio::time::sleep(Duration::from_millis(128)).await;
_ = client
.conn
.set_sink_volume(volume.load(atomic::Ordering::Relaxed))
.await;
Message::ReattachClient(Arc::new(client)).into()
});
}
}
@ -513,7 +552,7 @@ impl cosmic::Application for Audio {
}
Message::Client(client) => {
if let Some(client) = Arc::into_inner(client) {
self.audio_client = Some(Rc::new(RefCell::new(client)));
self.audio_client = Some(client);
self.model = model::Model::default();
}
}

View file

@ -0,0 +1,14 @@
power-settings = Strøm- og Batteriindstillinger...
hours = t
until-empty = indtil tom
battery-desc = Reduceret strømforbrug og ydeevne.
max-charge = Forøg dit batteris levetid, ved at angive en maksimal opladningsværdi på 80%
balanced = Balanceret
battery = Batteri
seconds = s
dgpu-running = Diskret GPU er aktiv og kan reducere batterilevetiden
performance = Høj Ydeevne
performance-desc = Høj ydeevne og strømforbrug.
minutes = m
balanced-desc = Standard ydeevne og batteriforbrug.
dgpu-applications = Applikationer som bruger { $gpu_name } diskret GPU

View file

@ -9,6 +9,6 @@ seconds = s
minutes = m
hours = h
until-empty = bis leer
power-settings = Energie- und Akkueinstellungen...
power-settings = Energie- und Akkueinstellungen ...
dgpu-running = Dedizierte GPU ist aktiv und kann die Akkulaufzeit reduzieren
dgpu-applications = Anwendungen, welche die dedizierte GPU { $gpu_name } nutzen

View file

@ -0,0 +1,14 @@
power-settings = Power & Battery settings...
hours = h
until-empty = until empty
battery-desc = Reduced power usage and performance.
max-charge = Increase the lifespan of your battery by setting a maximum charge value of 80%
balanced = Balanced
battery = Battery
seconds = s
dgpu-running = Discrete GPU is active and can reduce battery life
performance = High Performance
performance-desc = High performance and power usage.
minutes = m
balanced-desc = Standard performance and battery usage.
dgpu-applications = Applications using { $gpu_name } discrete GPU

View file

@ -0,0 +1,14 @@
battery = Batería
battery-desc = Reduce o consumo de enerxía e o rendemento.
balanced = Balanceado
balanced-desc = Rendemento e consumo de batería estándar.
performance = Alto rendemento
performance-desc = Alto rendemento e maior consumo de enerxía.
max-charge = Aumenta a vida útil da batería limitando a carga máxima ao 80 %
seconds = s
minutes = m
hours = h
until-empty = ata baleirar
power-settings = Axustes de enexía e batería...
dgpu-running = A GPU dedicada está activa e pode reducir a duración da batería
dgpu-applications = Aplicacións que están a usar a GPU dedicada { $gpu_name }

View file

@ -1,14 +1,14 @@
battery = Risparmio energetico
battery-desc = Consumi minimi, performance ridotta.
balanced = Bilanciato
balanced-desc = Consumi bilanciati, performance dinamica.
performance = Performance
performance-desc = Consumi alti, performance massima.
max-charge = Migliora la durata di vita della batteria caricandola fino all'80%
battery = Batteria
battery-desc = Prestazioni e consumo energetico ridotti.
balanced = Equilibrato
balanced-desc = Prestazioni e consumo energetico normali.
performance = Prestazioni elevate
performance-desc = Prestazioni elevate e consumo energetico.
max-charge = Aumenta la durata della batteria impostando un valore massimo di carica dell’80%
seconds = s
minutes = m
hours = h
until-empty = fino alla scarica
until-empty = finché non è vuoto
power-settings = Impostazioni energetiche...
dgpu-running = La GPU discreta è attiva e può ridurre la durata della batteria
dgpu-applications = Applicazioni che stanno usando la GPU discreta { $gpu_name }

View file

@ -2,7 +2,7 @@ battery = Betarî
seconds = ç
minutes = x
hours = d
until-empty = Heya vala dibe
until-empty = heya vala dibe
power-settings = Sazkariyên hêz û betariyê...
battery-desc = Bikaranîna hêzê kêmkirî û performans.
balanced = Hevseng

View file

@ -1,14 +1,14 @@
battery = Bateria
battery = Economia de energia
battery-desc = Uso reduzido de energia e desempenho limitado.
balanced = Balanceado
balanced-desc = Desempenho padrão e uso de bateria moderado.
performance = Alto Desempenho
performance = Alto desempenho
performance-desc = Máximo desempenho e alto uso de energia.
max-charge = Aumente a vida útil da sua bateria definindo um valor máximo de carga de 80%
seconds = s
minutes = m
hours = h
until-empty = até esvaziar
until-empty = até ficar descarregada
power-settings = Configurações de energia e bateria...
dgpu-running = GPU discreta está ativa e pode reduzir a vida útil da bateria
dgpu-applications = Aplicativos usando GPU discreta { $gpu_name }
dgpu-running = A GPU dedicada está ativa e pode reduzir a vida útil da bateria
dgpu-applications = Aplicativos usando a GPU dedicada { $gpu_name }

View file

@ -6,9 +6,9 @@ performance = 高效能
performance-desc = 高效能與高耗電量。
max-charge = 將電池充電上限設定為 80%,以延長電池使用壽命
seconds = 秒
minutes = 分
hours = 時
minutes = 分鍾
hours = 小時
until-empty = 直到電量耗盡
power-settings = 電源與電池設定…
dgpu-running = 獨立顯示卡已啟用,可能會縮短電池續航力
dgpu-applications = 使用 { $gpu_name } 獨立顯示卡的應用程式
dgpu-applications = 使用獨立顯示卡 { $gpu_name } 的應用程式

View file

@ -366,23 +366,31 @@ impl cosmic::Application for CosmicBatteryApplet {
let _ = tx.send(KeyboardBacklightRequest::Get);
}
let new_id = window::Id::unique();
self.popup.replace(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
Some((1, 1)),
None,
None,
);
if let Some(tx) = self.power_profile_sender.as_ref() {
let _ = tx.send(PowerProfileRequest::Get);
}
if let Some(tx) = self.update_trigger.as_ref() {
let _ = tx.send(());
}
let mut tasks = vec![get_popup(popup_settings)];
let mut tasks = vec![cosmic::surface::surface_task(
cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Self| {
let new_id = window::Id::unique();
app.popup.replace(new_id);
let popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
Some((1, 1)),
None,
None,
);
popup_settings
},
None,
),
)];
if let Some(tx) = &self.settings_daemon_sender {
let _ = tx.send(settings_daemon::Request::GetDisplayBrightness);
let _ = tx.send(settings_daemon::Request::GetMaxDisplayBrightness);

View file

@ -0,0 +1,12 @@
cancel = Annuller
confirm-pin = Bekræft venligst, at følgende pinkode matcher den, der vises på { $deviceName }
bluetooth = Bluetooth
unsuccessful = Parring Mislykkedes
pairable = Parbare
try-again = Prøv Igen
check-device = Sørg for, at { $deviceName } er slået til, inden for rækkevidde og klar til parring.
other-devices = Andre Bluetooth-enheder
connected = Forbundet
confirm = Bekræft
settings = Bluetooth-indstillinger...
discoverable = Synlig

View file

@ -1,6 +1,6 @@
bluetooth = Bluetooth
other-devices = Andere Bluetooth-Geräte
settings = Bluetooth-Einstellungen...
settings = Bluetooth-Einstellungen ...
connected = Verbunden
confirm-pin = Bitte bestätige, dass die folgende PIN mit der auf { $deviceName } angezeigten PIN übereinstimmt
confirm = Bestätigen

View file

@ -0,0 +1,12 @@
cancel = Cancel
confirm-pin = Please confirm that the following PIN matches the one displayed on { $deviceName }
bluetooth = Bluetooth
unsuccessful = Pairing Unsuccessful
pairable = Pairable
try-again = Try Again
check-device = Make sure { $deviceName } is turned on, in range, and is ready to pair.
other-devices = Other Bluetooth devices
connected = Connected
confirm = Confirm
settings = Bluetooth settings...
discoverable = Discoverable

View file

@ -2,11 +2,11 @@ bluetooth = بلوتوث
other-devices = سایر دستگاه‌های بلوتوث
settings = تنظیمات بلوتوث...
connected = متصل
confirm-pin = لطفاً تأیید کنید که پین زیر با پین نمایش داده شده در {$deviceName} یکسان است.
confirm-pin = لطفاً تأیید کنید که پین زیر با پین نمایش داده شده در { $deviceName } یکسان است.
confirm = تأیید
cancel = لغو
unsuccessful = جفت‌سازی ناموفق بود
check-device = مطمئن شوید که {$deviceName} روشن، در محدوده و آماده جفت‌سازی است.
check-device = مطمئن شوید که { $deviceName } روشن، در محدوده و آماده جفت‌سازی است.
try-again = تلاش دوباره
discoverable = قابل شناسایی
pairable = قابل جفت‌سازی

View file

@ -0,0 +1,12 @@
cancel = Cancelar
bluetooth = Bluetooth
other-devices = Outros dispositivos Bluetooth
settings = Axustes Bluetooth…
connected = Conectado
confirm-pin = Por favor, confirme que o seguinte PIN coincide co que se mostra en{ $deviceName }
confirm = Confirmar
unsuccessful = Enparellamento non exitoso
check-device = Asegurate de que { $deviceName } está acendido, dentro do alcance e listo para emparellar.
try-again = Reintentar
discoverable = Atopable
pairable = Emparellable

View file

@ -2,11 +2,11 @@ bluetooth = ब्लूटूथ
other-devices = अन्य ब्लूटूथ उपकरण
settings = ब्लूटूथ सेटिंग्स...
connected = जुड़े हुए
confirm-pin = कृपया सुनिश्चित करें कि निम्नलिखित PIN {$deviceName} पर प्रदर्शित PIN से मेल खाता है
confirm-pin = कृपया सुनिश्चित करें कि निम्नलिखित PIN { $deviceName } पर प्रदर्शित PIN से मेल खाता है
confirm = पुष्टि करें
cancel = रद्द करें
unsuccessful = पेयरिंग असफल
check-device = सुनिश्चित करें कि {$deviceName} चालू है, रेंज में है, और पेयर करने के लिए तैयार है।
check-device = सुनिश्चित करें कि { $deviceName } चालू है, रेंज में है, और पेयर करने के लिए तैयार है।
try-again = फिर से प्रयास करें
discoverable = खोजने योग्य
pairable = पेयर करने योग्य

View file

@ -2,11 +2,11 @@ bluetooth = Bluetooth
other-devices = Altri dispositivi Bluetooth
settings = Impostazioni Bluetooth...
connected = Connesso
confirm-pin = Per favore conferma che il seguente PIN corrisponda a quello visualizzato nel dispositivo {$deviceName}
confirm-pin = Per favore conferma che il seguente PIN corrisponda a quello visualizzato nel dispositivo { $deviceName }
confirm = Conferma
cancel = Annulla
unsuccessful = Accoppiamento fallito
check-device = Assicurati che il dispositivo {$deviceName} sia acceso, a portata e pronto per l'accoppiamento.
check-device = Assicurati che il dispositivo { $deviceName } sia acceso, a portata e pronto per l'accoppiamento.
try-again = Riprova
discoverable = Visibile
pairable = Accoppiabile
pairable = Accoppiabile

View file

@ -0,0 +1 @@
cancel = ᱵᱟᱹᱛᱤᱞ

View file

@ -1,7 +1,7 @@
bluetooth = 藍牙
other-devices = 其他藍牙裝置
settings = 藍牙設定…
connected = 已經連線
connected = 已連線
confirm-pin = 請確認此 PIN 碼與 { $deviceName } 上顯示的是否相符
confirm = 確認
cancel = 取消

View file

@ -121,20 +121,25 @@ impl cosmic::Application for CosmicBluetoothApplet {
} else {
set_discovery(true);
// TODO request update of state maybe
let new_id = window::Id::unique();
self.popup.replace(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
let get_popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let new_id = window::Id::unique();
app.popup.replace(new_id);
app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
)
},
None,
));
return Task::batch([
get_popup(popup_settings),
get_popup_task,
cosmic::task::future(set_tick(Duration::from_secs(3)))
.map(|()| cosmic::Action::App(Message::Ignore)),
]);

View file

@ -5,7 +5,9 @@ edition = "2024"
license = "GPL-3.0-only"
[dependencies]
cctk.workspace = true
cosmic-comp-config.workspace = true
futures.workspace = true
i18n-embed-fl.workspace = true
i18n-embed.workspace = true
cosmic.workspace = true

View file

@ -12,4 +12,5 @@ NoDisplay=true
X-CosmicApplet=true
X-CosmicShrinkable=true
X-CosmicHoverPopup=Auto
X-HostWaylandDisplay=true
X-OverflowPriority=10

View file

@ -0,0 +1,2 @@
show-keyboard-layout = Mostra la disposició de teclat...
keyboard-settings = Configuració de teclat...

View file

@ -0,0 +1,2 @@
show-keyboard-layout = Vis Tastaturopsætning...
keyboard-settings = Tastaturindstillinger...

View file

@ -1,2 +1,2 @@
show-keyboard-layout = Tastaturbelegung anzeigen...
keyboard-settings = Tastatureinstellungen...
show-keyboard-layout = Tastaturbelegung anzeigen ...
keyboard-settings = Tastatureinstellungen ...

View file

@ -0,0 +1,2 @@
show-keyboard-layout = Mostrar a disposición do teclado...
keyboard-settings = Axustes do teclado...

View file

@ -3,19 +3,23 @@
mod localize;
use cctk::{
cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1,
wayland_client::{Connection, Proxy, backend::Backend},
};
use cosmic::{
app,
app::Core,
applet::{self},
cosmic_config::{self, ConfigSet, CosmicConfigEntry},
cosmic_theme::Spacing,
iced::Subscription,
iced::core::window,
iced::{
Rectangle, Task,
self, Rectangle, Task,
event::wayland::{Event as WaylandEvent, OutputEvent},
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
window::Id,
},
iced::core::window,
prelude::*,
surface, theme,
widget::{
@ -23,14 +27,35 @@ use cosmic::{
rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription},
},
};
use cosmic_comp_config::CosmicCompConfig;
use std::sync::LazyLock;
use cosmic_comp_config::{CosmicCompConfig, XkbConfig};
use std::{
os::unix::{
io::{FromRawFd, RawFd},
net::UnixStream,
},
sync::LazyLock,
};
use xkb_data::KeyboardLayout;
mod wayland;
static AUTOSIZE_MAIN_ID: LazyLock<widget::Id> = LazyLock::new(|| widget::Id::new("autosize-main"));
pub const ID: &str = "com.system76.CosmicAppletInputSources";
pub fn run() -> cosmic::iced::Result {
let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET")
.ok()
.and_then(|fd| {
fd.parse::<RawFd>()
.ok()
.map(|fd| unsafe { UnixStream::from_raw_fd(fd) })
});
let wayland_connection = if let Some(socket) = socket {
Some(Connection::from_socket(socket).unwrap())
} else {
None
};
localize::localize();
let layouts = match xkb_data::all_keyboard_layouts() {
@ -41,27 +66,8 @@ pub fn run() -> cosmic::iced::Result {
}
};
let (comp_config_handler, comp_config) =
match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) {
Ok(config_handler) => {
let config = match CosmicCompConfig::get_entry(&config_handler) {
Ok(ok) => ok,
Err((errs, config)) => {
tracing::error!("errors loading config: {:?}", errs);
config
}
};
(Some(config_handler), config)
}
Err(err) => {
tracing::error!("failed to create config handler: {}", err);
(None, CosmicCompConfig::default())
}
};
cosmic::applet::run::<Window>(Flags {
comp_config,
comp_config_handler,
wayland_connection,
layouts: layouts.layout_list.layout,
})
}
@ -76,12 +82,13 @@ pub struct ActiveLayout {
pub struct Window {
core: Core,
popup: Option<Id>,
comp_config: CosmicCompConfig,
comp_config_handler: Option<cosmic_config::Config>,
layouts: Vec<KeyboardLayout>,
active_layouts: Vec<ActiveLayout>,
rectangle_tracker: Option<RectangleTracker<u32>>,
rectangle: Rectangle,
wayland_connection: Option<Connection>,
keyboard_layout: Option<ZcosmicKeyboardLayoutV1>,
current_layout: usize,
}
#[derive(Clone, Debug)]
@ -93,12 +100,13 @@ pub enum Message {
KeyboardSettings,
Surface(surface::Action),
Rectangle(RectangleUpdate<u32>),
WaylandConnection(Backend),
Wayland(wayland::Event),
}
#[derive(Debug)]
pub struct Flags {
pub comp_config: CosmicCompConfig,
pub comp_config_handler: Option<cosmic_config::Config>,
wayland_connection: Option<Connection>,
pub layouts: Vec<KeyboardLayout>,
}
@ -119,14 +127,15 @@ impl cosmic::Application for Window {
fn init(core: Core, flags: Self::Flags) -> (Self, app::Task<Self::Message>) {
let window = Window {
comp_config_handler: flags.comp_config_handler,
layouts: flags.layouts,
core,
popup: None,
comp_config: flags.comp_config,
active_layouts: Vec::new(),
rectangle_tracker: None,
rectangle: Rectangle::default(),
wayland_connection: flags.wayland_connection,
keyboard_layout: None,
current_layout: 0,
};
(window, Task::none())
}
@ -139,19 +148,24 @@ impl cosmic::Application for Window {
match message {
Message::TogglePopup => {
return if let Some(p) = self.popup.take() {
destroy_popup(p)
return crate::surface::surface_task(crate::surface::action::destroy_popup(p));
} else {
let new_id = Id::unique();
self.popup.replace(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
return crate::surface::surface_task(crate::surface::action::app_popup(
|_| Default::default(),
|app: &mut Window| {
let new_id = Id::unique();
app.popup.replace(new_id);
let popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings
},
None,
None,
None,
);
get_popup(popup_settings)
));
};
}
Message::PopupClosed(id) => {
@ -160,8 +174,7 @@ impl cosmic::Application for Window {
}
}
Message::CompConfig(config) => {
self.comp_config = *config;
self.active_layouts = self.update_xkb();
self.active_layouts = self.update_xkb(&config.xkb_config);
}
Message::KeyboardSettings => {
let mut cmd = std::process::Command::new("cosmic-settings");
@ -169,31 +182,10 @@ impl cosmic::Application for Window {
tokio::spawn(cosmic::process::spawn(cmd));
}
Message::SetActiveLayout(pos) => {
if pos == 0 {
return Task::none();
}
self.active_layouts.swap(0, pos);
let mut new_layout = String::new();
let mut new_variant = String::new();
for layout in &self.active_layouts {
new_layout.push_str(&layout.layout);
new_layout.push(',');
new_variant.push_str(&layout.variant);
new_variant.push(',');
}
let _excess_comma = new_layout.pop();
let _excess_comma = new_variant.pop();
self.comp_config.xkb_config.layout = new_layout;
self.comp_config.xkb_config.variant = new_variant;
if let Some(comp_config_handler) = &self.comp_config_handler {
if let Err(err) =
comp_config_handler.set("xkb_config", &self.comp_config.xkb_config)
{
tracing::error!("Failed to set config 'xkb_config' {err}");
if let Some(keyboard_layout) = &self.keyboard_layout {
keyboard_layout.set_group(pos as u32);
if let Some(backend) = keyboard_layout.backend().upgrade() {
let _ = backend.flush();
}
}
}
@ -210,13 +202,26 @@ impl cosmic::Application for Window {
self.rectangle_tracker = Some(tracker);
}
},
Message::WaylandConnection(backend) => {
if self.wayland_connection.is_none() {
self.wayland_connection = Some(Connection::from_backend(backend));
}
}
Message::Wayland(w) => match w {
wayland::Event::KeyboardLayout(keyboard_layout) => {
self.keyboard_layout = Some(keyboard_layout);
}
wayland::Event::Group(group) => {
self.current_layout = group as usize;
}
},
}
Task::none()
}
fn view(&self) -> Element<'_, Self::Message> {
let applet_text = if let Some(l) = self.active_layouts.first() {
let applet_text = if let Some(l) = self.active_layouts.get(self.current_layout) {
if !l.variant.is_empty() {
format!("{} ({})", l.layout, l.variant)
} else {
@ -249,8 +254,13 @@ impl cosmic::Application for Window {
let mut content_list =
widget::column::with_capacity(4 + self.active_layouts.len()).padding([8, 0]);
for (id, layout) in self.active_layouts.iter().enumerate() {
let font = if id == self.current_layout {
cosmic::font::bold()
} else {
cosmic::font::default()
};
let group = widget::column::with_capacity(2)
.push(widget::text::body(layout.description.as_str()))
.push(widget::text::body(layout.description.as_str()).font(font))
.push(widget::text::caption(layout.layout.as_str()));
content_list = content_list
.push(applet::menu_button(group).on_press(Message::SetActiveLayout(id)));
@ -271,7 +281,7 @@ impl cosmic::Application for Window {
}
fn subscription(&self) -> Subscription<Self::Message> {
Subscription::batch(vec![
let mut subscriptions = vec![
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
self.core
.watch_config("com.system76.CosmicComp")
@ -285,7 +295,23 @@ impl cosmic::Application for Window {
}
Message::CompConfig(Box::new(update.config))
}),
])
];
subscriptions.push(if let Some(connection) = &self.wayland_connection {
wayland::subscription(connection.clone()).map(Message::Wayland)
} else {
iced::event::listen_with(|evt, _, _| match evt {
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(evt)) => {
if let WaylandEvent::Output(OutputEvent::Created(_), output) = evt {
if let Some(backend) = output.backend().upgrade() {
return Some(Message::WaylandConnection(backend));
}
}
None
}
_ => None,
})
});
Subscription::batch(subscriptions)
}
fn style(&self) -> Option<cosmic::iced::theme::Style> {
@ -293,9 +319,8 @@ impl cosmic::Application for Window {
}
}
impl Window {
fn update_xkb(&self) -> Vec<ActiveLayout> {
fn update_xkb(&self, xkb: &XkbConfig) -> Vec<ActiveLayout> {
let mut active_layouts = Vec::new();
let xkb = &self.comp_config.xkb_config;
let layouts = xkb.layout.split_terminator(',');

View file

@ -0,0 +1,157 @@
use cctk::{
cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1,
keyboard_layout::{KeyboardLayoutHandler, KeyboardLayoutState},
sctk::{
self,
registry::{ProvidesRegistryState, RegistryState},
seat::{Capability, SeatHandler, SeatState},
},
wayland_client::{
Connection, QueueHandle, delegate_noop,
globals::registry_queue_init,
protocol::{wl_keyboard, wl_seat},
},
};
use cosmic::iced;
use futures::{SinkExt, channel::mpsc, executor::block_on};
use std::{hash::Hash, thread};
#[derive(Clone, Debug)]
pub enum Event {
KeyboardLayout(ZcosmicKeyboardLayoutV1),
Group(usize),
}
pub fn subscription(connection: Connection) -> iced::Subscription<Event> {
#[derive(Clone)]
struct WaylandSubscription(Connection);
impl Hash for WaylandSubscription {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.backend().display_id().hash(state);
}
}
iced::Subscription::run_with(
WaylandSubscription(connection),
|WaylandSubscription(connection)| {
let connection = connection.clone();
iced::stream::channel(8, move |sender| async move {
thread::spawn(move || thread(connection, sender));
})
},
)
}
struct Keyboard {
seat: wl_seat::WlSeat,
keyboard_layout: ZcosmicKeyboardLayoutV1,
}
impl Drop for Keyboard {
fn drop(&mut self) {
self.keyboard_layout.destroy();
}
}
struct AppData {
seat_state: SeatState,
registry_state: RegistryState,
keyboard_layout_state: KeyboardLayoutState,
keyboard: Option<Keyboard>,
sender: mpsc::Sender<Event>,
}
impl KeyboardLayoutHandler for AppData {
fn group(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &wl_keyboard::WlKeyboard,
_keyboard_layout: &ZcosmicKeyboardLayoutV1,
group: u32,
) {
let _ = block_on(self.sender.send(Event::Group(group as usize)));
}
}
impl ProvidesRegistryState for AppData {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
sctk::registry_handlers![SeatState,];
}
impl SeatHandler for AppData {
fn seat_state(&mut self) -> &mut SeatState {
&mut self.seat_state
}
fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
fn new_capability(
&mut self,
_conn: &Connection,
qh: &QueueHandle<Self>,
seat: wl_seat::WlSeat,
capability: Capability,
) {
if capability == Capability::Keyboard && self.keyboard.is_none() {
let keyboard = seat.get_keyboard(qh, ());
let keyboard_layout = self
.keyboard_layout_state
.get_keyboard_layout(&keyboard, qh);
keyboard.release();
if let Some(keyboard_layout) = keyboard_layout {
let _ = block_on(
self.sender
.send(Event::KeyboardLayout(keyboard_layout.clone())),
);
self.keyboard = Some(Keyboard {
seat,
keyboard_layout,
});
} else {
keyboard.release();
}
}
}
fn remove_capability(
&mut self,
_conn: &Connection,
_: &QueueHandle<Self>,
seat: wl_seat::WlSeat,
_capability: Capability,
) {
self.keyboard.take_if(|x| x.seat == seat);
}
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
self.keyboard.take_if(|x| x.seat == seat);
}
}
fn thread(conn: Connection, sender: mpsc::Sender<Event>) {
let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
let qh = event_queue.handle();
let registry_state = RegistryState::new(&globals);
let seat_state = SeatState::new(&globals, &qh);
let keyboard_layout_state = KeyboardLayoutState::new(&registry_state, &qh);
let mut app_data = AppData {
seat_state,
registry_state,
keyboard_layout_state,
keyboard: None,
sender,
};
loop {
event_queue.blocking_dispatch(&mut app_data).unwrap();
}
}
sctk::delegate_registry!(AppData);
sctk::delegate_seat!(AppData);
cctk::delegate_keyboard_layout!(AppData);
delegate_noop!(AppData: ignore wl_keyboard::WlKeyboard);

View file

@ -248,40 +248,48 @@ impl cosmic::Application for Minimize {
}
Message::OpenOverflowPopup => {
if let Some(id) = self.overflow_popup.take() {
return destroy_popup(id);
return cosmic::surface::surface_task(cosmic::surface::action::destroy_popup(
id,
));
} else {
let new_id = window::Id::unique();
let pos = self.max_icon_count().unwrap_or_default();
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Self| {
let new_id = window::Id::unique();
let pos = app.max_icon_count().unwrap_or_default();
self.overflow_popup = Some(new_id);
let icon_size = self.core.applet.suggested_size(true).0 as u32
+ 2 * self.core.applet.suggested_padding(true).1 as u32;
let spacing = self.core.system_theme().cosmic().space_xxs() as u32;
let major_axis_len = (icon_size + spacing) * (pos.saturating_sub(1) as u32);
let rectangle = match self.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => iced::Rectangle {
x: major_axis_len as i32,
y: 0,
width: icon_size as i32,
height: icon_size as i32,
app.overflow_popup = Some(new_id);
let icon_size = app.core.applet.suggested_size(true).0 as u32
+ 2 * app.core.applet.suggested_padding(true).1 as u32;
let spacing = app.core.system_theme().cosmic().space_xxs() as u32;
let major_axis_len =
(icon_size + spacing) * (pos.saturating_sub(1) as u32);
let rectangle = match app.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => iced::Rectangle {
x: major_axis_len as i32,
y: 0,
width: icon_size as i32,
height: icon_size as i32,
},
PanelAnchor::Left | PanelAnchor::Right => iced::Rectangle {
x: 0,
y: major_axis_len as i32,
width: icon_size as i32,
height: icon_size as i32,
},
};
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings.positioner.anchor_rect = rectangle;
popup_settings
},
PanelAnchor::Left | PanelAnchor::Right => iced::Rectangle {
x: 0,
y: major_axis_len as i32,
width: icon_size as i32,
height: icon_size as i32,
},
};
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings.positioner.anchor_rect = rectangle;
return get_popup(popup_settings);
));
}
}
Message::CloseOverflowPopup => todo!(),

View file

@ -0,0 +1,6 @@
cancel = Annuller
connected = Forbundet
connect = Opret forbindelse
wifi = Wi-Fi
identity = Identitet
network = Netværk

View file

@ -12,7 +12,7 @@ connected = Verbunden
connecting = Wird verbunden
connect = Verbinden
cancel = Abbrechen
settings = Netzwerkeinstellungen...
settings = Netzwerkeinstellungen ...
visible-wireless-networks = Sichtbare Drahtlosnetzwerke
enter-password = Gib das Passwort oder den Verschlüsselungsschlüssel ein
router-wps-button = Du kannst dich auch verbinden, indem du die Taste „WPS” am Router drückst

View file

@ -0,0 +1,2 @@
cancel = Cancel
connected = Connected

View file

@ -9,7 +9,7 @@ mac = MAC
megabits-per-second = Mbps
connected = Connecté
connecting = Connexion
connect = Connecter
connect = Se connecter
cancel = Annuler
settings = Paramètres réseaux...
visible-wireless-networks = Réseaux Wi-Fi disponibles

View file

@ -0,0 +1,24 @@
cancel = Cancelar
connected = Conectado
connect = Conectar
identity = Identidade
wifi = Wi-Fi
network = Rede
airplane-mode = Modo avión
airplane-mode-on = O modo avión está activo
turn-off-airplane-mode = Desactívao para activar a wifi, o Bluetooth e a banda larga móbil.
ipv4 = Dirección IPv4
ipv6 = Dirección IPv6
mac = MAC
megabits-per-second = Mbps
gigabits-per-second = Gbps
terabits-per-second = Tbps
connecting = Conectando
settings = Axustes da rede...
visible-wireless-networks = Redes sen fíos visibles
vpn-connections = Conexións VPN
enter-password = Introduce o contrasinal ou a chave de cifrado
router-wps-button = Tamén podes conectarte premendo o botón "WPS" do router
unable-to-connect = Non se puido conectar á rede
check-wifi-connection = Asegúrate de que a wifi está conectada a Internet e de que o contrasinal é correcto
reset = Restablecer

View file

@ -0,0 +1 @@
cancel = ᱵᱟᱹᱛᱤᱞ

View file

@ -8,7 +8,7 @@ ipv4 = IPv4 位址
ipv6 = IPv6 位址
mac = MAC
megabits-per-second = Mbps
connected = 已經連線
connected = 已連線
connecting = 連線中
connect = 連線
cancel = 取消

View file

@ -15,5 +15,5 @@ show-more = { $more } weitere anzeigen
clear-group = Gruppe leeren
clear-all = Alle Benachrichtigungen leeren
do-not-disturb = Nicht stören
notification-settings = Benachrichtigungseinstellungen...
notification-settings = Benachrichtigungseinstellungen ...
no-notifications = Keine Benachrichtigungen

View file

@ -0,0 +1,19 @@
hours-ago =
{ $duration ->
[0] Xusto agora
[one] Hai 1 hora
*[other] Hai { $duration } horas
}
minutes-ago =
{ $duration ->
[0] Xusto agora
[one] Hai 1 minuto
*[other] Hai { $duration } minutos
}
show-less = Mostrar menos
show-more = Mostrar { $more } máis
clear-group = Limpar grupo
clear-all = Limpar todas as notificacións
do-not-disturb = Non Molestar
notification-settings = Configuración das notificacións...
no-notifications = Sen notificacións

View file

@ -157,18 +157,23 @@ impl cosmic::Application for Notifications {
if let Some(p) = self.popup.take() {
return destroy_popup(p);
} else {
let new_id = window::Id::unique();
self.popup.replace(new_id);
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Self| {
let new_id = window::Id::unique();
app.popup.replace(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
let popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings
},
None,
None,
None,
);
return get_popup(popup_settings);
));
}
}
Message::DoNotDisturb(b) => {

View file

@ -2,3 +2,35 @@ cancel = Cancel·lar
confirm = Confirmar
restart = Reinicia
suspend = Suspèn
confirm-button =
{ $action ->
[restart] { restart }
[suspend] { suspend }
[shutdown] Apaga
[log-out] { log-out }
*[other] { confirm }
}
power = Energia
confirm-body =
{ $action ->
[restart] L'ordinador es reiniciarà
[suspend] L'ordinador se suspendrà
[shutdown] L'ordinador s'apagarà
[lock-screen] La pantalla es bloquejarà
[log-out] La sessió es tancarà
*[other] L'acció seleccionada es durà a terme
} automàticament d'aquí a { $countdown } segons.
lock-screen = Bloqueja la pantalla
shutdown = Apaga
log-out = Tanca la sessió
log-out-shortcut = Súper + Majús + Esc
settings = Configuració...
lock-screen-shortcut = Súper + Esc
confirm-title =
¿{ $action ->
[restart] { restart }
[suspend] { suspend }
[shutdown] { shutdown }
[log-out] Voleu tancar totes les aplicacions i tancar la sessió
*[other] Voleu dur a terme l'acció seleccionada
}?

View file

@ -1,5 +1,5 @@
power = Energie
settings = Einstellungen...
settings = Einstellungen ...
lock-screen = Sperrbildschirm
lock-screen-shortcut = Super + Esc
log-out = Abmelden

View file

@ -0,0 +1,36 @@
power = Enerxía
settings = Axustes...
lock-screen = Pantalla de Bloqueo
lock-screen-shortcut = Super + Esc
log-out = Pechar Sesión
log-out-shortcut = Super + Maiús + Esc
suspend = Suspender
restart = Reiniciar
shutdown = Apagar
confirm = Confirmar
cancel = Cancelar
confirm-button =
{ $action ->
[restart] { restart }
[suspend] { suspend }
[shutdown] Apagar
[log-out] { log-out }
*[other] { confirm }
}
confirm-title =
{ $action ->
[restart] { restart } agora?
[suspend] { suspend } agora?
[shutdown] { shutdown } agora?
[log-out] Pechar todas as aplicacións e pechar a sesión agora?
*[other] Aplicar a acción seleccionada agora
}
confirm-body =
O sistema { $action ->
[restart] reiniciarase
[suspend] suspenderase
[shutdown] apagarase
[lock-screen] bloqueará a pantalla
[log-out] pechará a sesión
*[other] aplicará a acción seleccionada
} automaticamente en { $countdown } segundos.

View file

@ -115,20 +115,27 @@ impl cosmic::Application for Power {
match message {
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
return destroy_popup(p);
return cosmic::surface::surface_task(cosmic::surface::action::destroy_popup(
p,
));
} else {
let new_id = window::Id::unique();
self.popup.replace(new_id);
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Power| {
let new_id = window::Id::unique();
app.popup.replace(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
let popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
popup_settings
},
None,
None,
None,
);
return get_popup(popup_settings);
));
}
}
Message::OpenSettings => {

View file

@ -246,38 +246,49 @@ impl cosmic::Application for App {
if let Some(popup_id) = self.popup.take() {
cmds.push(destroy_popup(popup_id));
}
let popup_id = self.next_popup_id();
let i = self.menus.keys().position(|&i| i == id).unwrap();
let (i, parent) = self
.overflow_index()
.and_then(|overflow_i| {
if overflow_i <= i {
Some(i - overflow_i).zip(self.overflow_popup)
} else {
Some((i, self.core.main_window_id().unwrap()))
}
})
.unwrap_or((i, self.core.main_window_id().unwrap()));
let mut popup_settings = self
.core
.applet
.get_popup_settings(parent, popup_id, None, None, None);
self.popup = Some(popup_id);
let popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let popup_id = app.next_popup_id();
let i = app.menus.keys().position(|&i| i == id).unwrap();
let (i, parent) = app
.overflow_index()
.and_then(|overflow_i| {
if overflow_i <= i {
Some(i - overflow_i).zip(app.overflow_popup)
} else {
Some((i, app.core.main_window_id().unwrap()))
}
})
.unwrap_or((i, app.core.main_window_id().unwrap()));
if matches!(
self.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = self.core.applet.suggested_size(false).1
+ 2 * self.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.y = i as i32 * suggested_size as i32;
} else {
let suggested_size = self.core.applet.suggested_size(false).0
+ 2 * self.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
}
cmds.push(get_popup(popup_settings));
let mut popup_settings = app
.core
.applet
.get_popup_settings(parent, popup_id, None, None, None);
app.popup = Some(popup_id);
if matches!(
app.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = app.core.applet.suggested_size(false).1
+ 2 * app.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.y =
i as i32 * suggested_size as i32;
} else {
let suggested_size = app.core.applet.suggested_size(false).0
+ 2 * app.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.x =
i as i32 * suggested_size as i32;
}
popup_settings
},
None,
));
cmds.push(popup_task);
return app::Task::batch(cmds);
} else if let Some(popup_id) = self.popup {
self.menus[&id].closed();
@ -339,39 +350,49 @@ impl cosmic::Application for App {
} else {
return Task::none();
}
let popup_id = self.next_popup_id();
let i = self.menus.keys().position(|&i| i == id).unwrap();
let popup_task = cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let popup_id = app.next_popup_id();
let i = app.menus.keys().position(|&i| i == id).unwrap();
let (i, parent) = self
.overflow_index()
.and_then(|overflow_i| {
if overflow_i <= i {
Some(i - overflow_i).zip(self.overflow_popup)
let (i, parent) = app
.overflow_index()
.and_then(|overflow_i| {
if overflow_i <= i {
Some(i - overflow_i).zip(app.overflow_popup)
} else {
Some((i, app.core.main_window_id().unwrap()))
}
})
.unwrap_or((i, app.core.main_window_id().unwrap()));
let mut popup_settings = app
.core
.applet
.get_popup_settings(parent, popup_id, None, None, None);
app.popup = Some(popup_id);
if matches!(
app.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = app.core.applet.suggested_size(true).1
+ 2 * app.core.applet.suggested_padding(true).1;
popup_settings.positioner.anchor_rect.y =
i as i32 * suggested_size as i32;
} else {
Some((i, self.core.main_window_id().unwrap()))
let suggested_size = app.core.applet.suggested_size(true).0
+ 2 * app.core.applet.suggested_padding(true).0;
popup_settings.positioner.anchor_rect.x =
i as i32 * suggested_size as i32;
}
})
.unwrap_or((i, self.core.main_window_id().unwrap()));
popup_settings
},
None,
));
cmds.push(popup_task);
let mut popup_settings = self
.core
.applet
.get_popup_settings(parent, popup_id, None, None, None);
self.popup = Some(popup_id);
if matches!(
self.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = self.core.applet.suggested_size(true).1
+ 2 * self.core.applet.suggested_padding(true).1;
popup_settings.positioner.anchor_rect.y = i as i32 * suggested_size as i32;
} else {
let suggested_size = self.core.applet.suggested_size(true).0
+ 2 * self.core.applet.suggested_padding(true).0;
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
}
cmds.push(get_popup(popup_settings));
Task::batch(cmds)
}
Msg::WheelScrolled(delta) => {
@ -399,33 +420,41 @@ impl cosmic::Application for App {
return destroy_popup(popup_id);
} else if let Some(overflow_index) = self.overflow_index() {
// If we don't have an overflow, create it
let popup_id = self.next_popup_id();
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
popup_id,
None,
None,
None,
);
popup_settings.close_with_children = false;
let popup_task =
cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let popup_id = app.next_popup_id();
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
popup_id,
None,
None,
None,
);
popup_settings.close_with_children = false;
if matches!(
self.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = self.core.applet.suggested_size(true).1
+ 2 * self.core.applet.suggested_padding(true).1;
popup_settings.positioner.anchor_rect.y =
overflow_index as i32 * suggested_size as i32;
} else {
let suggested_size = self.core.applet.suggested_size(true).0
+ 2 * self.core.applet.suggested_padding(true).0;
popup_settings.positioner.anchor_rect.x =
overflow_index as i32 * suggested_size as i32;
}
if matches!(
app.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = app.core.applet.suggested_size(true).1
+ 2 * app.core.applet.suggested_padding(true).1;
popup_settings.positioner.anchor_rect.y =
overflow_index as i32 * suggested_size as i32;
} else {
let suggested_size = app.core.applet.suggested_size(true).0
+ 2 * app.core.applet.suggested_padding(true).0;
popup_settings.positioner.anchor_rect.x =
overflow_index as i32 * suggested_size as i32;
}
self.overflow_popup = Some(popup_id);
return get_popup(popup_settings);
app.overflow_popup = Some(popup_id);
popup_settings
},
None,
));
return popup_task;
} else {
return Task::none();
}
@ -444,34 +473,41 @@ impl cosmic::Application for App {
} else {
return Task::none();
}
let popup_id = self.next_popup_id();
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
popup_id,
None,
None,
None,
);
self.popup = Some(popup_id);
let Some(i) = self.overflow_index() else {
return Task::batch(cmds);
};
let popup_task = cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
move |app: &mut Self| {
let popup_id = app.next_popup_id();
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
popup_id,
None,
None,
None,
);
app.popup = Some(popup_id);
if matches!(
self.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = self.core.applet.suggested_size(false).1
+ 2 * self.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.y = i as i32 * suggested_size as i32;
} else {
let suggested_size = self.core.applet.suggested_size(false).0
+ 2 * self.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
}
cmds.push(get_popup(popup_settings));
if matches!(
app.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
let suggested_size = app.core.applet.suggested_size(false).1
+ 2 * app.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.y =
i as i32 * suggested_size as i32;
} else {
let suggested_size = app.core.applet.suggested_size(false).0
+ 2 * app.core.applet.suggested_padding(false).1;
popup_settings.positioner.anchor_rect.x =
i as i32 * suggested_size as i32;
}
popup_settings
},
None,
));
cmds.push(popup_task);
Task::batch(cmds)
}
}
@ -656,7 +692,7 @@ fn menu_icon_button<'a>(
.class(if symbolic {
cosmic::theme::Svg::Custom(std::rc::Rc::new(|theme| {
cosmic::iced::widget::svg::Style {
color: Some(theme.cosmic().background(false).on.into()),
color: Some(theme.cosmic().background(theme.transparent).on.into()),
}
}))
} else {

View file

@ -232,7 +232,11 @@ fn layout_view(layout: &Layout, expanded: Option<i32>) -> cosmic::Element<'_, Ms
}
if let Some(icon_data) = i.icon_data() {
let handle = iced::widget::image::Handle::from_bytes(icon_data.to_vec());
children.insert(0, iced::widget::Image::new(handle).into());
let icon = iced::widget::Image::new(handle)
.width(iced::Length::Fixed(14.0))
.height(iced::Length::Fixed(14.0))
.content_fit(iced::core::ContentFit::Contain);
children.insert(0, icon.into());
} else if let Some(icon_name) = i.icon_name() {
let icon = cosmic::widget::icon::from_name(icon_name)
.size(14)

View file

@ -0,0 +1,2 @@
tile-windows = Organitza les finestres en mosaic automàticament
shortcuts = Dreceres

View file

@ -4,11 +4,11 @@ shortcuts = Tastenkombinationen
navigate-windows = Fenster navigieren
move-window = Fenster verschieben
toggle-floating-window = Schwebendes Fenster umschalten
view-all-shortcuts = Alle Tastenkombinationen ansehen...
view-all-shortcuts = Alle Tastenkombinationen ansehen ...
active-hint = Aktiv-Hinweis
gaps = Lücken
floating-window-exceptions = Ausnahmen für schwebende Fenster...
window-management-settings = Einstellungen für die Fensterverwaltung...
floating-window-exceptions = Ausnahmen für schwebende Fenster ...
window-management-settings = Einstellungen für die Fensterverwaltung ...
all-workspaces = Alle Arbeitsflächen
per-workspace = Pro Arbeitsfläche
super = Super

View file

@ -0,0 +1,20 @@
tile-windows = Dispoñer as xanelas en mosaico automaticamente
tile-current = Dispoñer en mosaico o espazo de traballo actual
shortcuts = Atallos
navigate-windows = Navergar polas xanelas
move-window = Mover a xanela
toggle-floating-window = Alternar xanela flotante
view-all-shortcuts = Ver todos os atallos...
active-hint = Indicador de foco activo
gaps = Espazos
floating-window-exceptions = Excepcións de xanelas flotantes...
window-management-settings = Axustes da xestión de xanelas...
all-workspaces = Todos os espazos de traballo
per-workspace = Por espazo de traballo
super = Super
shift = Maiús
arrow-keys = frechas
tiled = En mosaico
floating = Flotante
autotile-behavior = Organizar as xanelas en mosaico nos espazos de traballo
new-workspace = Comportamento dos novos espazos de traballo

View file

@ -3,9 +3,9 @@ tile-current = Huidig werkblad tegelen
shortcuts = Sneltoetsen
navigate-windows = Tussen vensters schakelen
move-window = Venster verplaatsen
toggle-floating-window = Zet zwevende vensters aan/uit
toggle-floating-window = Zweven aan- of uitzetten voor venster
view-all-shortcuts = Alle sneltoetsen bekijken…
active-hint = Visuele hint actief venster
active-hint = Vensterfocus-aanduiding
gaps = Ruimte rond getegelde vensters
floating-window-exceptions = Uitzonderingen voor zwevende vensters…
window-management-settings = Instellingen voor vensterbeheer…

View file

@ -147,17 +147,22 @@ impl cosmic::Application for Window {
return if let Some(p) = self.popup.take() {
destroy_popup(p)
} else {
let new_id = Id::unique();
self.popup = Some(new_id);
let popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
Some((1, 1)),
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Self| {
let new_id = Id::unique();
app.popup = Some(new_id);
let popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
Some((1, 1)),
None,
None,
);
popup_settings
},
None,
None,
);
get_popup(popup_settings)
));
};
}
Message::PopupClosed(id) => {

View file

@ -0,0 +1 @@
datetime-settings = Configuració de data, hora i calendari...

View file

@ -1 +1 @@
datetime-settings = Datum, Uhrzeit und Kalendereinstellungen...
datetime-settings = Datum, Uhrzeit und Kalendereinstellungen ...

View file

@ -0,0 +1 @@
datetime-settings = Axustes de data, hora e calendario...

View file

@ -521,35 +521,40 @@ impl cosmic::Application for Window {
if let Some(p) = self.popup.take() {
destroy_popup(p)
} else {
self.date_today = self.now.date();
self.date_selected = self.date_today;
return cosmic::surface::surface_task(cosmic::surface::action::app_popup(
|_| Default::default(),
|app: &mut Self| {
app.date_today = app.now.date();
app.date_selected = app.date_today;
let new_id = window::Id::unique();
self.popup = Some(new_id);
let new_id = window::Id::unique();
app.popup = Some(new_id);
let mut popup_settings = self.core.applet.get_popup_settings(
self.core.main_window_id().unwrap(),
new_id,
let mut popup_settings = app.core.applet.get_popup_settings(
app.core.main_window_id().unwrap(),
new_id,
None,
None,
None,
);
let Rectangle {
x,
y,
width,
height,
} = app.rectangle;
popup_settings.positioner.anchor_rect = Rectangle::<i32> {
x: x.max(1.) as i32,
y: y.max(1.) as i32,
width: width.max(1.) as i32,
height: height.max(1.) as i32,
};
popup_settings.positioner.size = None;
popup_settings
},
None,
None,
None,
);
let Rectangle {
x,
y,
width,
height,
} = self.rectangle;
popup_settings.positioner.anchor_rect = Rectangle::<i32> {
x: x.max(1.) as i32,
y: y.max(1.) as i32,
width: width.max(1.) as i32,
height: height.max(1.) as i32,
};
popup_settings.positioner.size = None;
get_popup(popup_settings)
));
}
}
Message::Tick => {

View file

@ -0,0 +1 @@
cosmic-applet-workspaces = Espais de treball del COSMIC

Some files were not shown because too many files have changed in this diff Show more