Compare commits

...
Sign in to create a new pull request.

56 commits

Author SHA1 Message Date
0186f5de15 chore: bump version to 1.3.0 2026-07-22 15:56:59 +02:00
jklgrasso
99cf076229 Update cosmic-protocols dep 2026-07-14 12:11:48 -04:00
Akrm Al-Hakimi
04d5f46d0d feat(networking): replace custom NetworkManager backend with nmrs
Switch the networking pages to use nmrs 3.4.0 for NetworkManager
state, device lists, Wi-Fi operations, VPN imports/activation, and
secret-agent handling.

Remove the old cosmic-settings-network-manager-subscription crate,
drop the direct dbus-settings NetworkManager dependencies, and delete
the unused nmcli VPN helper. Keep nm-connection-editor for profile
creation/editing where the UI still depends on it.

Carry Wi-Fi security metadata through the password flow so SAE/WPA3
personal networks can be activated with SAE key management, and make
failed/cancelled auth attempts clear pending connection state
2026-07-14 12:11:48 -04:00
Ashley Wulber
fff1485876
feat: reapply frosted glass feature 2026-07-10 19:06:17 +02:00
Jeremy Soller
ae5c750662
chore: update cosmic-panel-config (#2058)
- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.

Updated to work with https://github.com/pop-os/cosmic-panel/pull/627 and
should be tested with that to ensure everything works and is backwards
compatible.
2026-07-09 11:27:21 -06:00
Vukašin Vojinović
ccbd31f67c chore: update cosmic-panel-config 2026-07-09 17:15:58 +02:00
Hojjat
906725fd56 Revert "feat: frosted glass"
This reverts commit bce5164eea.
2026-07-08 18:06:46 -04:00
Hojjat
8393239125 Revert "fix: update iced"
This reverts commit 2d3bae47bb.
2026-07-08 18:06:46 -04:00
Victoria Brekenfeld
6202344c26 pages/display: Fix reverting orientation changes 2026-07-08 23:34:03 +02:00
Michael Aaron Murphy
b34d204a18 fix(sound): throttle volume sliders by 128ms 2026-07-08 22:20:12 +02:00
Michael Aaron Murphy
d971176aca build: make avif feature optional but enable by default 2026-07-07 16:34:25 -06:00
Michael Aaron Murphy
4d3915c1d7 feat(wallpapers): enable avif support via libdav1d 2026-07-07 16:34:25 -06:00
Jeremy Soller
c46e46b8e3
feat: frosted glass (#2062)
- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.

This should be merged last
2026-07-07 14:19:50 -06:00
Ashley Wulber
2d3bae47bb fix: update iced
popup blur & corner cleanup
2026-07-06 19:50:38 -04:00
Ashley Wulber
bce5164eea feat: frosted glass 2026-07-06 09:46:48 -04:00
Lysander Treumann
9f9311235e
i18n(en): change super-key-action disable to none
=
2026-06-24 23:06:14 +02:00
Veracion
dec2361a40
feat(keyboard): make key names for special character keys translatable 2026-06-24 22:46:03 +02:00
Anthony Lannutti
3bef2b08da
fix(network): validate MAC address length when parsing addresses from network manager
This fixes a bug in the HwAddress struct that allowed invalid MAC addresses to pass validation.

---

- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.
2026-06-24 17:10:28 +02:00
Michael Aaron Murphy
84729d1dcd
Merge remote-tracking branch 'weblate/master' 2026-06-23 20:19:26 +02:00
Hosted Weblate
ba67d30ce8
i18n: translation updates from weblate
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Arve Eriksson <arve.eriksson@startmail.com>
Co-authored-by: CYAXXX <85353920+CYAXXX@users.noreply.github.com>
Co-authored-by: David Carvalho <david.snt.carvalho@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fedorov Alexei <aleksejfedorov963@gmail.com>
Co-authored-by: Geeson Wan <wang14240@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hugo Carvalho <hugokarvalho@hotmail.com>
Co-authored-by: Jim Spentzos <jimspentzos2000@gmail.com>
Co-authored-by: Julien Brouillard <julienbrouillard1@gmail.com>
Co-authored-by: Lysander Treumann <lysander.treumann@gmx.de>
Co-authored-by: Mattias Eriksson <snaggen@gmail.com>
Co-authored-by: VandaL <vandalhj@gmail.com>
Co-authored-by: Walter William Beckerleg Bruckman <spayk.99@protonmail.com>
Co-authored-by: Zahid Rizky Fakhri <zahidrizkyfakhri@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: lorduskordus <lorduskordus@gmail.com>
Co-authored-by: taijuin Lee <taijuin@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-settings/ar/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/cs/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/de/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/el/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/fr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ga/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/id/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/kmr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pl/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ru/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/sv/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/uk/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/zh_Hant/
Translation: Pop OS/COSMIC Settings
2026-06-23 20:03:07 +02:00
Michael Murphy
a4d864dd8f
i18n: translation update from Hosted Weblate (#2016)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pop OS/COSMIC
Settings](https://hosted.weblate.org/projects/pop-os/cosmic-settings/).



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pop-os/cosmic-settings/horizontal-auto.svg)
2026-06-23 17:11:52 +02:00
Hosted Weblate
9876623f74
i18n: translation updates from weblate
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Arve Eriksson <arve.eriksson@startmail.com>
Co-authored-by: CYAXXX <85353920+CYAXXX@users.noreply.github.com>
Co-authored-by: David Carvalho <david.snt.carvalho@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fedorov Alexei <aleksejfedorov963@gmail.com>
Co-authored-by: Geeson Wan <wang14240@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hugo Carvalho <hugokarvalho@hotmail.com>
Co-authored-by: Jim Spentzos <jimspentzos2000@gmail.com>
Co-authored-by: Julien Brouillard <julienbrouillard1@gmail.com>
Co-authored-by: Lysander Treumann <lysander.treumann@gmx.de>
Co-authored-by: Mattias Eriksson <snaggen@gmail.com>
Co-authored-by: VandaL <vandalhj@gmail.com>
Co-authored-by: Walter William Beckerleg Bruckman <spayk.99@protonmail.com>
Co-authored-by: Zahid Rizky Fakhri <zahidrizkyfakhri@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: lorduskordus <lorduskordus@gmail.com>
Co-authored-by: taijuin Lee <taijuin@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-settings/ar/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/cs/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/de/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/el/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/fr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ga/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/id/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/kmr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pl/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ru/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/sv/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/uk/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/zh_Hant/
Translation: Pop OS/COSMIC Settings
2026-06-22 14:03:40 +02:00
Michael Aaron Murphy
5c23ec1b1a fix(default_apps): set text editor associations for application/x-zerosize 2026-06-18 12:12:00 -04:00
JADella94
1dd478aeb2
fix(vpn): handle non-file url on openvpn import
So openvpn import called to_file_path().unwrap(), which panics when the
file picker hands back a non-file:// url (sandboxed portal, etc). the
wireguard branch right above already handles this gracefully, so do the
same: return a Message::Error with a new OpenVpnConfigPath kind instead
of crashing.

Fixes #1896

- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.

Signed-off-by: James A DellaMorte <dellamorte.james@comcast.net>
Co-authored-by: James A DellaMorte <dellamorte.james@comcast.net>
2026-06-16 22:22:02 +02:00
Michael Murphy
9155a1e902
feat(sound): use cosmic-settings-daemon's varlink API 2026-06-15 21:09:03 +02:00
Ashley Wulber
3a77442dbc fix: check if action is already found before adding it to search results 2026-06-08 11:26:16 -04:00
Ashley Wulber
8cdf308db5 fix: focus inputs on open of context drawer 2026-06-05 15:37:44 -04:00
Anthony Lannutti
4ae20bc1db
feat: add openrc cargo feature for OpenRC bluetooth service management
Coding-Agent: Claude Code
Model: claude-sonnet-4-5
2026-06-04 20:34:57 +02:00
Steven Ceuppens
989a56346a
fix(bluetooth): add passkey/PIN display dialogs for keyboard pairing
Bluetooth keyboards require the host to display a passkey for the user
to type. Previously this was not supported because the agent capability
was set to DisplayYesNo and the DisplayPasskey/DisplayPinCode events
were not handled.

- Change agent capability to KeyboardDisplay
- Add DisplayPasskey dialog with typed-key progress indicator
- Add DisplayPinCode dialog for older keyboards
- Auto-dismiss display dialogs when device reports Paired=true
2026-06-03 23:24:04 +02:00
Michael Aaron Murphy
de5e16d360 fix(default-apps): additional missing apps 2026-06-02 16:30:23 +02:00
Michael Aaron Murphy
2e83e56c09 fix(default-apps): missing and wrong associations
* update the cosmic-mime-apps crate with fixes to app association detection
* actually use cosmic-mime-apps for significantly faster  association lookups
2026-06-01 17:27:28 +02:00
Hojjat
81912bed6c fix: wait for cosmic-settings-daemon and send initial max-brightness 2026-05-28 19:09:55 +02:00
Jeremy Soller
60a7de5adb
i18n: translation update from Hosted Weblate (#2005)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pop OS/COSMIC
Settings](https://hosted.weblate.org/projects/pop-os/cosmic-settings/).



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pop-os/cosmic-settings/horizontal-auto.svg)
2026-05-26 09:11:25 -06:00
Hosted Weblate
7e54af1f5b
i18n: translation updates from weblate
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Fedorov Alexei <aleksejfedorov963@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hugo Carvalho <hugokarvalho@hotmail.com>
Co-authored-by: Lysander Treumann <lysander.treumann@gmx.de>
Co-authored-by: therealmate <hellogaming91@gmail.com>
Co-authored-by: Димко <Dymkovych@proton.me>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ga/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ru/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/uk/
Translation: Pop OS/COSMIC Settings
2026-05-25 21:11:40 +02:00
Vukašin Vojinović
f19da76129 improv(panel): corner radius handling
This ensures that the padding of a panel is taken into account when calculating corner_radius (except when Square), and also prevents various visual issues, like slightly round without gaps having a corner radius of 0.

Also extracts panel config fetching into a helper function.
2026-05-19 18:36:07 +02:00
manascb1344
1f225d19ce refactor: extract sorting logic for startup applications and all apps
- Introduced a new function `sort_entries_by_name` to encapsulate the sorting logic for desktop entries by name, improving code readability and maintainability.
- Updated existing sorting implementations to use the new function, ensuring consistent behavior across the application.
2026-05-19 13:13:34 +02:00
manascb1344
d6bf77bf3a sort startup applications and all apps by name for improved organization 2026-05-19 13:13:34 +02:00
Vukašin Vojinović
6db1e6a17b fix(sound): use go_next_item for device profiles button 2026-05-15 16:26:20 -06:00
Vukašin Vojinović
4b1f388a4f chore: add rustfmt config 2026-05-15 16:26:20 -06:00
Vukašin Vojinović
cb4a0d3a3a chore: update libcosmic 2026-05-15 16:26:20 -06:00
manascb1344
f2c374ff6b Sort VPN connections alphabetically
Adds case-insensitive alphabetical sorting to the VPN connections list.
The connections are now sorted by name when received and before display,
making it easier to find specific VPN profiles.

Fixes #1947
2026-05-15 09:22:51 -06:00
Ashley Wulber
703a934b09 fix: retry connection to settings daemon and allow requests for brightness values 2026-05-12 16:22:00 -04:00
Michael Murphy
a96987091e
i18n: translation update from Hosted Weblate (#1963)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pop OS/COSMIC
Settings](https://hosted.weblate.org/projects/pop-os/cosmic-settings/).



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pop-os/cosmic-settings/horizontal-auto.svg)
2026-05-12 17:01:20 +02:00
Hosted Weblate
9b7a9538e6
i18n: translation updates from weblate
Co-authored-by: Baurzhan Muftakhidinov <baurthefirst@gmail.com>
Co-authored-by: BoneNI <bounkirdni@gmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: David Carvalho <david.snt.carvalho@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fedorov Alexei <aleksejfedorov963@gmail.com>
Co-authored-by: Geeson Wan <wang14240@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hugo Carvalho <hugokarvalho@hotmail.com>
Co-authored-by: Isaac Subirana <isaacsubiranac@gmail.com>
Co-authored-by: Jim Spentzos <jimspentzos2000@gmail.com>
Co-authored-by: Jiri Grönroos <jiri.gronroos@iki.fi>
Co-authored-by: Julien Brouillard <julienbrouillard1@gmail.com>
Co-authored-by: Jun Hwi Ku <siguning@gmail.com>
Co-authored-by: Nara Díaz Viñolas <nara.diaz.vinolas@gmail.com>
Co-authored-by: Tadas Misiūnas <tadujo@gmail.com>
Co-authored-by: VandaL <vandalhj@gmail.com>
Co-authored-by: Walter William Beckerleg Bruckman <spayk.99@protonmail.com>
Co-authored-by: Zahid Rizky Fakhri <zahidrizkyfakhri@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: lorduskordus <lorduskordus@gmail.com>
Co-authored-by: therealmate <hellogaming91@gmail.com>
Co-authored-by: Димко <Dymkovych@proton.me>
Co-authored-by: Марко М. Костић <marko.m.kostic@gmail.com>
Co-authored-by: 김유빈 <k.sein1016@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ar/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ca/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/cs/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/de/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/el/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/fi/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/fr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/id/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/kk/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ko/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/lt/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pl/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/ru/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/sr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/sv/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/uk/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-settings/zh_Hant/
Translation: Pop OS/COSMIC Settings
2026-05-12 11:11:45 +00:00
manascb1344
d632047ab3 fix(wifi): remove misleading search placeholder 2026-05-12 10:31:19 +02:00
Jeremy Soller
cb58af2be4
chore: add defaults (#1994)
- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.
2026-05-04 12:53:00 -06:00
Ashley Wulber
d49a1d1990 chore: add defaults 2026-05-04 13:15:57 -04:00
Jeremy Soller
1a44f8634f
Update libcosmic (#1985)
- Contains fix for wgpu invalidation error with float16 shader
- Updates MSRV to Rust 1.93
- Updates wgpu to v28 & cosmic-text to v0.19

---

- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.
2026-04-27 12:32:57 -06:00
Michael Aaron Murphy
f4c7a2c14d
chore: bump version to 1.0.12 2026-04-27 15:29:49 +02:00
Michael Aaron Murphy
c882df3d20
chore: update libcosmic & iced
- Contains fix for wgpu validation error on float16 shader with older
  GPUs.
- Updates wgpu to 28 and cosmic-text to 0.19
- Misc. fixes and improvements for libcosmic widgets
2026-04-27 15:29:44 +02:00
Michael Aaron Murphy
e4c4c0d903
build: set MSRV to Rust 1.93 2026-04-27 15:08:13 +02:00
Anthony Lannutti
fa085f9006
fix: set locale with DBus to fix OpenRC support
This PR is intended to address #1955. At a high level, when using OpenRC with `openrc-settingsd`, the application would panic after trying to run the non-existent `localectl` command. This PR addresses that issue by setting the locale via D-Bus instead of through `localectl`. Additionally, there was a call to `localectl` to get the available locales for the system that was replaced with the more portable and POSIX compliant `locale` command.

- [x] I have disclosed use of any AI generated code in my commit messages.
- [x] I understand these changes in full and will be able to respond to review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions.

---
AI Disclosure: This code was generated with the assistance of AI (Claude 3.7 Sonnet)
under human direction and supervision. All code has been reviewed and tested.
2026-04-25 00:07:15 +02:00
Ashley Wulber
78644a32e3
Revert "Changes the PageInner.size field to have no explicit default.… (#1977)
… It is only initialized (not None) when PageInner.panel_config is
initialized. This implementation fails faster and more verbose, when
program is in an unintended state."

This reverts commit ba5fb65b44.

- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.

Co-authored-by: Levi Portenier <levi@system76.com>
2026-04-22 14:33:37 -06:00
Tony Wasserka
c04e49e218 fix(wallpapers): follow symlinks when scanning wallpaper directories
On NixOS, /run/current-system/sw/share/backgrounds lists all wallpapers
installed on the system. The folder entries are symbolic links to the actual
PNG files (each in a dedicated nix store path). Previously WalkDir would skip
over these, resulting in cosmic-settings displaying an empty wallpaper list.

By following symbolic links, the list is now properly populated.
2026-04-22 22:10:24 +02:00
Vukašin Vojinović
26f0fb0609 chore(default_apps): reduce code duplication
Also fixes the flex items wrapping too early by setting the FlexRow width to Shrink.
2026-04-22 22:10:06 +02:00
Vukašin Vojinović
317de19eb3 fix(shortcuts): modified count for accessibility and tiling
These weren't being set previously, so they never showed up.
Also updates remaining things here to use ListButton.
2026-04-22 22:10:06 +02:00
254 changed files with 10290 additions and 9228 deletions

View file

@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: install system dependencies
run: sudo apt-get update && sudo apt-get install lld cmake libclang-dev libexpat1-dev libfontconfig-dev libfreetype-dev libpipewire-0.3-dev libpulse-dev pkg-config libxkbcommon-dev libudev-dev libinput-dev libwayland-dev
run: sudo apt-get update && sudo apt-get install lld cmake libclang-dev libdav1d-dev libexpat1-dev libfontconfig-dev libfreetype-dev libpipewire-0.3-dev libpulse-dev pkg-config libxkbcommon-dev libudev-dev libinput-dev libwayland-dev
- uses: actions/checkout@v6
- name: install toolchain
run: rustup toolchain install 1.90.0 --component clippy
@ -38,7 +38,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: install system dependencies
run: sudo apt-get update && sudo apt-get install lld cmake libclang-dev libexpat1-dev libfontconfig-dev libfreetype-dev libpipewire-0.3-dev libpulse-dev pkg-config libxkbcommon-dev libudev-dev libinput-dev libwayland-dev
run: sudo apt-get update && sudo apt-get install lld cmake libclang-dev libdav1d-dev libexpat1-dev libfontconfig-dev libfreetype-dev libpipewire-0.3-dev libpulse-dev pkg-config libxkbcommon-dev libudev-dev libinput-dev libwayland-dev
- uses: actions/checkout@v6
- name: install toolchain
run: rustup show
@ -50,7 +50,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: install system dependencies
run: sudo apt-get update && sudo apt-get install lld cmake libclang-dev libexpat1-dev libfontconfig-dev libfreetype-dev libpipewire-0.3-dev libpulse-dev pkg-config libxkbcommon-dev libudev-dev libinput-dev libwayland-dev just
run: sudo apt-get update && sudo apt-get install lld cmake libclang-dev libdav1d-dev libexpat1-dev libfontconfig-dev libfreetype-dev libpipewire-0.3-dev libpulse-dev pkg-config libxkbcommon-dev libudev-dev libinput-dev libwayland-dev just
- uses: actions/checkout@v6
- name: install toolchain
run: rustup show

15
.zed/settings.json Normal file
View file

@ -0,0 +1,15 @@
{
"format_on_save": "on",
"lsp": {
"rust-analyzer": {
"initialization_options": {
"check": {
"command": "clippy",
},
"rustfmt": {
"extraArgs": ["+nightly"],
},
},
},
},
}

2296
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,22 @@
[workspace]
members = ["cosmic-settings", "crates/*", "page", "pages/*", "subscriptions/*"]
members = ["cosmic-settings", "page", "pages/*", "subscriptions/*"]
default-members = ["cosmic-settings"]
resolver = "3"
[workspace.package]
rust-version = "1.90"
rust-version = "1.93"
[workspace.dependencies]
cosmic-randr = { git = "https://github.com/pop-os/cosmic-randr" }
cosmic-randr = { path = "../cosmic-randr/lib" }
tokio = { version = "1.49.0", features = ["macros"] }
iced_winit = { git = "https://github.com/pop-os/libcosmic", default-features = false }
iced_winit = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/winit", default-features = false }
image = { version = "0.25.10", default-features = false, features = [
"jpeg",
"png",
"rayon",
"webp",
"hdr",
] }
[workspace.dependencies.libcosmic]
features = [
@ -20,29 +27,29 @@ features = [
"tokio",
"qr_code",
]
git = "https://github.com/pop-os/libcosmic"
path = "/home/lionel/Projets/COSMIC/libcosmic"
[workspace.dependencies.cosmic-config]
git = "https://github.com/pop-os/libcosmic"
path = "/home/lionel/Projets/COSMIC/libcosmic/cosmic-config"
[workspace.dependencies.cosmic-bg-config]
git = "https://github.com/pop-os/cosmic-bg"
path = "../cosmic-bg/config"
[workspace.dependencies.cosmic-comp-config]
git = "https://github.com/pop-os/cosmic-comp"
path = "../cosmic-comp/cosmic-comp-config"
features = ["output"]
[workspace.dependencies.cosmic-idle-config]
git = "https://github.com/pop-os/cosmic-idle"
path = "../cosmic-idle/cosmic-idle-config"
[workspace.dependencies.cosmic-panel-config]
git = "https://github.com/pop-os/cosmic-panel"
path = "../cosmic-panel/cosmic-panel-config"
[workspace.dependencies.cosmic-randr-shell]
git = "https://github.com/pop-os/cosmic-randr"
path = "../cosmic-randr/shell"
[workspace.dependencies.sctk]
version = "0.20"
version = "1.3.0"
package = "smithay-client-toolkit"
[profile.dev]
@ -59,34 +66,18 @@ inherits = "release"
lto = "off"
debug = true
# [patch.'https://github.com/pop-os/cosmic-text']
# cosmic-text = { git = "https://github.com/pop-os/cosmic-text//", rev = "b017d7c" }
[patch.'https://github.com/pop-os/cosmic-protocols']
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols//", rev = "d0e95be" }
cosmic-client-toolkit = { git = "https://github.com/pop-os/cosmic-protocols//", rev = "d0e95be" }
cosmic-protocols = { path = "../cosmic-protocols" }
cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" }
# [patch.'https://github.com/pop-os/cosmic-settings-daemon']
# cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon//", branch = "input_nobuild" }
[patch.'https://github.com/pop-os/libcosmic']
cosmic-config = { path = "/home/lionel/Projets/COSMIC/libcosmic/cosmic-config" }
cosmic-theme = { path = "/home/lionel/Projets/COSMIC/libcosmic/cosmic-theme" }
iced_futures = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/futures" }
iced_winit = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/winit" }
# For development and testing purposes
# [patch.'https://github.com/pop-os/libcosmic']
# libcosmic = { path = "../libcosmic" }
# cosmic-config = { path = "../libcosmic/cosmic-config" }
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
# iced_futures = { path = "../libcosmic/iced/futures" }
# iced_winit = { path = "../libcosmic/iced/winit" }
# libcosmic = { git = "https://github.com/pop-os/libcosmic//" }
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//" }
# cosmic-theme = { git = "https://github.com/pop-os/libcosmic//" }
# iced_futures = { git = "https://github.com/pop-os/libcosmic//" }
# iced_winit = { git = "https://github.com/pop-os/libcosmic//" }
# [patch.'https://github.com/pop-os/dbus-settings-bindings']
# cosmic-dbus-networkmanager = { path = "../dbus-settings-bindings/networkmanager" }
# upower_dbus = { path = "../dbus-settings-bindings/upower" }
# nm-secret-agent-manager = { git = "https://github.com/pop-os/dbus-settings-bindings//", branch = "nm-secret-agent" }
[patch.'https://github.com/pop-os/cosmic-text.git']
cosmic-text = { path = "../cosmic-text" }
[patch.crates-io]
atspi = { git = "https://github.com/wash2/atspi" }

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-settings"
version = "1.0.7"
version = "1.0.12"
edition = "2024"
license = "GPL-3.0-only"
publish = false
@ -21,8 +21,6 @@ color-eyre = "0.6.5"
cosmic-bg-config.workspace = true
cosmic-comp-config = { workspace = true, optional = true }
cosmic-config.workspace = true
cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
nm-secret-agent-manager = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
cosmic-idle-config.workspace = true
cosmic-panel-config = { workspace = true, optional = true }
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", optional = true }
@ -34,11 +32,8 @@ cosmic-settings-accessibility-subscription = { path = "../subscriptions/accessib
cosmic-settings-a11y-manager-subscription = { path = "../subscriptions/a11y-manager", optional = true }
cosmic-settings-airplane-mode-subscription = { path = "../subscriptions/airplane-mode", optional = true }
cosmic-settings-bluetooth-subscription = { path = "../subscriptions/bluetooth", optional = true }
cosmic-settings-network-manager-subscription = { path = "../subscriptions/network-manager", optional = true }
cosmic-settings-sound = { path = "../pages/sound", optional = true }
cosmic-settings-upower-subscription = { path = "../subscriptions/upower", optional = true }
cosmic-settings-sound-subscription = { path = "../subscriptions/sound", optional = true, features = [
"auto-profile-init",
] }
cosmic-settings-wallpaper = { path = "../pages/wallpapers" }
cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
derive_setters = "0.1.9"
@ -49,13 +44,7 @@ futures = "0.3.32"
hostname-validator = "1.1.1"
hostname1-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
i18n-embed-fl = "0.10.0"
image = { version = "0.25", default-features = false, features = [
"jpeg",
"png",
"rayon",
"webp",
"hdr",
] }
image.workspace = true
indexmap = "2.13.0"
itertools = "0.14.0"
itoa = "1.0.17"
@ -63,13 +52,15 @@ libcosmic.workspace = true
iced_winit.workspace = true
locale1 = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
sysinfo = { version = "=0.38.0", optional = true }
mime-apps = { package = "cosmic-mime-apps", git = "https://github.com/pop-os/cosmic-mime-apps", optional = true }
mime-apps = { package = "cosmic-mime-apps", git = "https://github.com/pop-os/cosmic-mime-apps", features = ["tokio"], optional = true }
notify = "8.2.0"
nmrs = { version = "3.4.0", optional = true }
regex = "1.12.3"
ron = "0.12"
rust-embed = "8.11.0"
sctk = { workspace = true, optional = true }
secure-string = "0.3.0"
secret-service = { version = "5.1.0", features = ["rt-tokio-crypto-rust"], optional = true }
serde = { version = "1.0.228", features = ["derive"] }
slab = "0.4.12"
slotmap = "1.1.1"
@ -98,6 +89,13 @@ gettext-rs = { version = "0.7.7", features = [
num-traits = "0.2"
pwhash = "1"
which = "8.0.0"
zlink = "0.5.0"
intmap = "3.1.3"
[dependencies.cosmic-settings-audio-client]
git = "https://github.com/pop-os/cosmic-settings-daemon"
features = ["codec"]
optional = true
[dependencies.icu]
version = "2.1.1"
@ -113,8 +111,11 @@ git = "https://github.com/AerynOS/locales-rs"
optional = true
[features]
default = ["a11y", "linux", "single-instance", "wgpu"]
default = ["a11y", "avif", "linux", "single-instance", "wgpu", "systemd"]
avif = ["image/avif-native"]
gettext = ["dep:gettext-rs"]
systemd = []
openrc = []
# Default features for Linux
linux = [
@ -167,10 +168,9 @@ page-input = [
]
page-legacy-applications = ["cosmic-comp-config", "dep:cosmic-randr"]
page-networking = [
"dep:cosmic-settings-network-manager-subscription",
"xdg-portal",
"dep:cosmic-dbus-networkmanager",
"dep:nm-secret-agent-manager",
"dep:nmrs",
"dep:secret-service",
"dep:zbus",
]
page-power = ["dep:upower_dbus", "dep:zbus"]
@ -181,7 +181,7 @@ page-region = [
"dep:zbus",
"dep:accounts-zbus",
]
page-sound = ["dep:cosmic-settings-sound-subscription"]
page-sound = ["dep:cosmic-settings-audio-client", "dep:cosmic-settings-sound"]
page-users = ["xdg-portal", "dep:accounts-zbus", "dep:zbus", "dep:zbus_polkit"]
page-window-management = ["cosmic-comp-config", "dep:cosmic-settings-config"]
page-workspaces = ["cosmic-comp-config"]

View file

@ -1,4 +1,5 @@
use std::{env, fs, path::PathBuf};
use std::path::PathBuf;
use std::{env, fs};
use xdgen::{App, Context, FluentString};
fn main() {

View file

@ -5,7 +5,6 @@ use crate::PageCommands;
use crate::config::Config;
#[cfg(feature = "page-accessibility")]
use crate::pages::accessibility;
use crate::pages::applications;
#[cfg(feature = "page-bluetooth")]
use crate::pages::bluetooth;
use crate::pages::desktop::{self, appearance};
@ -19,26 +18,21 @@ use crate::pages::networking;
use crate::pages::power;
#[cfg(feature = "page-sound")]
use crate::pages::sound;
use crate::pages::{self, system, time};
use crate::pages::{self, applications, system, time};
use crate::subscription::desktop_files;
use crate::widget::{page_title, search_header};
use cosmic::app::context_drawer::ContextDrawer;
use cosmic::app::{Core, Task};
#[cfg(feature = "wayland")]
use cosmic::cctk::{sctk::output::OutputInfo, wayland_client::protocol::wl_output::WlOutput};
use cosmic::{
Element,
app::{Core, Task, context_drawer::ContextDrawer},
iced::{
self, Length, Subscription,
event::{self, PlatformSpecific},
keyboard, window,
},
prelude::*,
surface,
widget::{
button, column, container, icon, id_container, nav_bar, row, scrollable, segmented_button,
settings, text_input,
},
use cosmic::iced::event::{self, PlatformSpecific};
use cosmic::iced::{self, Length, Subscription, keyboard, window};
use cosmic::prelude::*;
use cosmic::widget::{
button, column, container, icon, id_container, nav_bar, row, scrollable, segmented_button,
settings, text_input,
};
use cosmic::{Element, surface};
#[cfg(feature = "cosmic-comp-config")]
use cosmic_comp_config::CosmicCompConfig;
#[cfg(feature = "wayland")]
@ -52,8 +46,9 @@ use desktop::{
#[cfg(feature = "wayland")]
use event::wayland;
use page::Entity;
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::{borrow::Cow, str::FromStr};
use std::str::FromStr;
#[allow(clippy::struct_excessive_bools)]
#[allow(clippy::module_name_repetitions)]

View file

@ -1,10 +1,8 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{
cosmic_config::{self, ConfigGet, ConfigSet},
cosmic_theme::palette::Srgba,
};
use cosmic::cosmic_config::{self, ConfigGet, ConfigSet};
use cosmic::cosmic_theme::palette::Srgba;
const NAME: &str = "com.system76.CosmicSettings";

View file

@ -1,10 +1,8 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use i18n_embed::{
DefaultLocalizer, LanguageLoader, Localizer,
fluent::{FluentLanguageLoader, fluent_language_loader},
};
use i18n_embed::fluent::{FluentLanguageLoader, fluent_language_loader};
use i18n_embed::{DefaultLocalizer, LanguageLoader, Localizer};
use rust_embed::RustEmbed;
use std::sync::LazyLock;

View file

@ -7,6 +7,12 @@
#![allow(clippy::cast_lossless)]
#![allow(clippy::too_many_lines)]
#[cfg(not(any(feature = "systemd", feature = "openrc")))]
compile_error!(
"At least one service manager feature must be enabled. \
Enable 'systemd' or 'openrc' for bluetooth service management."
);
pub mod app;
use std::str::FromStr;
@ -16,6 +22,7 @@ pub mod config;
#[macro_use]
pub mod localize;
pub mod pages;
pub mod service_manager;
pub mod subscription;
pub mod theme;
pub mod utils;
@ -24,7 +31,8 @@ pub mod widget;
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use cosmic::{app::CosmicFlags, iced::Limits};
use cosmic::app::CosmicFlags;
use cosmic::iced::Limits;
use i18n_embed::DesktopLanguageRequester;
use ron::error::SpannedError;
use serde::{Deserialize, Serialize};

View file

@ -1,20 +1,15 @@
use std::collections::HashSet;
use std::fmt::Write;
use cosmic::{
Apply,
iced::core::text::Wrapping,
iced::{Element, Length, stream},
surface,
widget::{self, icon, settings, svg, text},
};
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::{Element, Length, stream};
use cosmic::widget::{self, icon, settings, svg, text};
use cosmic::{Apply, surface};
use cosmic_comp_config::{ZoomConfig, ZoomMovement};
use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_config::{Action, Binding, shortcuts};
use cosmic_settings_page::{
self as page, Entity,
section::{self, Section},
};
use cosmic_settings_page::section::{self, Section};
use cosmic_settings_page::{self as page, Entity};
use futures::SinkExt;
use slotmap::SlotMap;
use tracing::error;

View file

@ -1,21 +1,16 @@
use cosmic::{
Apply, Element, Task,
cosmic_theme::{CosmicPalette, ThemeBuilder},
iced::core::text::Wrapping,
iced::stream,
surface,
theme::CosmicTheme,
widget::{dropdown, settings, text},
};
use cosmic::cosmic_theme::{CosmicPalette, ThemeBuilder};
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::stream;
use cosmic::theme::CosmicTheme;
use cosmic::widget::{dropdown, settings, text};
use cosmic::{Apply, Element, Task, surface};
pub use cosmic_comp_config::ZoomMovement;
use cosmic_config::CosmicConfigEntry;
use cosmic_settings_a11y_manager_subscription as cosmic_a11y_manager;
use cosmic_settings_accessibility_subscription as a11y_bus;
use cosmic_settings_daemon_config::CosmicSettingsDaemonConfig;
use cosmic_settings_page::{
self as page, Insert,
section::{self, Section},
};
use cosmic_settings_page::section::{self, Section};
use cosmic_settings_page::{self as page, Insert};
use futures::SinkExt;
use num_traits::FromPrimitive;
use slotmap::SlotMap;

View file

@ -2,28 +2,23 @@
// Copyright 2024 bbb651 <bar.ye651@gmail.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
path::{Path, PathBuf},
sync::Arc,
};
use cosmic::{
Apply, Element, Task,
iced::Alignment,
surface,
widget::{self, dropdown, icon, settings},
};
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{self, dropdown, icon, settings};
use cosmic::{Apply, Element, Task, surface};
use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_config::shortcuts::SystemActions;
use cosmic_settings_config::shortcuts::action::System;
use cosmic_settings_page::{self as page, Section, section};
use freedesktop_desktop_entry::{
DesktopEntry, Iter as DesktopEntryIter, default_paths, get_languages_from_env,
};
use mime_apps::App;
use freedesktop_desktop_entry::{DesktopEntry, default_paths, get_languages_from_env};
use mime::Mime;
use slotmap::SlotMap;
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::io::SeekFrom;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use tokio::io::{AsyncSeekExt, AsyncWriteExt};
const DROPDOWN_WEB_BROWSER: usize = 0;
const DROPDOWN_FILE_MANAGER: usize = 1;
@ -52,7 +47,7 @@ pub enum Category {
#[derive(Clone, Debug)]
pub enum Message {
SetDefault(Category, usize),
Update(CachedMimeApps),
Update(Arc<CachedMimeApps>),
Surface(surface::Action),
}
@ -68,28 +63,27 @@ impl From<Message> for crate::pages::Message {
}
}
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct CachedMimeApps {
pub list: mime_apps::List,
pub local_list: mime_apps::List,
pub apps: Vec<AppMeta>,
pub known_mimes: BTreeSet<mime::Mime>,
pub config_path: Box<Path>,
local_list: Option<(mime_apps::List, tokio::fs::File)>,
apps: Vec<AppMeta>,
known_mimes: BTreeSet<mime::Mime>,
}
#[derive(Clone, Debug)]
pub struct AppMeta {
selected: Option<usize>,
app_ids: Vec<String>,
app_ids: Vec<Box<str>>,
apps: Vec<String>,
icons: Vec<icon::Handle>,
}
#[derive(Clone, Debug, Default)]
#[derive(Debug, Default)]
pub struct Page {
on_enter_handle: Option<cosmic::iced::task::Handle>,
mime_apps: Option<CachedMimeApps>,
shortcuts_config: Option<cosmic_config::Config>,
update_config: Option<tokio::task::JoinHandle<()>>,
}
impl page::AutoBind<crate::pages::Message> for Page {}
@ -113,46 +107,45 @@ impl page::Page<crate::pages::Message> for Page {
handle.abort();
}
let config_update_handle = self.update_config.take();
if self.shortcuts_config.is_none() {
self.shortcuts_config = cosmic_settings_config::shortcuts::context().ok();
}
let (task, on_enter_handle) = Task::future(async move {
let mut list = mime_apps::List::default();
list.load_from_paths(&mime_apps::list_paths());
let mut local_list = mime_apps::List::default();
if let Some(path) = mime_apps::local_list_path()
&& let Ok(buffer) = std::fs::read_to_string(&path)
{
local_list.load_from(&buffer);
// Wait for previous copy operation to complete.
if let Some(handle) = config_update_handle {
_ = handle.await;
}
let assocs = mime_apps::associations::by_app();
let local_list = mime_apps::load_user_mimeapps().await.ok();
let mut list = mime_apps::List::default();
list.load_from_paths(&mime_apps::list_paths());
let assocs = mime_apps::associations::load_from_disk(&list);
let apps = vec![
load_defaults(&assocs, &["x-scheme-handler/http"]).await,
load_defaults(&assocs, &["inode/directory"]).await,
load_defaults(&assocs, &["x-scheme-handler/mailto"]).await,
load_defaults(&assocs, &["audio/mp3", "application/ogg", "video/mp4"]).await,
load_defaults(&assocs, &["video/mp4"]).await,
load_defaults(&assocs, &["image/png"]).await,
load_defaults(&assocs, &["text/calendar"]).await,
load_terminal_apps(&assocs).await,
load_defaults(&assocs, &["text/plain"]).await,
load_defaults(&list, &assocs, &["x-scheme-handler/http"]),
load_defaults(&list, &assocs, &["inode/directory"]),
load_defaults(&list, &assocs, &["x-scheme-handler/mailto"]),
load_defaults(
&list,
&assocs,
&["audio/mpeg", "application/ogg", "audio/x-flac", "video/mp4"],
),
load_defaults(&list, &assocs, &["video/mp4"]),
load_defaults(&list, &assocs, &["image/png"]),
load_defaults(&list, &assocs, &["text/calendar"]),
load_terminal_apps(&list, &assocs).await,
load_defaults(&list, &assocs, &["text/plain"]),
];
Message::Update(CachedMimeApps {
Message::Update(Arc::new(CachedMimeApps {
apps,
list,
local_list,
known_mimes: mime_apps::mime_info::mime_types(),
config_path: dirs::config_dir()
.expect("config dir not found")
.join("mimeapps.list")
.into(),
})
}))
.into()
})
.abortable();
@ -193,6 +186,7 @@ impl Page {
"application/ogg",
"application/x-cue",
"application/x-ogg",
"audio/mpeg",
"audio/mp3",
"x-content/audio-cdda",
])
@ -237,13 +231,16 @@ impl Page {
"x-scheme-handler/https",
],
),
Category::TextEditor => (DROPDOWN_TEXT_EDITOR, &["text/plain"]),
Category::TextEditor => (
DROPDOWN_TEXT_EDITOR,
&["application/x-zerosize", "text/plain"],
),
Category::Mime(_mime_type) => return Task::none(),
};
let meta = &mut mime_apps.apps[category_id];
if meta.selected != Some(id) {
if meta.selected.is_none_or(|selected| selected != id) {
meta.selected = Some(id);
let appid = &meta.app_ids[id];
@ -254,24 +251,40 @@ impl Page {
assign_default_terminal(config, appid);
}
for mime in mime_types {
if let Ok(mime) = mime.parse() {
mime_apps
.local_list
.set_default_app(mime, [appid, ".desktop"].concat());
};
if let Some((local_list, local_file)) = mime_apps.local_list.as_mut() {
for mime in mime_types {
if let Ok(mime) = mime.parse() {
tracing::info!(target: "default-apps", ?mime, appid, "setting default for mime");
local_list.set_default_app(mime, [appid, ".desktop"].concat());
};
}
if let Some(config_update_handle) = self.update_config.take() {
config_update_handle.abort();
}
let mut buffer = local_list.to_string();
buffer.push('\n');
if let Ok(mut local_file) =
futures::executor::block_on(local_file.try_clone())
{
self.update_config = Some(tokio::spawn(async move {
tracing::debug!(target: "default-apps", buffer, "writing to mimeapps config");
_ = local_file.seek(SeekFrom::Start(0)).await;
_ = local_file.set_len(buffer.len() as u64).await;
_ = local_file.write_all(buffer.as_bytes()).await;
_ = local_file.flush().await;
_ = local_file.seek(SeekFrom::Start(0)).await;
_ = tokio::process::Command::new("update-desktop-database")
.status()
.await;
}));
}
}
let mut buffer = mime_apps.local_list.to_string();
buffer.push('\n');
_ = std::fs::write(&mime_apps.config_path, buffer);
_ = std::process::Command::new("update-desktop-database").status();
}
}
Message::Update(mime_apps) => {
self.mime_apps = Some(mime_apps);
}
Message::Update(mime_apps) => self.mime_apps = Arc::into_inner(mime_apps),
Message::Surface(a) => {
return cosmic::task::message(crate::app::Message::Surface(a));
}
@ -281,269 +294,83 @@ impl Page {
}
}
fn app_item(meta: &AppMeta, label: String, category: Category) -> widget::FlexRow<'_, Message> {
// Avoid creating popup_dropdown for empty app lists.
// Empty categories are rendered as non-interactive text to prevent Wayland crashes.
settings::flex_item(
label,
if meta.apps.is_empty() {
widget::text(fl!("default-apps", "not-installed")).apply(Element::from)
} else {
dropdown::popup_dropdown(
&meta.apps,
Some(meta.selected.unwrap_or(0)),
move |id| Message::SetDefault(category, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| crate::app::Message::PageMessage(crate::pages::Message::DefaultApps(a)),
)
.icons(Cow::Borrowed(&meta.icons))
.apply(Element::from)
},
)
.align_items(Alignment::Center)
.width(Length::Shrink)
}
fn apps() -> Section<crate::pages::Message> {
Section::default().view::<Page>(move |_binder, page, section| {
let Some(mime_apps) = page.mime_apps.as_ref() else {
return widget::space().into();
};
// Avoid creating popup_dropdown for empty app lists.
// Empty categories are rendered as non-interactive text to prevent Wayland crashes.
settings::section()
settings::section::with_capacity(9)
.title(&section.title)
.add({
let meta = &mime_apps.apps[DROPDOWN_WEB_BROWSER];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "web-browser"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "web-browser"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::WebBrowser, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
.min_item_width(300.0)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_FILE_MANAGER];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "file-manager"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "file-manager"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::FileManager, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_MAIL];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "mail-client"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "mail-client"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::Mail, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_MUSIC];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "music"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "music"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::Audio, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_VIDEO];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "video"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "video"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::Video, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_PHOTO];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "photos"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "photos"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::Image, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_CALENDAR];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "calendar"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "calendar"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::Calendar, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_TERMINAL];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "terminal"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "terminal"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::Terminal, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add({
let meta = &mime_apps.apps[DROPDOWN_TEXT_EDITOR];
if meta.apps.is_empty() {
settings::flex_item(
fl!("default-apps", "text-editor"),
widget::text(fl!("default-apps", "not-installed")),
)
.align_items(Alignment::Center)
} else {
settings::flex_item(
fl!("default-apps", "text-editor"),
dropdown::popup_dropdown(
&meta.apps,
meta.selected,
|id| Message::SetDefault(Category::TextEditor, id),
cosmic::iced::window::Id::RESERVED,
Message::Surface,
|a| {
crate::app::Message::PageMessage(
crate::pages::Message::DefaultApps(a),
)
},
)
.icons(Cow::Borrowed(&meta.icons)),
)
.align_items(Alignment::Center)
}
})
.add(app_item(
&mime_apps.apps[DROPDOWN_WEB_BROWSER],
fl!("default-apps", "web-browser"),
Category::WebBrowser,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_FILE_MANAGER],
fl!("default-apps", "file-manager"),
Category::FileManager,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_MAIL],
fl!("default-apps", "mail-client"),
Category::Mail,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_MUSIC],
fl!("default-apps", "music"),
Category::Audio,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_VIDEO],
fl!("default-apps", "video"),
Category::Video,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_PHOTO],
fl!("default-apps", "photos"),
Category::Image,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_CALENDAR],
fl!("default-apps", "calendar"),
Category::Calendar,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_TERMINAL],
fl!("default-apps", "terminal"),
Category::Terminal,
))
.add(app_item(
&mime_apps.apps[DROPDOWN_TEXT_EDITOR],
fl!("default-apps", "text-editor"),
Category::TextEditor,
))
.apply(Element::from)
.map(crate::pages::Message::DefaultApps)
})
@ -584,57 +411,71 @@ fn assign_default_terminal(config: &cosmic_config::Config, appid: &str) {
}
}
async fn load_defaults(assocs: &BTreeMap<Arc<str>, Arc<App>>, for_mimes: &[&str]) -> AppMeta {
fn load_defaults(
list: &mime_apps::List,
assocs: &mime_apps::Associations,
for_mimes: &[&str],
) -> AppMeta {
let mut unsorted = Vec::new();
let mut current_app = None;
let mut default_app = None;
for for_mime in for_mimes {
let Ok(mime) = for_mime.parse() else {
return AppMeta {
selected: None,
app_ids: Vec::new(),
apps: Vec::new(),
icons: Vec::new(),
};
let Ok(mime) = Mime::from_str(for_mime) else {
continue;
};
let current_app_entry = xdg_mime_query_default(for_mime).await;
let current_appid = current_app_entry
.as_ref()
.and_then(|entry| entry.strip_suffix(".desktop"));
for default in list.default_app_for(&mime).into_iter().flatten() {
let appid = default.strip_suffix(".desktop").unwrap_or(default.as_ref());
if unsorted.is_empty() {
current_app = current_appid.and_then(|appid| assocs.get(appid));
tracing::info!(target: "default-apps", ?mime, appid, "entry for mime");
if default_app.is_none() {
default_app = assocs.by_app_id.get(appid);
if default_app.is_some() {
break;
}
}
}
unsorted.extend(
mime_apps::apps_for_mime(&mime, assocs)
.map(|(app_id, app)| (app_id.clone(), app.clone())),
);
for (appid, app) in mime_apps::apps_for_mime(&mime, assocs) {
if app.icon.is_none() {
continue;
}
if unsorted.iter().any(|(id, _)| id == appid) {
continue;
}
unsorted.push((appid.clone(), app.clone()));
}
}
unsorted.sort_unstable_by_key(|(_, app)| app.name.clone());
unsorted.dedup_by_key(|(app_id, _)| app_id.clone());
let mut selected = None;
let mut app_ids = Vec::new();
let mut apps = Vec::new();
let mut icons = Vec::new();
for (id, (appid, app)) in unsorted.iter().enumerate() {
if let Some(current_app) = current_app
&& app.name.as_ref() == current_app.name.as_ref()
let mut id = 0;
for (appid, app) in unsorted.iter() {
let Some(icon) = app.icon.as_deref() else {
continue;
};
if let Some(default_app) = default_app
&& app.name.as_ref() == default_app.name.as_ref()
{
selected = Some(id);
}
app_ids.push(appid.as_ref().into());
apps.push(app.name.as_ref().into());
icons.push(if app.icon.starts_with('/') {
icon::from_path(PathBuf::from(app.icon.as_ref()))
icons.push(if icon.starts_with('/') {
icon::from_path(PathBuf::from(icon))
} else {
icon::from_name(app.icon.as_ref()).size(20).handle()
icon::from_name(icon).size(20).handle()
});
id += 1;
}
AppMeta {
@ -645,86 +486,55 @@ async fn load_defaults(assocs: &BTreeMap<Arc<str>, Arc<App>>, for_mimes: &[&str]
}
}
async fn xdg_mime_query_default(mime_type: &str) -> Option<String> {
let output = tokio::process::Command::new("xdg-mime")
.args(["query", "default", mime_type])
.output()
.await
.ok()?;
if !output.status.success() {
return None;
}
String::from_utf8(output.stdout)
.ok()
.map(|string| string.trim().to_owned())
}
async fn load_terminal_apps(assocs: &BTreeMap<Arc<str>, Arc<App>>) -> AppMeta {
let mut terminals = Vec::new();
let mut current_app = None;
// Get the current default terminal if set
let current_entry = xdg_mime_query_default("x-scheme-handler/terminal").await;
let current_appid = current_entry
.as_ref()
.and_then(|entry| entry.strip_suffix(".desktop"));
// Also check x-terminal-emulator
if current_app.is_none() {
let current_entry = xdg_mime_query_default("application/x-terminal-emulator").await;
let current_appid = current_entry
.as_ref()
.and_then(|entry| entry.strip_suffix(".desktop"));
if let Some(appid) = current_appid {
current_app = assocs.get(appid).cloned();
}
}
// Scan desktop entries for terminal applications
for path in DesktopEntryIter::new(default_paths()) {
if let Ok(bytes) = std::fs::read_to_string(&path)
&& let Ok(entry) = DesktopEntry::from_str(&path, &bytes, None::<&[&str]>)
{
// Check if it's a terminal application
if entry
.categories()
.map(|cats| cats.contains(&"TerminalEmulator"))
.unwrap_or(false)
{
let id = entry.id();
if let Some(app) = assocs.get(id) {
if current_appid.as_ref().map(|c| *c == id).unwrap_or(false) {
current_app = Some(app.clone());
}
terminals.push((Arc::from(id), app.clone()));
}
}
}
}
terminals.sort_unstable_by_key(|(_, app): &(Arc<str>, Arc<App>)| app.name.clone());
terminals.dedup_by(|a, b| a.0 == b.0);
async fn load_terminal_apps(list: &mime_apps::List, assocs: &mime_apps::Associations) -> AppMeta {
let mut selected = None;
let mut app_ids = Vec::new();
let mut apps = Vec::new();
let mut icons = Vec::new();
for (id, (appid, app)) in terminals.iter().enumerate() {
if let Some(ref current_app) = current_app
&& app.name.as_ref() == current_app.name.as_ref()
{
let mut terminals: Vec<_> = assocs
.apps
.iter()
.filter(|app| app.is_terminal && app.icon.is_some())
.collect();
terminals.sort_unstable_by_key(|app| app.name.clone());
'outer: for for_mime in [
"x-scheme-handler/terminal",
"application/x-terminal-emulator",
] {
let Ok(mime) = Mime::from_str(for_mime) else {
continue;
};
for default in list.default_app_for(&mime).into_iter().flatten() {
let appid = default.strip_suffix(".desktop").unwrap_or(default.as_ref());
for (id, app) in terminals.iter().enumerate() {
if app.appid.as_ref() == appid {
selected = Some(id);
break 'outer;
}
}
}
}
for (id, app) in terminals.into_iter().enumerate() {
let Some(icon) = app.icon.as_deref() else {
continue;
};
if selected.is_none() && app.appid.as_ref() == "com.system76.CosmicTerm" {
selected = Some(id);
}
app_ids.push(appid.to_string());
app_ids.push(app.appid.clone());
apps.push(app.name.as_ref().into());
icons.push(if app.icon.starts_with('/') {
icon::from_path(PathBuf::from(app.icon.as_ref()))
icons.push(if icon.starts_with('/') {
icon::from_path(PathBuf::from(icon))
} else {
icon::from_name(app.icon.as_ref()).size(20).handle()
icon::from_name(icon).size(20).handle()
});
}

View file

@ -1,25 +1,17 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::{
process::ExitStatus,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
use std::process::ExitStatus;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use cosmic::{
Apply, Element, Task,
cosmic_config::{self, ConfigGet, ConfigSet},
iced::stream,
surface,
widget::{self, dropdown, settings, text},
};
use cosmic::cosmic_config::{self, ConfigGet, ConfigSet};
use cosmic::iced::stream;
use cosmic::widget::{self, dropdown, settings, text};
use cosmic::{Apply, Element, Task, surface};
use cosmic_comp_config::{EavesdroppingKeyboardMode, XwaylandDescaling, XwaylandEavesdropping};
use cosmic_randr_shell::List;
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use futures::SinkExt;
use slotmap::SlotMap;
use tokio::sync::oneshot;

View file

@ -1,7 +1,8 @@
use cosmic::app::ContextDrawer;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{button, icon, settings, text};
use cosmic::{Apply, Element, Task, widget};
use cosmic::widget::text_input::focus;
use cosmic::widget::{Id, button, icon, settings, text};
use cosmic::{Apply, Element, Task, task, widget};
use cosmic_settings_page::section::Entity;
use cosmic_settings_page::{self as page, Content, Info, Section};
use freedesktop_desktop_entry::DesktopEntry;
@ -9,8 +10,12 @@ use itertools::Itertools;
use slotmap::{Key, SlotMap};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::LazyLock;
use tracing::error;
pub static ADD_APPLICATION_SEARCH: LazyLock<widget::Id> =
LazyLock::new(|| widget::Id::new("ADD_APPLICATION_SEARCH"));
#[derive(Clone, Debug)]
pub struct CachedApps {
apps: HashMap<DirectoryType, Vec<DesktopEntry>>,
@ -51,6 +56,7 @@ pub enum Message {
ShowApplicationSidebar(DirectoryType),
UpdateApplications(CachedApps),
UpdateStartupApplications(CachedApps),
FocusAddApplicationSearch,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
@ -116,6 +122,7 @@ impl page::Page<crate::pages::Message> for Page {
let search = widget::search_input("", &self.application_search)
.on_input(|i| Message::ApplicationSearch(i).into())
.on_clear(Message::ApplicationSearch(String::new()).into())
.id(ADD_APPLICATION_SEARCH.clone())
.apply(Element::from);
Some(
@ -142,8 +149,11 @@ impl page::Page<crate::pages::Message> for Page {
let user_entries =
freedesktop_desktop_entry::Iter::new(user_dirs.into_iter()).entries(Some(&locales));
let mut user_entries_vec = user_entries.collect_vec();
sort_entries_by_name(&mut user_entries_vec, &locales);
let mut apps_hash = HashMap::with_capacity(1);
apps_hash.insert(DirectoryType::User, user_entries.collect_vec());
apps_hash.insert(DirectoryType::User, user_entries_vec);
Message::UpdateStartupApplications(CachedApps {
apps: apps_hash,
@ -217,7 +227,8 @@ impl Page {
}
Message::ShowApplicationSidebar(directory_type) => {
self.context = Some(Context::AddApplication(directory_type));
return cosmic::task::message(crate::app::Message::OpenContextDrawer(self.entity));
return cosmic::task::message(crate::app::Message::OpenContextDrawer(self.entity))
.chain(task::message(Message::FocusAddApplicationSearch));
}
Message::AddStartupApplication(directory_type, app) => {
let mut file_name = app.clone().appid;
@ -246,6 +257,10 @@ impl Page {
if let Some(target_apps) = target_apps {
let mut new_apps = target_apps.clone();
new_apps.push(app.clone());
sort_entries_by_name(
&mut new_apps,
&cached_startup_apps.locales,
);
cached_startup_apps
.apps
@ -312,6 +327,26 @@ impl Page {
self.target_directory_type = None;
self.context = None;
}
Message::FocusAddApplicationSearch => {
// retry until the widget is in the tree and focused or the dialog is removed.
if matches!(self.context, Some(Context::AddApplication(_))) {
return cosmic::iced::runtime::task::widget(
cosmic::iced::core::widget::operation::focusable::find_focused(),
)
.collect()
.then(|id| {
if id
.first()
.is_some_and(|id| *id == ADD_APPLICATION_SEARCH.clone())
{
Task::none()
} else {
focus(ADD_APPLICATION_SEARCH.clone())
.chain(task::message(Message::FocusAddApplicationSearch))
}
});
}
}
_ => {}
}
@ -433,6 +468,14 @@ fn apps() -> Section<crate::pages::Message> {
})
}
fn sort_entries_by_name(entries: &mut [DesktopEntry], locales: &[String]) {
entries.sort_by_cached_key(|e| {
e.name(locales)
.map(|n| n.to_lowercase())
.unwrap_or_else(|| e.appid.to_lowercase())
});
}
fn get_all_apps(locales: Vec<String>) -> Vec<DesktopEntry> {
let mut dedupe = HashSet::new();
@ -473,5 +516,7 @@ fn get_all_apps(locales: Vec<String>) -> Vec<DesktopEntry> {
dedupe.insert(app_id.to_owned());
}
sort_entries_by_name(&mut result, &locales);
result
}

View file

@ -3,7 +3,8 @@
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::{Alignment, Length, color};
use cosmic::widget::{self, settings, space::horizontal as horizontal_space, text};
use cosmic::widget::space::horizontal as horizontal_space;
use cosmic::widget::{self, settings, text};
use cosmic::{Apply, Element, Task, theme};
use cosmic_settings_bluetooth_subscription::*;
use cosmic_settings_page::{self as page, Section, section};
@ -15,24 +16,25 @@ use std::sync::Arc;
use std::time::Duration;
use zbus::zvariant::OwnedObjectPath;
#[cfg(test)]
use crate::service_manager::MockServiceManager;
use crate::service_manager::ServiceManagerHandle;
enum Dialog {
// RequestAuthorization {
// device: OwnedObjectPath,
// response: oneshot::Sender<bool>,
// },
RequestConfirmation {
device: String,
passkey: u32,
response: oneshot::Sender<bool>,
},
// RequestPasskey {
// device: OwnedObjectPath,
// response: oneshot::Sender<Option<u32>>,
// },
// RequestPinCode {
// device: OwnedObjectPath,
// response: oneshot::Sender<Option<String>>,
// },
DisplayPasskey {
device: String,
passkey: u32,
entered: u16,
},
DisplayPinCode {
device: String,
pincode: String,
},
}
#[derive(Default)]
@ -160,6 +162,8 @@ pub struct Page {
service_is_active: bool,
subscription: Option<tokio::sync::oneshot::Sender<()>>,
service_manager: ServiceManagerHandle,
}
impl page::Page<crate::pages::Message> for Page {
@ -255,6 +259,85 @@ impl page::Page<crate::pages::Message> for Page {
Some(dialog)
}
Dialog::DisplayPasskey {
device,
passkey,
entered,
} => {
let description = widget::text::body(fl!(
"bluetooth-display-passkey",
"description",
device = device
))
.wrapping(Wrapping::Word);
let passkey_str = format!("{passkey:06}");
let entered = *entered as usize;
let pin = widget::text::title1(passkey_str)
.width(Length::Fill)
.align_x(Alignment::Center)
.wrapping(Wrapping::None);
let mut control = widget::column::with_capacity(3).push(description).push(pin);
// Only show the key progress counter when the device actually
// reports keypress notifications. Most keyboards don't support
// this, so entered stays at 0 for the entire pairing process.
if entered > 0 {
let progress = widget::text::body(format!("{entered} / 6 keys entered"))
.width(Length::Fill)
.align_x(Alignment::Center)
.wrapping(Wrapping::None);
control = control.push(progress);
}
let control = control.spacing(theme::spacing().space_xxs);
let cancel_button =
widget::button::standard(fl!("cancel")).on_press(Message::PinCancel);
let dialog = widget::dialog()
.title(fl!("bluetooth-display-passkey"))
.control(control)
.secondary_action(cancel_button)
.apply(Element::from)
.map(Into::into);
Some(dialog)
}
Dialog::DisplayPinCode { device, pincode } => {
let description = widget::text::body(fl!(
"bluetooth-display-pin",
"description",
device = device
))
.wrapping(Wrapping::Word);
let pin = widget::text::title1(pincode.clone())
.width(Length::Fill)
.align_x(Alignment::Center)
.wrapping(Wrapping::None);
let control = widget::column::with_capacity(2)
.push(description)
.push(pin)
.spacing(theme::spacing().space_xxs);
let cancel_button =
widget::button::standard(fl!("cancel")).on_press(Message::PinCancel);
let dialog = widget::dialog()
.title(fl!("bluetooth-display-pin"))
.control(control)
.secondary_action(cancel_button)
.apply(Element::from)
.map(Into::into);
Some(dialog)
}
}
}
}
@ -401,6 +484,19 @@ impl Page {
}
Event::UpdatedDevice(path, update) => {
// Dismiss passkey/pin display dialogs when pairing completes,
// since BlueZ may not always send Cancel after a successful pair.
if update
.iter()
.any(|u| matches!(u, DeviceUpdate::Paired(true)))
&& matches!(
self.dialog,
Some(Dialog::DisplayPasskey { .. } | Dialog::DisplayPinCode { .. })
)
{
self.dialog = None;
}
if let Some(existing) = self.model.devices.get_mut(&path) {
tracing::debug!("Device {} updated", existing.address);
existing.update(update);
@ -434,7 +530,18 @@ impl Page {
}
Event::DBusServiceUnknown => {
self.bluez_service_unknown = true;
// D-Bus activation for org.bluez is absent. On systemd this usually
// means bluez isn't installed; on OpenRC it may be installed but lack
// the D-Bus service file. Check the service manager to disambiguate.
if self.service_manager.is_installed() {
// Service manager confirms the service exists — query its real state.
self.bluez_service_unknown = false;
self.service_is_active = self.service_manager.is_active();
self.service_is_enabled = self.service_manager.is_enabled();
} else {
// Genuinely not installed — let status() show the unknown message.
self.bluez_service_unknown = true;
}
}
Event::Agent(message) => {
@ -464,6 +571,32 @@ impl Page {
});
}
bluez_zbus::agent1::Message::DisplayPasskey {
device,
passkey,
entered,
} => {
let device = self.model.devices.get(&device).map_or_else(
|| device.to_string(),
|device| device.alias_or_addr().to_owned(),
);
self.dialog = Some(Dialog::DisplayPasskey {
device,
passkey,
entered,
});
}
bluez_zbus::agent1::Message::DisplayPinCode { device, pincode } => {
let device = self.model.devices.get(&device).map_or_else(
|| device.to_string(),
|device| device.alias_or_addr().to_owned(),
);
self.dialog = Some(Dialog::DisplayPinCode { device, pincode });
}
bluez_zbus::agent1::Message::RequestPasskey { response, .. } => {
_ = response.send(None);
}
@ -552,8 +685,8 @@ impl Page {
}
Message::DBusConnect(connection) => {
self.service_is_active = systemd::is_bluetooth_active();
self.service_is_enabled = systemd::is_bluetooth_enabled();
self.service_is_active = self.service_manager.is_active();
self.service_is_enabled = self.service_manager.is_enabled();
self.connection = Some(connection.clone());
let get_adapters_fut = get_adapters(connection.clone());
@ -685,8 +818,9 @@ impl Page {
}
Message::ServiceActivate => {
return cosmic::task::future(async {
systemd::activate_bluetooth().await;
let activate_future = self.service_manager.activate();
return cosmic::task::future(async move {
activate_future.await;
tokio::time::sleep(Duration::from_secs(3)).await;
match zbus::Connection::system().await {
@ -697,8 +831,9 @@ impl Page {
}
Message::ServiceEnable => {
return cosmic::task::future(async {
systemd::enable_bluetooth().await;
let enable_future = self.service_manager.enable();
return cosmic::task::future(async move {
enable_future.await;
tokio::time::sleep(Duration::from_secs(3)).await;
match zbus::Connection::system().await {
@ -757,7 +892,7 @@ fn status() -> Section<crate::pages::Message> {
return bluetooth_service_issue(
fl!("bluetooth", "inactive"),
fl!("activate"),
Message::ServiceEnable,
Message::ServiceActivate,
);
}
@ -1010,36 +1145,65 @@ fn multiple_adapter() -> Section<crate::pages::Message> {
impl page::AutoBind<crate::pages::Message> for Page {}
mod systemd {
use futures::FutureExt;
pub fn activate_bluetooth() -> impl Future<Output = ()> + Send {
tokio::process::Command::new("pkexec")
.args(["systemctl", "start", "bluetooth"])
.status()
.map(|_| ())
}
pub fn enable_bluetooth() -> impl Future<Output = ()> + Send {
tokio::process::Command::new("pkexec")
.args(["systemctl", "enable", "--now", "bluetooth"])
.status()
.map(|_| ())
}
pub fn is_bluetooth_enabled() -> bool {
std::process::Command::new("systemctl")
.args(["is-enabled", "bluetooth"])
.status()
.map(|status| status.success())
.unwrap_or(true)
}
pub fn is_bluetooth_active() -> bool {
std::process::Command::new("systemctl")
.args(["is-active", "bluetooth"])
.status()
.map(|status| status.success())
.unwrap_or(true)
impl Page {
#[cfg(test)]
fn with_service_manager(service_manager: ServiceManagerHandle) -> Self {
Self {
service_manager,
..Page::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dbus_service_unknown_with_installed_service_queries_manager() {
let bluetooth = MockServiceManager::new(true, false);
let mut page = Page::with_service_manager(ServiceManagerHandle::new(Box::new(bluetooth)));
let _task = page.update(Message::BluetoothEvent(Event::DBusServiceUnknown));
assert!(page.service_is_enabled);
assert!(!page.service_is_active);
assert!(!page.bluez_service_unknown);
}
#[test]
fn test_dbus_service_unknown_with_uninstalled_service_sets_bluez_unknown() {
let bluetooth = MockServiceManager::new(true, true).with_installed(false);
let mut page = Page::with_service_manager(ServiceManagerHandle::new(Box::new(bluetooth)));
let _task = page.update(Message::BluetoothEvent(Event::DBusServiceUnknown));
assert!(page.bluez_service_unknown);
assert!(!page.service_is_enabled);
assert!(!page.service_is_active);
}
#[test]
fn test_dbus_service_unknown_clears_previously_set_bluez_service_unknown() {
// Simulate a stale state and verify the handler re-checks via is_installed().
let bluetooth = MockServiceManager::new(true, true);
let mut page = Page::with_service_manager(ServiceManagerHandle::new(Box::new(bluetooth)));
page.bluez_service_unknown = true;
let _task = page.update(Message::BluetoothEvent(Event::DBusServiceUnknown));
assert!(!page.bluez_service_unknown);
}
#[tokio::test]
async fn test_service_activate_calls_through_to_service_manager() {
let bluetooth = MockServiceManager::new(false, false);
let mut page = Page::with_service_manager(ServiceManagerHandle::new(Box::new(bluetooth)));
let _task = page.update(Message::ServiceActivate);
}
#[tokio::test]
async fn test_service_enable_calls_through_to_service_manager() {
let bluetooth = MockServiceManager::new(false, false);
let mut page = Page::with_service_manager(ServiceManagerHandle::new(Box::new(bluetooth)));
let _task = page.update(Message::ServiceEnable);
}
}

View file

@ -2,12 +2,11 @@ use cosmic::app::{ContextDrawer, context_drawer};
use cosmic::config::CosmicTk;
use cosmic::cosmic_config::{Config, ConfigSet};
use cosmic::cosmic_theme::Spacing;
use cosmic::iced::Alignment;
use cosmic::iced::core::{Color, Length};
use cosmic::widget::{
ColorPickerModel, color_picker::ColorPickerUpdate, container, flex_row, settings, text,
};
use cosmic::{Apply, Task};
use cosmic::{Element, widget};
use cosmic::widget::color_picker::ColorPickerUpdate;
use cosmic::widget::{ColorPickerModel, container, flex_row, row, settings, slider, text};
use cosmic::{Apply, Element, Task, task, widget};
use cosmic_config::ConfigGet;
use std::sync::Arc;
use tracing::error;
@ -15,11 +14,8 @@ use tracing::error;
use crate::app;
use crate::widget::color_picker_context_view;
use super::{
ContextView, Message, font_config, icon_themes,
icon_themes::{IconHandles, IconThemes},
theme_manager,
};
use super::icon_themes::{IconHandles, IconThemes};
use super::{ContextView, Message, font_config, icon_themes, theme_manager};
pub struct Content {
context_view: Option<ContextView>,
@ -40,6 +36,12 @@ pub struct Content {
icon_themes: IconThemes,
icon_handles: IconHandles,
tk_config: Option<Config>,
frosted: u8,
glass: f32,
frosted_system_interface: bool,
frosted_windows: bool,
frosted_panel: bool,
frosted_applets: bool,
comp_config: cosmic_config::Config,
#[cfg(feature = "cosmic-comp-config")]
@ -94,7 +96,7 @@ impl From<&theme_manager::Manager> for Content {
application_background: ColorPickerModel::new(
&*HEX,
&*RGB,
Some(theme.background.base.into()),
Some(theme.background(false).base.into()),
theme_manager.get_color(&ContextView::ApplicationBackground),
),
container_background: ColorPickerModel::new(
@ -106,7 +108,7 @@ impl From<&theme_manager::Manager> for Content {
interface_text: ColorPickerModel::new(
&*HEX,
&*RGB,
Some(theme.background.on.into()),
Some(theme.background(false).on.into()),
theme_manager.get_color(&ContextView::InterfaceText),
),
control_component: ColorPickerModel::new(
@ -122,6 +124,12 @@ impl From<&theme_manager::Manager> for Content {
theme_manager.get_color(&ContextView::AccentWindowHint),
),
font_config: font_config::Model::new(),
frosted: theme.frosted as u8,
glass: theme.alpha_map.guess_offset(),
frosted_system_interface: theme.frosted_system_interface,
frosted_windows: theme.frosted_windows,
frosted_panel: theme.frosted_panel,
frosted_applets: theme.frosted_applets,
icons_fetched: false,
icon_global: cosmic::config::apply_theme_global(),
icon_fetch_handle: None,
@ -285,6 +293,40 @@ impl Content {
Task::none()
}
pub fn update_frosted_system_interface(&mut self, v: bool) -> Task<app::Message> {
self.frosted_system_interface = v;
Task::none()
}
pub fn update_frosted_windows(&mut self, v: bool) -> Task<app::Message> {
self.frosted_windows = v;
Task::none()
}
pub fn update_frosted_panel(&mut self, v: bool) -> Task<app::Message> {
self.frosted_panel = v;
Task::none()
}
pub fn update_frosted_applets(&mut self, v: bool) -> Task<app::Message> {
self.frosted_applets = v;
Task::none()
}
pub fn update_blur(&mut self, v: u8) -> Task<app::Message> {
self.frosted = v;
Task::none()
}
pub fn update_glass(&mut self, v: f32) -> Task<app::Message> {
self.glass = v;
Task::none()
}
pub fn on_open(&mut self, context_view: &ContextView) -> Task<app::Message> {
match *context_view {
ContextView::IconsAndToolkit => {
@ -300,6 +342,7 @@ impl Content {
}
ContextView::MonospaceFont | ContextView::SystemFont => {
self.font_config.reset();
return task::message(super::Message::FocusFontInput);
}
_ => {}
}
@ -331,7 +374,7 @@ impl Content {
self.application_background = ColorPickerModel::new(
&*HEX,
&*RGB,
Some(manager.theme().background.base.into()),
Some(manager.theme().background(false).base.into()),
manager.get_color(&ContextView::ApplicationBackground),
);
self.custom_accent = ColorPickerModel::new(
@ -349,7 +392,7 @@ impl Content {
self.interface_text = ColorPickerModel::new(
&*HEX,
&*RGB,
Some(manager.theme().background.on.into()),
Some(manager.theme().background(false).on.into()),
manager.get_color(&ContextView::InterfaceText),
);
self.control_component = ColorPickerModel::new(
@ -475,6 +518,10 @@ impl Content {
self.shadow_and_corners(),
crate::pages::Message::CloseContextDrawer,
),
ContextView::FrostedGlass => context_drawer(
self.frosted_glass(),
crate::pages::Message::CloseContextDrawer,
),
})
}
@ -558,4 +605,66 @@ impl Content {
.apply(Element::from)
.map(crate::pages::Message::Appearance)
}
pub fn frosted_glass(&self) -> Element<'_, crate::pages::Message> {
settings::section()
.add(
settings::item::builder(fl!("style", "frosted-panels"))
.description(fl!("style", "frosted-panels-desc"))
.toggler(self.frosted_panel, Message::FrostedPanel),
)
.add(
settings::item::builder(fl!("style", "frosted-applets"))
.description(fl!("style", "frosted-applets-desc"))
.toggler(self.frosted_applets, Message::FrostedApplets),
)
.add(
settings::item::builder(fl!("style", "frosted-system-interface"))
.description(fl!("style", "frosted-system-interface-desc"))
.toggler(
self.frosted_system_interface,
Message::FrostedSystemInterface,
),
)
.add(
settings::item::builder(fl!("style", "frosted-windows"))
.description(fl!("style", "frosted-windows-desc"))
.toggler(self.frosted_windows, Message::FrostedWindows),
)
.add(
settings::item::builder(fl!("style", "frosted-thickness")).flex_control({
row::with_children(vec![
text::body(fl!("style", "less")).into(),
slider(0..=13, self.frosted, Message::Blur)
.width(Length::Fill)
.apply(cosmic::widget::container)
.max_width(250)
.into(),
text::body(fl!("style", "more")).into(),
])
.align_y(Alignment::Center)
.spacing(8)
.width(Length::Fill)
}),
)
.add(
settings::item::builder(fl!("style", "glass-opacity")).flex_control({
row::with_children(vec![
text::body(fl!("style", "less")).into(),
slider(0.0..=1.0, self.glass, Message::Glass)
.step(0.05)
.width(Length::Fill)
.apply(cosmic::widget::container)
.max_width(250)
.into(),
text::body(fl!("style", "more")).into(),
])
.align_y(Alignment::Center)
.spacing(8)
.width(Length::Fill)
}),
)
.apply(Element::from)
.map(crate::pages::Message::Appearance)
}
}

View file

@ -1,20 +1,20 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::sync::Arc;
use std::sync::{Arc, LazyLock};
use cosmic::{
Apply, Element, Task,
config::{CosmicTk, FontConfig},
widget,
};
use cosmic::config::{CosmicTk, FontConfig};
use cosmic::{Apply, Element, Task, widget};
use cosmic_config::ConfigSet;
use mime::FONT;
use crate::app;
use crate::widget::selection_context_item;
use super::{ContextView, Message, drawer};
pub static FONT_SEARCH: LazyLock<widget::Id> = LazyLock::new(|| widget::Id::new("FONT_SEARCH"));
const INTERFACE_FONT: &str = "interface_font";
const MONOSPACE_FONT: &str = "monospace_font";
@ -167,6 +167,7 @@ impl Model {
.on_clear(Message::DrawerFont(drawer::FontMessage::Search(
String::new(),
)))
.id(FONT_SEARCH.clone())
.apply(Element::from)
.map(crate::pages::Message::Appearance)
}

View file

@ -1,14 +1,13 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::{collections::BTreeMap, path::PathBuf};
use std::collections::BTreeMap;
use std::path::PathBuf;
use super::Message;
use cosmic::{
Element,
iced::{Background, Length},
widget::{button, icon, text},
};
use cosmic::Element;
use cosmic::iced::{Background, Length};
use cosmic::widget::{button, icon, text};
use tokio::io::AsyncBufReadExt;
const ICON_PREV_N: usize = 6;

View file

@ -16,23 +16,24 @@ use cosmic::app::ContextDrawer;
use cosmic::config::CosmicTk;
use cosmic::cosmic_config::{Config, ConfigSet, CosmicConfigEntry};
use cosmic::cosmic_theme::palette::{FromColor, Hsv, Srgb};
use cosmic::cosmic_theme::{CornerRadii, Density, ThemeBuilder, ThemeMode};
use cosmic::cosmic_theme::{CornerRadii, Density, Roundness, ThemeBuilder, ThemeMode};
#[cfg(feature = "xdg-portal")]
use cosmic::dialog::file_chooser::{self, FileFilter};
use cosmic::iced::Subscription;
use cosmic::iced::core::{Alignment, Length};
use cosmic::widget::{
button, color_picker::ColorPickerUpdate, container, row, settings, space::horizontal, text,
};
use cosmic::{Apply, Element, Task, widget};
use cosmic::widget::color_picker::ColorPickerUpdate;
use cosmic::widget::space::horizontal;
use cosmic::widget::text_input::focus;
use cosmic::widget::{button, container, row, settings, text};
use cosmic::{Apply, Element, Task, task, widget};
#[cfg(feature = "wayland")]
use cosmic_panel_config::CosmicPanelConfig;
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use ron::ser::PrettyConfig;
use slotmap::{Key, SlotMap};
use crate::app;
use crate::pages::desktop::appearance::font_config::FONT_SEARCH;
#[derive(Clone, Copy, Debug)]
pub enum ContextView {
@ -40,6 +41,7 @@ pub enum ContextView {
ApplicationBackground,
ContainerBackground,
ControlComponent,
FrostedGlass,
#[cfg(feature = "cosmic-comp-config")]
ShadowAndCorners,
CustomAccent,
@ -139,6 +141,7 @@ pub enum Message {
#[cfg(feature = "xdg-portal")]
ExportSuccess,
FocusFontInput,
GapSize(u32),
#[cfg(feature = "xdg-portal")]
ImportError,
@ -157,6 +160,12 @@ pub enum Message {
UseDefaultWindowHint(bool),
WindowHintSize(u32),
Daytime(bool),
Blur(u8),
Glass(f32),
FrostedPanel(bool),
FrostedApplets(bool),
FrostedSystemInterface(bool),
FrostedWindows(bool),
}
impl From<Message> for crate::app::Message {
@ -171,56 +180,6 @@ impl From<Message> for crate::pages::Message {
}
}
#[derive(Debug, Clone, Copy)]
pub enum Roundness {
Round,
SlightlyRound,
Square,
}
impl From<Roundness> for CornerRadii {
fn from(value: Roundness) -> Self {
match value {
Roundness::Round => CornerRadii {
radius_0: [0.0; 4],
radius_xs: [4.0; 4],
radius_s: [8.0; 4],
radius_m: [16.0; 4],
radius_l: [32.0; 4],
radius_xl: [160.0; 4],
},
Roundness::SlightlyRound => CornerRadii {
radius_0: [0.0; 4],
radius_xs: [2.0; 4],
radius_s: [8.0; 4],
radius_m: [8.0; 4],
radius_l: [8.0; 4],
radius_xl: [8.0; 4],
},
Roundness::Square => CornerRadii {
radius_0: [0.0; 4],
radius_xs: [2.0; 4],
radius_s: [2.0; 4],
radius_m: [2.0; 4],
radius_l: [2.0; 4],
radius_xl: [2.0; 4],
},
}
}
}
impl From<CornerRadii> for Roundness {
fn from(value: CornerRadii) -> Self {
if (value.radius_m[0] - 16.0).abs() < 0.01 {
Self::Round
} else if (value.radius_m[0] - 8.0).abs() < 0.01 {
Self::SlightlyRound
} else {
Self::Square
}
}
}
impl Page {
#[allow(clippy::too_many_lines)]
pub fn update(&mut self, message: Message) -> Task<app::Message> {
@ -241,6 +200,7 @@ impl Page {
Message::DrawerOpen(context_view) => {
self.context_view = Some(context_view);
self.drawer = drawer::Content::from(&self.theme_manager);
tasks.push(cosmic::task::message(
crate::app::Message::OpenContextDrawer(self.entity),
));
@ -289,8 +249,8 @@ impl Page {
#[cfg(feature = "wayland")]
tokio::task::spawn(async move {
Self::update_panel_radii(r);
Self::update_dock_padding(r);
Self::update_panel_radii(r);
});
}
@ -556,6 +516,86 @@ impl Page {
self.drawer.reset(&self.theme_manager);
}
}
Message::FocusFontInput => {
// retry until the widget is in the tree and focused or the dialog is removed.
if matches!(
self.context_view,
Some(ContextView::SystemFont | ContextView::MonospaceFont)
) {
return cosmic::iced::runtime::task::widget(
cosmic::iced::core::widget::operation::focusable::find_focused(),
)
.collect()
.then(|id| {
if id.first().is_some_and(|id| *id == FONT_SEARCH.clone()) {
Task::none()
} else {
focus(FONT_SEARCH.clone()).chain(task::message(Message::FocusFontInput))
}
});
}
}
Message::Blur(b) => {
let Ok(blur_strength) = b.try_into() else {
tracing::error!("Invalid blur strength: {b}");
return Task::none();
};
theme_staged = self.theme_manager.set_frosted(blur_strength).map(|_| {
self.drawer.reset(&self.theme_manager);
theme_manager::ThemeStaged::Current
});
tasks.push(self.drawer.update_blur(blur_strength as u8));
}
Message::Glass(glass) => {
let mut alpha_map = cosmic::cosmic_theme::AlphaMap::default();
let glass_offset_min = alpha_map.extremely_high_2;
let glass_offset_max = 1.0 - alpha_map.extremely_low;
let offset = (glass - 0.5) / 0.5
* if glass < 0.5 {
glass_offset_min
} else {
glass_offset_max
};
alpha_map = alpha_map.offset(offset);
// TODO optimize and apply to both light and dark themes
theme_staged = self.theme_manager.set_glass(alpha_map).map(|_| {
self.drawer.reset(&self.theme_manager);
theme_manager::ThemeStaged::Current
});
tasks.push(self.drawer.update_glass(glass));
}
Message::FrostedSystemInterface(v) => {
theme_staged = self.theme_manager.set_frosted_system_interface(v).map(|_| {
self.drawer.reset(&self.theme_manager);
theme_manager::ThemeStaged::Current
});
tasks.push(self.drawer.update_frosted_system_interface(v));
}
Message::FrostedWindows(v) => {
theme_staged = self.theme_manager.set_frosted_windows(v).map(|_| {
self.drawer.reset(&self.theme_manager);
theme_manager::ThemeStaged::Current
});
tasks.push(self.drawer.update_frosted_windows(v));
}
Message::FrostedPanel(v) => {
theme_staged = self.theme_manager.set_frosted_panel(v).map(|_| {
self.drawer.reset(&self.theme_manager);
theme_manager::ThemeStaged::Current
});
tasks.push(self.drawer.update_frosted_panel(v));
}
Message::FrostedApplets(v) => {
theme_staged = self.theme_manager.set_frosted_applets(v).map(|_| {
self.drawer.reset(&self.theme_manager);
theme_manager::ThemeStaged::Current
});
tasks.push(self.drawer.update_frosted_applets(v));
}
}
let mut tasks = cosmic::Task::batch(tasks);
@ -570,113 +610,66 @@ impl Page {
}
// TODO: cache panel and dock configs so that they needn't be re-read
#[cfg(feature = "wayland")]
fn load_panel_config(name: &str) -> Option<(Config, CosmicPanelConfig)> {
let helper = CosmicPanelConfig::cosmic_config(name).ok()?;
let config = CosmicPanelConfig::get_entry(&helper).ok()?;
(config.name == name).then_some((helper, config))
}
#[cfg(feature = "wayland")]
pub fn update_panel_radii(roundness: Roundness) {
let panel_config_helper = CosmicPanelConfig::cosmic_config("Panel").ok();
let dock_config_helper = CosmicPanelConfig::cosmic_config("Dock").ok();
let corner_radii: CornerRadii = roundness.into();
let radius = corner_radii.radius_xl[0] as u32;
let mut panel_config = panel_config_helper.as_ref().and_then(|config_helper| {
let panel_config = CosmicPanelConfig::get_entry(config_helper).ok()?;
(panel_config.name == "Panel").then_some(panel_config)
});
for name in ["Panel", "Dock"] {
let Some((helper, mut config)) = Self::load_panel_config(name) else {
continue;
};
let mut dock_config = dock_config_helper.as_ref().and_then(|config_helper| {
let panel_config = CosmicPanelConfig::get_entry(config_helper).ok()?;
(panel_config.name == "Dock").then_some(panel_config)
});
if let Some(panel_config_helper) = panel_config_helper.as_ref()
&& let Some(panel_config) = panel_config.as_mut()
{
let radii = if panel_config.anchor_gap {
let cornder_radii: CornerRadii = roundness.into();
cornder_radii.radius_xl[0] as u32
} else if matches!(roundness, Roundness::Round) && !panel_config.expand_to_edges {
12
let new_radius = if config.anchor_gap {
radius
} else if !config.expand_to_edges {
radius.min(12)
} else {
0
};
if let Err(why) = panel_config.set_border_radius(panel_config_helper, radii) {
tracing::error!(?why, "Error updating panel corner radii");
}
}
if let Some(dock_config_helper) = dock_config_helper.as_ref()
&& let Some(dock_config) = dock_config.as_mut()
{
let radii = if dock_config.anchor_gap {
let cornder_radii: CornerRadii = roundness.into();
cornder_radii.radius_xl[0] as u32
} else if matches!(roundness, Roundness::Round) && !dock_config.expand_to_edges {
12
} else {
0
};
if let Err(why) = dock_config.set_border_radius(dock_config_helper, radii) {
tracing::error!(?why, "Error updating dock corner radii");
if let Err(why) = config.set_border_radius(&helper, new_radius) {
tracing::error!(?why, "Error updating {name} corner radii");
}
}
}
#[cfg(feature = "wayland")]
pub fn update_dock_padding(roundness: Roundness) {
let dock_config_helper = CosmicPanelConfig::cosmic_config("Dock").ok();
let Some((helper, mut config)) = Self::load_panel_config("Dock") else {
return;
};
let mut dock_config = dock_config_helper.as_ref().and_then(|config_helper| {
let panel_config = CosmicPanelConfig::get_entry(config_helper).ok()?;
(panel_config.name == "Dock").then_some(panel_config)
});
let padding = match roundness {
Roundness::Round | Roundness::SlightlyRound => 4,
Roundness::Square => 0,
};
if let Some(dock_config_helper) = dock_config_helper.as_ref()
&& let Some(dock_config) = dock_config.as_mut()
{
let padding = match roundness {
Roundness::Round => 4,
Roundness::SlightlyRound => 4,
Roundness::Square => 0,
};
if let Err(why) = dock_config.set_padding(dock_config_helper, padding) {
tracing::error!(?why, "Error updating dock padding");
}
if let Err(why) = config.set_padding(&helper, padding) {
tracing::error!(?why, "Error updating dock padding");
}
}
// TODO: cache panel and dock configs so that they needn't be re-read
#[cfg(feature = "wayland")]
pub fn update_panel_spacing(density: Density) {
let spacing: cosmic::cosmic_theme::Spacing = density.into();
let space_none = spacing.space_none;
let panel_config_helper = CosmicPanelConfig::cosmic_config("Panel").ok();
let dock_config_helper = CosmicPanelConfig::cosmic_config("Dock").ok();
let mut panel_config = panel_config_helper.as_ref().and_then(|config_helper| {
let panel_config = CosmicPanelConfig::get_entry(config_helper).ok()?;
(panel_config.name == "Panel").then_some(panel_config)
});
let mut dock_config = dock_config_helper.as_ref().and_then(|config_helper| {
let panel_config = CosmicPanelConfig::get_entry(config_helper).ok()?;
(panel_config.name == "Dock").then_some(panel_config)
});
let space_none = spacing.space_none as u32;
if let Some(panel_config_helper) = panel_config_helper.as_ref()
&& let Some(panel_config) = panel_config.as_mut()
{
let update = panel_config.set_spacing(panel_config_helper, space_none as u32);
if let Err(err) = update {
tracing::error!(?err, "Error updating panel spacing");
for name in ["Panel", "Dock"] {
let Some((helper, mut config)) = Self::load_panel_config(name) else {
continue;
};
if let Err(err) = config.set_spacing(&helper, space_none) {
tracing::error!(?err, "Error updating {name} spacing");
}
};
if let Some(dock_config_helper) = dock_config_helper.as_ref()
&& let Some(dock_config) = dock_config.as_mut()
{
let update = dock_config.set_spacing(dock_config_helper, space_none as u32);
if let Err(err) = update {
tracing::error!(?err, "Error updating dock spacing");
}
};
}
}
fn can_reset(&self) -> bool {

View file

@ -1,9 +1,12 @@
use cosmic::iced::core::{Alignment, Length, text::Wrapping};
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::core::{Alignment, Length};
use cosmic::widget::icon::{from_name, icon};
use cosmic::widget::{button, container, settings, text};
use cosmic::{Apply, Element};
use cosmic_settings_page::Section;
use crate::pages::desktop::appearance::ContextView;
use super::{Message, Page, Roundness};
#[allow(clippy::too_many_lines)]
@ -12,6 +15,7 @@ pub fn section() -> Section<crate::pages::Message> {
round = fl!("style", "round");
slightly_round = fl!("style", "slightly-round");
square = fl!("style", "square");
frosted_glass = fl!("style", "frosted-glass");
});
let dark_round_style = from_name("illustration-appearance-dark-style-round").handle();
@ -136,6 +140,10 @@ pub fn section() -> Section<crate::pages::Message> {
)
.center_x(Length::Fill),
)
.add(crate::widget::go_next_item(
&descriptions[frosted_glass],
Message::DrawerOpen(ContextView::FrostedGlass),
))
.apply(Element::from)
.map(crate::pages::Message::Appearance)
})

View file

@ -1,8 +1,8 @@
use cosmic::cosmic_config::{Config, ConfigSet, CosmicConfigEntry};
use cosmic::cosmic_theme::palette::{Srgb, Srgba};
use cosmic::cosmic_theme::{
CornerRadii, DARK_THEME_BUILDER_ID, LIGHT_THEME_BUILDER_ID, Spacing, Theme, ThemeBuilder,
ThemeMode,
AlphaMap, BlurStrength, CornerRadii, DARK_THEME_BUILDER_ID, LIGHT_THEME_BUILDER_ID, Spacing,
Theme, ThemeBuilder, ThemeMode,
};
use cosmic::iced::core::Color;
@ -74,7 +74,7 @@ impl From<(Option<Config>, Option<Config>, Option<Vec<Srgba>>)> for ThemeCustomi
.success(theme.success.base.color)
.warning(theme.warning.base.color)
.neutral_tint(theme.palette.neutral_5.color)
.text_tint(theme.background.on.color);
.text_tint(theme.background(false).on.color);
theme_builder.gaps = theme.gaps;
@ -160,20 +160,6 @@ impl Default for Manager {
impl Manager {
pub fn build_theme(&mut self, stage: ThemeStaged) -> Task<app::Message> {
macro_rules! theme_transaction {
($config:ident, $current_theme:ident, $new_theme:ident, { $($name:ident;)+ }) => {
let tx = $config.transaction();
$(
if $current_theme.$name != $new_theme.$name {
_ = tx.set(stringify!($name), $new_theme.$name.clone());
}
)+
_ = tx.commit();
}
}
let map_data_fn = |customizer: &ThemeCustomizer| {
(customizer.builder.0.clone(), customizer.theme.1.clone())
};
@ -199,32 +185,80 @@ impl Manager {
cosmic::task::future(async move {
for (builder, config) in data.by_ref() {
if let Some(config) = config {
let current_theme = match Theme::get_entry(&config) {
let cur = match Theme::get_entry(&config) {
Ok(theme) => theme,
Err((_errs, theme)) => theme,
};
let new_theme = builder.build();
theme_transaction!(config, current_theme, new_theme, {
accent;
accent_button;
background;
button;
destructive;
destructive_button;
link_button;
icon_button;
palette;
primary;
secondary;
shade;
success;
text_button;
warning;
warning_button;
window_hint;
accent_text;
});
let new = builder.build();
// do this without the macro
let tx = config.transaction();
if cur.accent != new.accent {
_ = tx.set("accent", new.accent.clone());
}
if cur.accent_button != new.accent_button {
_ = tx.set("accent_button", new.accent_button.clone());
}
if cur.background(false) != new.background(false) {
_ = tx.set("background", new.background(false).clone());
}
if cur.background(true) != new.background(true) {
_ = tx.set("transparent_background", new.background(true).clone());
}
if cur.button != new.button {
_ = tx.set("button", new.button.clone());
}
if cur.destructive != new.destructive {
_ = tx.set("destructive", new.destructive.clone());
}
if cur.destructive_button != new.destructive_button {
_ = tx.set("destructive_button", new.destructive_button.clone());
}
if cur.link_button != new.link_button {
_ = tx.set("link_button", new.link_button.clone());
}
if cur.icon_button != new.icon_button {
_ = tx.set("icon_button", new.icon_button.clone());
}
if cur.palette != new.palette {
_ = tx.set("palette", new.palette.clone());
}
if cur.primary(false) != new.primary(false) {
_ = tx.set("primary", new.primary(false).clone());
}
if cur.secondary(false) != new.secondary(false) {
_ = tx.set("secondary", new.secondary(false).clone());
}
if cur.primary(true) != new.primary(true) {
_ = tx.set("transparent_primary", new.primary(true).clone());
}
if cur.secondary(true) != new.secondary(true) {
_ = tx.set("transparent_secondary", new.secondary(true).clone());
}
if cur.shade != new.shade {
_ = tx.set("shade", new.shade.clone());
}
if cur.success != new.success {
_ = tx.set("success", new.success.clone());
}
if cur.text_button != new.text_button {
_ = tx.set("text_button", new.text_button.clone());
}
if cur.warning != new.warning {
_ = tx.set("warning", new.warning.clone());
}
if cur.warning_button != new.warning_button {
_ = tx.set("warning_button", new.warning_button.clone());
}
if cur.window_hint != new.window_hint {
_ = tx.set("window_hint", new.window_hint.clone());
}
if cur.accent_text != new.accent_text {
_ = tx.set("accent_text", new.accent_text.clone());
}
_ = tx.commit();
}
}
@ -360,6 +394,39 @@ impl Manager {
Some(ThemeStaged::Both)
}
pub fn set_frosted(&mut self, frosted: BlurStrength) -> Option<ThemeStaged> {
self.dark.set_frosted(frosted)?;
self.light.set_frosted(frosted)?;
Some(ThemeStaged::Both)
}
pub fn set_glass(&mut self, alpha_map: AlphaMap) -> Option<ThemeStaged> {
self.dark.set_glass(alpha_map)?;
self.light.set_glass(alpha_map)?;
Some(ThemeStaged::Both)
}
pub fn set_frosted_panel(&mut self, enabled: bool) -> Option<ThemeStaged> {
self.dark.set_frosted_panel(enabled)?;
self.light.set_frosted_panel(enabled)?;
Some(ThemeStaged::Both)
}
pub fn set_frosted_applets(&mut self, enabled: bool) -> Option<ThemeStaged> {
self.dark.set_frosted_applets(enabled)?;
self.light.set_frosted_applets(enabled)?;
Some(ThemeStaged::Both)
}
pub fn set_frosted_system_interface(&mut self, enabled: bool) -> Option<ThemeStaged> {
self.dark.set_frosted_system_interface(enabled)?;
self.light.set_frosted_system_interface(enabled)?;
Some(ThemeStaged::Both)
}
pub fn set_frosted_windows(&mut self, enabled: bool) -> Option<ThemeStaged> {
self.dark.set_frosted_windows(enabled)?;
self.light.set_frosted_windows(enabled)?;
Some(ThemeStaged::Both)
}
pub fn get_color(&self, context: &ContextView) -> Option<Color> {
match *context {
ContextView::CustomAccent => self.custom_accent().map(Color::from),
@ -540,6 +607,81 @@ impl ThemeCustomizer {
Some(ThemeStaged::Current)
}
pub fn set_frosted(&mut self, frosted: BlurStrength) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;
self.builder.0.set_frosted(config, frosted).ok()?;
self.theme
.0
.set_frosted(self.theme.1.as_ref()?, frosted)
.ok()?;
Some(ThemeStaged::Current)
}
pub fn set_glass(&mut self, alpha_map: AlphaMap) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;
self.builder.0.set_alpha_map(config, alpha_map).ok()?;
self.theme
.0
.set_alpha_map(self.theme.1.as_ref()?, alpha_map)
.ok()?;
Some(ThemeStaged::Current)
}
pub fn set_frosted_windows(&mut self, enabled: bool) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;
self.builder.0.set_frosted_windows(config, enabled).ok()?;
self.theme
.0
.set_frosted_windows(self.theme.1.as_ref()?, enabled)
.ok()?;
Some(ThemeStaged::Current)
}
pub fn set_frosted_system_interface(&mut self, enabled: bool) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;
self.builder
.0
.set_frosted_system_interface(config, enabled)
.ok()?;
self.theme
.0
.set_frosted_system_interface(self.theme.1.as_ref()?, enabled)
.ok()?;
Some(ThemeStaged::Current)
}
pub fn set_frosted_applets(&mut self, enabled: bool) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;
self.builder.0.set_frosted_applets(config, enabled).ok()?;
self.theme
.0
.set_frosted_applets(self.theme.1.as_ref()?, enabled)
.ok()?;
Some(ThemeStaged::Current)
}
pub fn set_frosted_panel(&mut self, enabled: bool) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;
self.builder.0.set_frosted_panel(config, enabled).ok()?;
self.theme
.0
.set_frosted_panel(self.theme.1.as_ref()?, enabled)
.ok()?;
Some(ThemeStaged::Current)
}
// set active hints is set on all themes to be consistent between dark & light themes.
pub fn set_active_hint(&mut self, active_hint: u32) -> Option<ThemeStaged> {
let config = self.builder.1.as_ref()?;

View file

@ -1,22 +1,16 @@
use cosmic::{
Apply, Element, Task,
app::ContextDrawer,
cosmic_config::CosmicConfigEntry,
iced::{Alignment, Length},
widget::{button, container, row},
};
use cosmic::app::ContextDrawer;
use cosmic::cosmic_config::CosmicConfigEntry;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{button, container, row};
use cosmic::{Apply, Element, Task};
use cosmic_panel_config::CosmicPanelConfig;
use cosmic_settings_page::{self as page, Section, section};
use slotmap::{Key, SlotMap};
use std::borrow::Cow;
use crate::{
app,
pages::{
self,
desktop::panel::applets_inner::{self, AppletsPage, ContextDrawerVariant, lists},
},
};
use crate::app;
use crate::pages::desktop::panel::applets_inner::{self, AppletsPage, ContextDrawerVariant, lists};
use crate::pages::{self};
pub(crate) struct Page {
inner: applets_inner::Page,

View file

@ -1,11 +1,8 @@
use std::collections::HashMap;
use cosmic::Apply;
use cosmic::{
Element, Task,
cosmic_config::{ConfigSet, CosmicConfigEntry},
widget::{settings, text},
};
use cosmic::cosmic_config::{ConfigSet, CosmicConfigEntry};
use cosmic::widget::{settings, text};
use cosmic::{Apply, Element, Task};
use cosmic_panel_config::{CosmicPanelConfig, CosmicPanelContainerConfig};
use cosmic_settings_page::{self as page, Section, section};
use slotmap::SlotMap;

View file

@ -10,23 +10,21 @@ use cosmic::iced;
use cosmic::iced::core::clipboard::IconSurface;
use cosmic::widget::{Column, button, column, container, icon, list_column, row, text, text_input};
use cosmic::{
Apply, Element,
cosmic_config::{Config, CosmicConfigEntry},
iced::core::{
Clipboard, Shell, Widget, layout, renderer,
widget::{Operation, Tree, tree},
},
iced::runtime::{Task, core::id::Id},
iced::{
Alignment, Border, Color, Length, Point, Rectangle, Size, Vector, core::window, event,
mouse, overlay, touch,
},
theme,
use cosmic::cosmic_config::{Config, CosmicConfigEntry};
use cosmic::iced::core::widget::{Operation, Tree, tree};
use cosmic::iced::core::{Clipboard, Shell, Widget, layout, renderer, window};
use cosmic::iced::runtime::Task;
use cosmic::iced::runtime::core::id::Id;
use cosmic::iced::{
Alignment, Border, Color, Length, Point, Rectangle, Size, Vector, event, mouse, overlay, touch,
};
use cosmic::{Apply, Element, theme};
use std::borrow::Cow;
use std::fmt::Debug;
use std::mem;
use std::path::{Path, PathBuf};
use std::{borrow::Cow, fmt::Debug, mem, sync::LazyLock};
use std::sync::LazyLock;
use crate::{app, pages};
use cosmic_panel_config::CosmicPanelConfig;

View file

@ -1,12 +1,11 @@
use cosmic::{
Element, Task,
cctk::sctk::reexports::client::{Proxy, backend::ObjectId, protocol::wl_output::WlOutput},
cosmic_config::{self, CosmicConfigEntry},
cosmic_theme::Density,
iced::{Alignment, Length},
surface,
widget::{button, container, dropdown, row, settings, slider, space, text},
};
use cosmic::cctk::sctk::reexports::client::Proxy;
use cosmic::cctk::sctk::reexports::client::backend::ObjectId;
use cosmic::cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
use cosmic::cosmic_config::{self, CosmicConfigEntry};
use cosmic::cosmic_theme::{Density, Roundness};
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{button, container, dropdown, row, settings, slider, space, text};
use cosmic::{Element, Task, surface};
use cosmic::Apply;
use cosmic_config::ConfigSet;
@ -15,16 +14,15 @@ use cosmic_panel_config::{
CosmicPanelOuput, PanelAnchor, PanelSize,
};
use cosmic_settings_page::{self as page, Section};
use std::{collections::HashMap, time::Duration};
use crate::pages::desktop::appearance::Roundness;
use std::collections::HashMap;
use std::time::Duration;
pub struct PageInner {
pub(crate) config_helper: Option<cosmic_config::Config>,
pub(crate) panel_config: Option<CosmicPanelConfig>,
pub size: Option<PanelSize>,
pub opacity: f32,
pub opacity_changing: bool,
pub size: PanelSize,
pub outputs: Vec<String>,
pub anchors: Vec<String>,
pub backgrounds: Vec<String>,
@ -40,9 +38,9 @@ impl Default for PageInner {
Self {
config_helper: Option::default(),
panel_config: Option::default(),
size: Option::default(),
opacity: 0.0,
opacity_changing: false,
size: PanelSize::M,
outputs: vec![fl!("all-displays")],
anchors: vec![
Anchor(PanelAnchor::Left).to_string(),
@ -120,7 +118,7 @@ pub(crate) fn behavior_and_position<
.title(&section.title)
.add(
settings::item::builder(&descriptions[autohide_label])
.toggler(panel_config.autohide.is_some(), Message::AutoHidePanel),
.toggler(panel_config.autohide_enabled(), Message::AutoHidePanel),
)
.add(settings::item(
&descriptions[position],
@ -209,9 +207,7 @@ pub(crate) fn style<
text::body(fl!("small")).into(),
slider(
0..=4,
match inner.size.as_ref().expect(
"PageInner.size is None even though PageInner.panel_config is Some",
) {
match inner.size {
PanelSize::XS => 0,
PanelSize::S => 1,
PanelSize::M => 2,
@ -506,7 +502,7 @@ impl PageInner {
if let Err(err) = default.write_entry(config) {
tracing::error!(?err, "Error resetting panel config.");
}
self.size = Some(default.size.clone());
self.size.clone_from(&default.size);
self.system_default = Some(default.clone());
self.panel_config.clone_from(&self.system_default);
} else {
@ -546,18 +542,10 @@ impl PageInner {
Message::AutoHidePanel(enabled) => {
if enabled {
_ = panel_config.set_exclusive_zone(helper, false);
_ = panel_config.set_autohide(
helper,
Some(AutoHide {
wait_time: 1000,
transition_time: 200,
handle_size: 4,
unhide_delay: 200,
}),
);
_ = panel_config.set_autohide(helper, AutoHide::OnOverlap);
} else {
_ = panel_config.set_exclusive_zone(helper, true);
_ = panel_config.set_autohide(helper, None);
_ = panel_config.set_autohide(helper, AutoHide::Never);
}
}
Message::PanelAnchor(i) => {
@ -591,30 +579,21 @@ impl PageInner {
}
let theme = cosmic::theme::system_preference();
let theme = theme.cosmic();
let radius = theme.corner_radii;
let roundness: Roundness = radius.into();
let new_radius;
if enabled {
let radii = theme.corner_radii.radius_xl[0] as u32;
new_radius = radii;
} else if matches!(roundness, Roundness::Round) && !panel_config.expand_to_edges {
new_radius = 12;
let radius = theme.corner_radii.radius_xl[0] as u32;
let new_radius = if enabled {
radius
} else if !panel_config.expand_to_edges {
radius.min(12)
} else {
new_radius = 0;
}
0
};
_ = panel_config.set_border_radius(helper, new_radius).unwrap();
}
Message::PanelSize(size) => {
self.size = Some(size);
self.size = size;
}
Message::PanelSizeCommit => {
_ = panel_config.set_size(
helper,
self.size
.as_ref()
.expect("PageInner.size is None even though it should be Some, since PageInner.panel_config is Some")
.clone()
);
_ = panel_config.set_size(helper, self.size.clone());
// Reset any size overrides the user might have set
_ = panel_config.set_size_center(helper, None);
_ = panel_config.set_size_wings(helper, None);
@ -632,17 +611,14 @@ impl PageInner {
let theme = cosmic::theme::system_preference();
let theme = theme.cosmic();
let radius = theme.corner_radii;
let roundness: Roundness = radius.into();
let new_radius;
if panel_config.anchor_gap {
let radii = theme.corner_radii.radius_xl[0] as u32;
new_radius = radii;
} else if matches!(roundness, Roundness::Round) && !enabled {
new_radius = 12;
let radius = theme.corner_radii.radius_xl[0] as u32;
let new_radius = if panel_config.anchor_gap {
radius
} else if !enabled {
radius.min(12)
} else {
new_radius = 0;
}
0
};
_ = panel_config.set_border_radius(helper, new_radius).unwrap();
}
Message::OpacityRequest(opacity) => {
@ -677,7 +653,7 @@ impl PageInner {
}
}
Message::PanelConfig(c) => {
self.size = Some(c.size.clone());
self.size = c.size.clone();
self.panel_config = Some(*c);
return Task::none();
}

View file

@ -1,6 +1,7 @@
use std::collections::HashMap;
use cosmic::{Task, cosmic_config::CosmicConfigEntry};
use cosmic::Task;
use cosmic::cosmic_config::CosmicConfigEntry;
use cosmic_panel_config::{CosmicPanelConfig, CosmicPanelContainerConfig};
use cosmic_settings_page::{self as page, Section, section};
use slotmap::SlotMap;
@ -79,7 +80,6 @@ impl Default for Page {
// If the config is not present, it will be created with the default values and the name will not match
(panel_config.name == "Panel").then_some(panel_config)
});
let size = panel_config.as_ref().map(|c| c.size.clone());
let system_default = cosmic::cosmic_config::Config::system(
&format!("{}.Panel", cosmic_panel_config::NAME),
CosmicPanelConfig::VERSION,
@ -99,7 +99,6 @@ impl Default for Page {
inner: PageInner {
config_helper,
panel_config,
size,
container_config,
outputs_map: HashMap::new(),
system_default,

View file

@ -7,37 +7,25 @@ pub mod widgets;
pub use config::Config;
use url::Url;
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use cosmic::app::ContextDrawer;
#[cfg(feature = "xdg-portal")]
use cosmic::dialog::file_chooser;
use cosmic::{
Apply, Element, Task,
iced::core::text::{Ellipsize, EllipsizeHeightLimit},
widget::{ColorPickerModel, color_picker::ColorPickerUpdate, icon},
};
use cosmic::{app::ContextDrawer, iced::runtime::core::image::Handle as ImageHandle};
use cosmic::{
iced::Subscription,
widget::{
button, dropdown, list_column, row,
segmented_button::{self, SingleSelectModel},
settings,
space::horizontal as horizontal_space,
tab_bar, text,
},
};
use cosmic::{
iced::{Alignment, Color, Length, window},
surface,
use cosmic::iced::core::text::{Ellipsize, EllipsizeHeightLimit};
use cosmic::iced::runtime::core::image::Handle as ImageHandle;
use cosmic::iced::{Alignment, Color, Length, Subscription, window};
use cosmic::widget::color_picker::ColorPickerUpdate;
use cosmic::widget::segmented_button::{self, SingleSelectModel};
use cosmic::widget::space::horizontal as horizontal_space;
use cosmic::widget::{
ColorPickerModel, button, dropdown, icon, list_column, row, settings, tab_bar, text,
};
use cosmic::{Apply, Element, Task, surface};
use cosmic_bg_config::Source;
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use cosmic_settings_wallpaper::{self as wallpaper, Entry, ScalingMode};
use image::imageops::FilterType::Lanczos3;
use image::{ImageBuffer, Rgba};

View file

@ -3,8 +3,8 @@
use super::Message;
use cosmic::iced::Radius;
use cosmic::iced::core::Border;
use cosmic::iced::core::{Background, Color, Degrees, Length, gradient::Linear};
use cosmic::iced::core::gradient::Linear;
use cosmic::iced::core::{Background, Border, Color, Degrees, Length};
use cosmic::iced::runtime::core::image::Handle as ImageHandle;
use cosmic::prelude::*;
use cosmic::widget::{Space, button, container};

View file

@ -1,18 +1,14 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{
Apply, Element,
iced::Length,
surface,
widget::{self, settings},
};
use cosmic::iced::Length;
use cosmic::widget::{self, settings};
use cosmic::{Apply, Element, surface};
use cosmic_comp_config::CosmicCompConfig;
use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_config::{Action, Binding, Shortcuts, shortcuts};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use slotmap::SlotMap;
use tracing::error;
@ -95,7 +91,7 @@ impl Default for Page {
fl!("super-key", "launcher"),
fl!("super-key", "workspaces"),
fl!("super-key", "applications"),
fl!("super-key", "disable"),
fl!("super-key", "none"),
],
super_key_active: super_key_active_config(),
comp_config,
@ -233,7 +229,7 @@ pub fn window_management() -> Section<crate::pages::Message> {
_launcher = fl!("super-key", "launcher");
_workspaces = fl!("super-key", "workspaces");
_applications = fl!("super-key", "applications");
_disable = fl!("super-key", "disable");
_none = fl!("super-key", "none");
edge_gravity = fl!("edge-gravity");
});

View file

@ -3,15 +3,11 @@
// TODO make settings work
use cosmic::{
Apply, Element,
cosmic_config::{self, ConfigGet, ConfigSet},
surface,
widget::{self, settings},
};
use cosmic::cosmic_config::{self, ConfigGet, ConfigSet};
use cosmic::widget::{self, settings};
use cosmic::{Apply, Element, surface};
use cosmic_comp_config::workspace::{Action, WorkspaceConfig, WorkspaceLayout, WorkspaceMode};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use slotmap::SlotMap;
use tracing::error;

View file

@ -5,11 +5,9 @@ use cosmic::Renderer;
use cosmic::iced::core::renderer::Quad;
use cosmic::iced::core::widget::{Tree, tree};
use cosmic::iced::core::{
self as core, Border, Clipboard, Element, Layout, Length, Rectangle, Renderer as IcedRenderer,
Shell, Size, Widget,
self as core, Border, Clipboard, Element, Layout, Length, Point, Rectangle,
Renderer as IcedRenderer, Shell, Size, Widget, alignment, layout, mouse, renderer, text, touch,
};
use cosmic::iced::core::{Point, layout, mouse, renderer, touch};
use cosmic::iced::core::{alignment, text};
use cosmic::widget::segmented_button::{self, SingleSelectModel};
use cosmic_randr_shell::{self as randr, OutputKey};
use randr::Transform;

View file

@ -20,9 +20,10 @@ use cosmic_settings_page::{self as page, Section, section};
use futures::SinkExt;
use indexmap::Equivalent;
use slotmap::{Key, SecondaryMap, SlotMap};
use std::collections::BTreeMap;
use std::process::ExitStatus;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, LazyLock};
use std::{collections::BTreeMap, process::ExitStatus};
use tokio::sync::oneshot;
static DPI_SCALES: &[u32] = &[50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300];
@ -912,8 +913,8 @@ impl Page {
Some(orientation) => self.set_dialog(
Randr::Transform(match orientation {
1 => Transform::Rotate90,
2 => Transform::Flipped180,
3 => Transform::Flipped270,
2 => Transform::Rotate180,
3 => Transform::Rotate270,
_ => Transform::Normal,
}),
&request,

View file

@ -5,50 +5,96 @@ pub mod shortcuts;
use std::cmp;
use cosmic::{
Apply, Element, Task,
app::{ContextDrawer, context_drawer},
cosmic_config::{self, ConfigSet},
iced::{Alignment, Length},
theme,
widget::{self, ListColumn, button, container, icon, list, row, settings},
};
use cosmic::app::{ContextDrawer, context_drawer};
use cosmic::cosmic_config::{self, ConfigSet};
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{self, ListColumn, button, container, icon, list, row, settings};
use cosmic::{Apply, Element, Task, theme};
use cosmic_comp_config::{KeyboardConfig, NumlockState, XkbConfig};
use cosmic_settings_page::{self as page, Section, section};
use itertools::Itertools;
use slotmap::{DefaultKey, Key, SlotMap};
static COMPOSE_OPTIONS: &[(&str, &str)] = &[
// ("Left Alt", "compose:lalt"), XXX?
("Right Alt", "compose:ralt"),
("Left Super", "compose:lwin"),
("Right Super", "compose:rwin"),
("Menu key", "compose:menu"),
("Right Ctrl", "compose:rctrl"),
("Caps Lock", "compose:caps"),
("Scroll Lock", "compose:sclk"),
("Print Screen", "compose:prsc"),
];
/// Contains all options for mapping a [SpecialKey].
/// The available options differ for each key being mapped.
enum SpecialKeyAlternative {
AltLeft,
AltRight,
Ctrl,
CtrlRight,
SwapWithCtrl,
Super,
SuperLeft,
SuperRight,
CapsLock,
ScrollLock,
MenuKey,
Backspace,
Escape,
SwapWithEscape,
PrintScreen,
None,
}
impl SpecialKeyAlternative {
fn display_text(&self) -> String {
match self {
Self::AltLeft => fl!("keyboard-special-char", "alt-left"),
Self::AltRight => fl!("keyboard-special-char", "alt-right"),
Self::Ctrl => fl!("keyboard-special-char", "ctrl"),
Self::CtrlRight => fl!("keyboard-special-char", "ctrl-right"),
Self::SwapWithCtrl => fl!("keyboard-special-char", "swap-with-ctrl"),
Self::Super => fl!("keyboard-special-char", "super"),
Self::SuperLeft => fl!("keyboard-special-char", "super-left"),
Self::SuperRight => fl!("keyboard-special-char", "super-right"),
Self::CapsLock => fl!("keyboard-special-char", "caps"),
Self::ScrollLock => fl!("keyboard-special-char", "scroll-lock"),
Self::MenuKey => fl!("keyboard-special-char", "menu"),
Self::Backspace => fl!("keyboard-special-char", "backspace"),
Self::Escape => fl!("keyboard-special-char", "escape"),
Self::SwapWithEscape => fl!("keyboard-special-char", "swap-with-escape"),
Self::PrintScreen => fl!("keyboard-special-char", "print-screen"),
Self::None => fl!("keyboard-special-char", "none"),
}
}
}
static ALTERNATE_CHARACTER_OPTIONS: &[(&str, &str)] = &[
("Left Alt", "lv3:lalt_switch"),
("Right Alt", "lv3:ralt_switch"),
("Left Super", "lv3:lwin_switch"),
("Right Super", "lv3:rwin_switch"),
("Menu key", "lv3:menu_switch"),
/// The second/right value is the key identifier, which is how it will be saved to the configuration.
/// These values are different for each [SpecialKey], even for the same [SpecialKeyAlternative].
/// The identifier starts with a prefix defined by [SpecialKey::prefixes].
type SpecialKeyOption = (SpecialKeyAlternative, &'static str);
/// Available options for [SpecialKey::Compose].
static COMPOSE_OPTIONS: &[SpecialKeyOption] = &[
// ("Left Alt", "compose:lalt"), XXX?
(SpecialKeyAlternative::AltRight, "compose:ralt"),
(SpecialKeyAlternative::SuperLeft, "compose:lwin"),
(SpecialKeyAlternative::SuperRight, "compose:rwin"),
(SpecialKeyAlternative::MenuKey, "compose:menu"),
(SpecialKeyAlternative::CtrlRight, "compose:rctrl"),
(SpecialKeyAlternative::CapsLock, "compose:caps"),
(SpecialKeyAlternative::ScrollLock, "compose:sclk"),
(SpecialKeyAlternative::PrintScreen, "compose:prsc"),
];
/// Available options for [SpecialKey::AlternateCharacters].
static ALTERNATE_CHARACTER_OPTIONS: &[SpecialKeyOption] = &[
(SpecialKeyAlternative::AltLeft, "lv3:lalt_switch"),
(SpecialKeyAlternative::AltRight, "lv3:ralt_switch"),
(SpecialKeyAlternative::SuperLeft, "lv3:lwin_switch"),
(SpecialKeyAlternative::SuperRight, "lv3:rwin_switch"),
(SpecialKeyAlternative::MenuKey, "lv3:menu_switch"),
// ("Right Ctrl", "lv3:"), XXX
("Caps Lock", "lv3:caps_switch"),
(SpecialKeyAlternative::CapsLock, "lv3:caps_switch"),
// ("Scroll Lock", "lv3:"), XXX
// ("Print Screen", "lv3"), XXX
];
static CAPS_LOCK_OPTIONS: &[(&str, &str)] = &[
("Escape", "caps:escape"),
("Swap with Escape", "caps:swapescape"),
("Backspace", "caps:backspace"),
("Super", "caps:super"),
("Control", "caps:ctrl_modifier"),
("Swap with Control", "ctrl:swapcaps"),
/// Available options for [SpecialKey::CapsLock].
static CAPS_LOCK_OPTIONS: &[SpecialKeyOption] = &[
(SpecialKeyAlternative::Escape, "caps:escape"),
(SpecialKeyAlternative::SwapWithEscape, "caps:swapescape"),
(SpecialKeyAlternative::Backspace, "caps:backspace"),
(SpecialKeyAlternative::Super, "caps:super"),
(SpecialKeyAlternative::Ctrl, "caps:ctrl_modifier"),
(SpecialKeyAlternative::SwapWithCtrl, "ctrl:swapcaps"),
];
#[derive(Clone, Debug)]
@ -146,9 +192,9 @@ pub enum SpecialKey {
impl SpecialKey {
pub fn title(self) -> String {
match self {
Self::Compose => "Compose".to_string(),
Self::AlternateCharacters => "Alternate Characters".to_string(),
Self::CapsLock => "Caps Lock".to_string(),
Self::Compose => fl!("keyboard-special-char", "compose"),
Self::AlternateCharacters => fl!("keyboard-special-char", "alternate"),
Self::CapsLock => fl!("keyboard-special-char", "caps"),
}
}
@ -241,7 +287,7 @@ fn input_source(
}
fn special_char_radio_row<'a>(
desc: &'a str,
desc: String,
value: Option<&'static str>,
current_value: Option<&'a str>,
) -> list::ListButton<'a, Message> {
@ -621,14 +667,22 @@ impl Page {
let mut list = cosmic::widget::list_column();
if matches!(special_key, SpecialKey::CapsLock) {
list = list.add(special_char_radio_row("Caps Lock", None, current));
list = list.add(special_char_radio_row(
SpecialKeyAlternative::CapsLock.display_text(),
None,
current,
));
} else {
list = list.add(special_char_radio_row("None", None, current));
list = list.add(special_char_radio_row(
SpecialKeyAlternative::None.display_text(),
None,
current,
));
}
list = options
.iter()
.map(|(desc, id)| special_char_radio_row(desc, Some(id), current))
.map(|(key, id)| special_char_radio_row(key.display_text(), Some(id), current))
.fold(list, ListColumn::add);
widget::column::with_capacity(2)

View file

@ -8,7 +8,7 @@ use cosmic::iced::keyboard::{Key, Location, Modifiers};
use cosmic::iced::platform_specific::shell::wayland::commands::keyboard_shortcuts_inhibit;
use cosmic::iced::platform_specific::shell::wayland::keymap;
use cosmic::iced::{self, Alignment, Length};
use cosmic::widget::{self, button, icon, settings, text};
use cosmic::widget::{self, button, icon, list, settings, text};
use cosmic::{Apply, Element, Task, theme};
use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_config::shortcuts::{self, Action, Binding, Shortcuts};
@ -652,7 +652,8 @@ impl Model {
modifiers,
..
}) => {
use cosmic::iced::keyboard::{Key, key::Named};
use cosmic::iced::keyboard::Key;
use cosmic::iced::keyboard::key::Named;
if matches!(
key,
@ -669,7 +670,8 @@ impl Model {
location,
..
}) => {
use cosmic::iced::keyboard::{Key, key::Named};
use cosmic::iced::keyboard::Key;
use cosmic::iced::keyboard::key::Named;
if matches!(
key,
@ -865,13 +867,11 @@ fn context_drawer<'a>(
}
/// Display a shortcut as a list item
fn shortcut_item(custom: bool, id: usize, data: &ShortcutModel) -> Element<'_, ShortcutMessage> {
#[derive(Copy, Clone, Debug)]
enum LocalMessage {
Remove,
Show,
}
fn shortcut_item(
custom: bool,
id: usize,
data: &ShortcutModel,
) -> list::ListButton<'_, ShortcutMessage> {
let bindings = data
.bindings
.iter()
@ -880,7 +880,7 @@ fn shortcut_item(custom: bool, id: usize, data: &ShortcutModel) -> Element<'_, S
.map(|(_, shortcut)| text::body(shortcut.binding.to_string()).into())
.collect::<Vec<_>>();
let shortcuts: Element<LocalMessage> = if bindings.is_empty() {
let shortcuts: Element<ShortcutMessage> = if bindings.is_empty() {
text::body(fl!("disabled")).into()
} else {
widget::column::with_children(bindings)
@ -900,7 +900,7 @@ fn shortcut_item(custom: bool, id: usize, data: &ShortcutModel) -> Element<'_, S
.push(icon::from_name("go-next-symbolic").size(16))
.push_maybe(custom.then(|| {
widget::button::icon(icon::from_name("edit-delete-symbolic"))
.on_press(LocalMessage::Remove)
.on_press(ShortcutMessage::DeleteShortcut(id))
}))
.align_y(Alignment::Center)
.spacing(8);
@ -909,14 +909,7 @@ fn shortcut_item(custom: bool, id: usize, data: &ShortcutModel) -> Element<'_, S
.flex_control(control)
.align_items(Alignment::Center)
.spacing(16)
.apply(widget::container)
.class(theme::Container::List)
.apply(widget::button::custom)
.class(theme::Button::Transparent)
.on_press(LocalMessage::Show)
.apply(Element::from)
.map(move |message| match message {
LocalMessage::Show => ShortcutMessage::ShowShortcut(id, data.description.clone()),
LocalMessage::Remove => ShortcutMessage::DeleteShortcut(id),
})
.width(Length::Shrink)
.apply(list::button)
.on_press(ShortcutMessage::ShowShortcut(id, data.description.clone()))
}

View file

@ -590,7 +590,8 @@ impl page::Page<crate::pages::Message> for Page {
&self,
core: &cosmic::Core,
) -> cosmic::iced::Subscription<crate::pages::Message> {
use cosmic::iced::{self, event::listen_with};
use cosmic::iced::event::listen_with;
use cosmic::iced::{self};
cosmic::iced::Subscription::batch(vec![
if self.add_shortcut.active
@ -605,7 +606,8 @@ impl page::Page<crate::pages::Message> for Page {
modifiers,
..
}) => {
use cosmic::iced::keyboard::{Key, key::Named};
use cosmic::iced::keyboard::Key;
use cosmic::iced::keyboard::key::Named;
if matches!(
key,
Key::Named(Named::Super | Named::Alt | Named::Control | Named::Shift)
@ -624,7 +626,8 @@ impl page::Page<crate::pages::Message> for Page {
location,
..
}) => {
use cosmic::iced::keyboard::{Key, key::Named};
use cosmic::iced::keyboard::Key;
use cosmic::iced::keyboard::key::Named;
if matches!(
key,
Key::Named(Named::Super | Named::Alt | Named::Control | Named::Shift)

View file

@ -2,8 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use super::{ShortcutMessage, ShortcutModel};
use cosmic::app::ContextDrawer;
use cosmic::iced::platform_specific::shell::wayland::commands::keyboard_shortcuts_inhibit;
use cosmic::{Element, Task, app::ContextDrawer};
use cosmic::{Element, Task};
use cosmic_settings_config::shortcuts::Action;
use cosmic_settings_config::shortcuts::action::ResizeDirection;
use cosmic_settings_page::{self as page, Section, section};

View file

@ -15,16 +15,16 @@ pub mod tiling;
use cosmic::app::ContextDrawer;
use cosmic::iced::Length;
use cosmic::widget::{self, icon, settings, text};
use cosmic::{Apply, Element, Task, theme};
use cosmic::widget::list::ListButton;
use cosmic::widget::{self, settings, text};
use cosmic::{Apply, Element, Task};
use cosmic_config::ConfigGet;
use cosmic_settings_config::Binding;
use cosmic_settings_config::shortcuts::action::{
Direction, FocusDirection, Orientation, ResizeDirection,
};
use cosmic_settings_config::shortcuts::{self, Action, Shortcuts};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use itertools::Itertools;
use shortcuts::action::System as SystemAction;
use slab::Slab;
@ -222,11 +222,13 @@ impl Page {
self.search.localized = SecondaryMap::new();
self.search.input = String::new();
self.search_model.on_clear();
self.modified.custom = 0;
self.modified.accessibility = 0;
self.modified.manage_windows = 0;
self.modified.move_windows = 0;
self.modified.nav = 0;
self.modified.system = 0;
self.modified.window_tiling = 0;
self.modified.custom = 0;
}
fn reload_search(&mut self) {
@ -408,11 +410,13 @@ impl Search {
.fold(
Slab::with_capacity(self.actions.len()),
|mut slab, (_, action)| {
slab.insert(ShortcutModel::new(
&self.defaults,
&self.shortcuts,
action.clone(),
));
if !slab.iter().any(|(_, model)| &model.action == action) {
slab.insert(ShortcutModel::new(
&self.defaults,
&self.shortcuts,
action.clone(),
));
}
slab
},
@ -497,32 +501,18 @@ fn shortcuts() -> Section<crate::pages::Message> {
}
/// Display a category as a list item
fn category_item(category: Category, name: &str, modified: u16) -> Element<'_, Message> {
let icon = icon::from_name("go-next-symbolic").size(16);
let control = if modified == 0 {
Element::from(icon)
} else {
widget::row::with_capacity(2)
.push(text::body(fl!("modified", count = modified)))
.push(icon)
.into()
};
settings::item::builder(name)
.control(control)
.spacing(16)
.apply(widget::container)
.class(theme::Container::List)
.apply(widget::button::custom)
.class(theme::Button::Transparent)
.width(Length::Fill)
.on_press(Message::Category(category))
.into()
fn category_item(category: Category, name: &str, modified: u16) -> ListButton<'_, Message> {
crate::widget::go_next_with_item(
name,
(modified > 0).then(|| text::body(fl!("modified", count = modified)).apply(Element::from)),
Message::Category(category),
)
}
fn action_category(action: &Action) -> Option<Category> {
Some(if manage_windows::actions().contains(action) {
Some(if accessibility::actions().contains(action) {
Category::Accessibility
} else if manage_windows::actions().contains(action) {
Category::ManageWindow
} else if move_window::actions().contains(action) {
Category::MoveWindow
@ -530,6 +520,8 @@ fn action_category(action: &Action) -> Option<Category> {
Category::Nav
} else if system::actions().contains(action) {
Category::System
} else if tiling::actions().contains(action) {
Category::WindowTiling
} else {
return None;
})

View file

@ -2,8 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
//
use super::{ShortcutMessage, ShortcutModel};
use cosmic::app::ContextDrawer;
use cosmic::iced::platform_specific::shell::wayland::commands::keyboard_shortcuts_inhibit;
use cosmic::{Element, Task, app::ContextDrawer};
use cosmic::{Element, Task};
use cosmic_settings_config::shortcuts::Action;
use cosmic_settings_config::shortcuts::action::Direction;
use cosmic_settings_page::{self as page, Section, section};

View file

@ -2,8 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use super::{ShortcutMessage, ShortcutModel};
use cosmic::app::ContextDrawer;
use cosmic::iced::platform_specific::shell::wayland::commands::keyboard_shortcuts_inhibit;
use cosmic::{Element, Task, app::ContextDrawer};
use cosmic::{Element, Task};
use cosmic_settings_config::shortcuts::Action;
use cosmic_settings_config::shortcuts::action::System as SystemAction;
use cosmic_settings_page::{self as page, Section, section};

View file

@ -2,8 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use super::{ShortcutMessage, ShortcutModel};
use cosmic::app::ContextDrawer;
use cosmic::iced::platform_specific::shell::wayland::commands::keyboard_shortcuts_inhibit;
use cosmic::{Element, Task, app::ContextDrawer};
use cosmic::{Element, Task};
use cosmic_settings_config::shortcuts::Action;
use cosmic_settings_config::shortcuts::action::Orientation;
use cosmic_settings_page::{self as page, Section, section};

View file

@ -2,10 +2,8 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::app;
use cosmic::{
Task,
cosmic_config::{self, ConfigGet, ConfigSet},
};
use cosmic::Task;
use cosmic::cosmic_config::{self, ConfigGet, ConfigSet};
use cosmic_comp_config::input::{
AccelConfig, AccelProfile, ClickMethod, InputConfig, ScrollConfig, ScrollMethod, TapButtonMap,
TapConfig,

View file

@ -5,8 +5,7 @@ use cosmic::iced::{Alignment, Length};
use cosmic::widget::{self, row, settings, text};
use cosmic::{Apply, Element};
use cosmic_comp_config::input::AccelProfile;
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use slotmap::SlotMap;
use super::Message;

View file

@ -8,8 +8,7 @@ use cosmic::{Apply, Element};
use cosmic_comp_config::CosmicCompConfig;
use cosmic_comp_config::input::{AccelProfile, ClickMethod, ScrollMethod};
use cosmic_comp_config::workspace::{WorkspaceConfig, WorkspaceLayout};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use slotmap::SlotMap;
use tracing::error;

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,24 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
pub mod backend;
pub mod vpn;
pub mod wifi;
pub mod wired;
use std::{ffi::OsStr, process::Stdio, sync::Arc};
use std::process::Stdio;
use std::sync::Arc;
use anyhow::Context;
use cosmic::{Apply, Element, Task, widget};
use cosmic_dbus_networkmanager::{
interface::enums::{DeviceState, DeviceType},
nm::NetworkManager,
};
use cosmic_settings_network_manager_subscription as network_manager;
use cosmic_settings_page::{self as page, Section, section};
use futures::{SinkExt, StreamExt};
use secure_string::SecureString;
use slotmap::SlotMap;
use self::backend as network_manager;
use self::backend::devices::{DeviceState, DeviceType};
pub type SecretSender = Arc<tokio::sync::Mutex<Option<tokio::sync::oneshot::Sender<SecureString>>>>;
static NM_CONNECTION_EDITOR: &str = "nm-connection-editor";
@ -37,8 +37,8 @@ pub struct Page {
pub enum Message {
/// An error occurred.
Error(String),
/// Successfully connected to the system dbus.
NetworkManagerConnect(zbus::Connection),
/// Successfully connected to NetworkManager.
NetworkManagerConnect(nmrs::NetworkManager),
/// Open the wifi settings page with the selected device.
OpenPage {
page: page::Entity,
@ -143,6 +143,7 @@ impl page::Page<crate::pages::Message> for Page {
fl!("network-device-state", "unavailable")
}
DeviceState::Unknown => fl!("network-device-state", "unknown"),
DeviceState::Other(_) => fl!("network-device-state", "unknown"),
DeviceState::Unmanaged => fl!("network-device-state", "unmanaged"),
},
"preferences-wireless-symbolic",
@ -190,6 +191,7 @@ impl page::Page<crate::pages::Message> for Page {
fl!("network-device-state", "unplugged")
}
DeviceState::Unknown => fl!("network-device-state", "unknown"),
DeviceState::Other(_) => fl!("network-device-state", "unknown"),
DeviceState::Unmanaged => fl!("network-device-state", "unmanaged"),
},
"preferences-wired-symbolic",
@ -225,9 +227,9 @@ impl page::Page<crate::pages::Message> for Page {
fn on_enter(&mut self) -> cosmic::Task<crate::pages::Message> {
if self.nm_task.is_none() {
return cosmic::Task::future(async move {
zbus::Connection::system()
nmrs::NetworkManager::new()
.await
.context("failed to create system dbus connection")
.context("failed to connect to NetworkManager")
.map_or_else(
|why| Message::Error(why.to_string()),
Message::NetworkManagerConnect,
@ -310,33 +312,47 @@ impl Page {
Task::none()
}
fn connect(&mut self, conn: zbus::Connection) -> Task<crate::app::Message> {
fn connect(&mut self, nm: nmrs::NetworkManager) -> Task<crate::app::Message> {
if self.nm_task.is_none() {
let (canceller, task) =
crate::utils::forward_event_loop(move |mut sender| async move {
let network_manager = match NetworkManager::new(&conn).await {
Ok(n) => n,
Err(why) => {
tracing::error!(
why = why.to_string(),
"failed to connect to network_manager"
);
return futures::future::pending().await;
match network_manager::devices::list(&nm, |_| true).await {
Ok(devices) => {
_ = sender
.send(crate::pages::Message::Networking(Message::UpdateDevices(
devices.into_iter().map(Arc::new).collect(),
)))
.await;
}
};
Err(why) => {
_ = sender
.send(crate::pages::Message::Networking(Message::Error(
why.to_string(),
)))
.await;
}
}
let mut devices_changed =
std::pin::pin!(network_manager.receive_devices_changed().await.then(
|_| async {
match network_manager::devices::list(&conn, |_| true).await {
Ok(devices) => Message::UpdateDevices(
let Ok(events) = nm.network_events().await else {
return futures::future::pending().await;
};
let devices_changed = events.filter_map(|event| async {
match event {
Ok(nmrs::NetworkEvent::DeviceChanged { .. })
| Ok(nmrs::NetworkEvent::SettingsChanged(_))
| Ok(nmrs::NetworkEvent::NetworkManagerRestarted) => {
match network_manager::devices::list(&nm, |_| true).await {
Ok(devices) => Some(Message::UpdateDevices(
devices.into_iter().map(Arc::new).collect(),
),
Err(why) => Message::Error(why.to_string()),
)),
Err(why) => Some(Message::Error(why.to_string())),
}
}
));
Ok(_) => None,
Err(why) => Some(Message::Error(why.to_string())),
}
});
futures::pin_mut!(devices_changed);
while let Some(message) = devices_changed.next().await {
_ = sender
@ -353,16 +369,6 @@ impl Page {
}
}
async fn nm_add_vpn_file<P: AsRef<OsStr>>(type_: &str, path: P) -> Result<(), String> {
tokio::process::Command::new("nmcli")
.args(["connection", "import", "type", type_, "file"])
.arg(path)
.stderr(Stdio::piped())
.output()
.await
.apply(crate::utils::map_stderr_output)
}
async fn nm_add_wired() -> Result<(), String> {
nm_connection_editor(&["--type=802-3-ethernet", "-c"]).await
}

View file

@ -1,30 +1,27 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
pub mod nmcli;
use std::collections::HashMap;
use std::sync::{Arc, LazyLock};
use anyhow::Context;
use cosmic::dialog::file_chooser::FileFilter;
use cosmic::task;
use cosmic::{
Apply, Element, Task,
iced::core::text::Wrapping,
iced::{Alignment, Length},
widget::{self, icon, space::horizontal as horizontal_space, text_input::focus},
};
use cosmic_settings_network_manager_subscription::nm_secret_agent::{self, PasswordFlag};
use cosmic_settings_network_manager_subscription::{
self as network_manager, NetworkManagerState, UUID, current_networks::ActiveConnectionInfo,
};
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::space::horizontal as horizontal_space;
use cosmic::widget::text_input::focus;
use cosmic::widget::{self, icon};
use cosmic::{Apply, Element, Task, task};
use cosmic_settings_page::{self as page, Section, section};
use futures::{FutureExt, SinkExt, StreamExt};
use indexmap::IndexMap;
use secure_string::SecureString;
use tokio::sync::Mutex;
use super::backend as network_manager;
use super::backend::current_networks::ActiveConnectionInfo;
use super::backend::nm_secret_agent::{self, PasswordFlag};
use super::backend::{NetworkManagerState, UUID};
use crate::pages::networking::SecretSender;
pub static SECURE_INPUT_VPN: LazyLock<widget::Id> = LazyLock::new(widget::Id::unique);
@ -60,8 +57,8 @@ pub enum Message {
NetworkManager(network_manager::Event),
/// An update from the secret agent
SecretAgent(network_manager::nm_secret_agent::Event),
/// Successfully connected to the system dbus.
NetworkManagerConnect(zbus::Connection),
/// Successfully connected to NetworkManager.
NetworkManagerConnect(nmrs::NetworkManager),
/// Updates the password text input
PasswordUpdate(SecureString),
/// Refresh devices and their connection profiles
@ -95,6 +92,7 @@ pub enum ErrorKind {
ConnectionEditor,
ConnectionSettings,
DbusConnection,
OpenVpnConfigPath,
UpdatingState,
WireGuardConfigPath,
WireGuardDevice,
@ -109,6 +107,7 @@ impl ErrorKind {
ErrorKind::ConnectionEditor => fl!("vpn-error", "connection-editor"),
ErrorKind::ConnectionSettings => fl!("vpn-error", "connection-settings"),
ErrorKind::DbusConnection => fl!("dbus-connection-error"),
ErrorKind::OpenVpnConfigPath => fl!("vpn-error", "openvpn-config-path"),
ErrorKind::UpdatingState => fl!("vpn-error", "updating-state"),
ErrorKind::WireGuardConfigPath => fl!("vpn-error", "wireguard-config-path"),
ErrorKind::WireGuardDevice => fl!("vpn-error", "wireguard-device"),
@ -179,7 +178,7 @@ pub enum VpnDialog {
#[derive(Debug)]
pub struct NmState {
conn: zbus::Connection,
conn: nmrs::NetworkManager,
sender: futures::channel::mpsc::UnboundedSender<network_manager::Request>,
active_conns: Vec<ActiveConnectionInfo>,
devices: Vec<network_manager::devices::DeviceInfo>,
@ -369,20 +368,22 @@ impl page::Page<crate::pages::Message> for Page {
let (tx, rx) = tokio::sync::mpsc::channel(4);
self.secret_tx = Some(tx);
if self.nm_task.is_none() {
return cosmic::Task::batch([cosmic::task::future(async move {
zbus::Connection::system()
.await
.context("failed to create system dbus connection")
.map_or_else(
|why| Message::Error(ErrorKind::DbusConnection, why.to_string()),
Message::NetworkManagerConnect,
)
}),
cosmic::Task::stream(
cosmic_settings_network_manager_subscription::nm_secret_agent::secret_agent_stream("com.system76.CosmicSettings.VPN.NetworkManager.SecretAgent", rx),
)
.map(|m| crate::pages::Message::Vpn(Message::SecretAgent(m))),
]);
return cosmic::Task::batch([
cosmic::task::future(async move {
nmrs::NetworkManager::new()
.await
.context("failed to connect to NetworkManager")
.map_or_else(
|why| Message::Error(ErrorKind::DbusConnection, why.to_string()),
Message::NetworkManagerConnect,
)
}),
cosmic::Task::stream(nm_secret_agent::secret_agent_stream(
"com.system76.CosmicSettings.VPN.NetworkManager.SecretAgent",
rx,
))
.map(|m| crate::pages::Message::Vpn(Message::SecretAgent(m))),
]);
}
cosmic::Task::none()
@ -428,7 +429,19 @@ impl Page {
}
}
Message::KnownConnections(connections) => {
self.known_connections = connections;
let mut connections: Vec<_> = connections.into_iter().collect();
connections.sort_by(|a, b| {
let name_a = match &a.1 {
ConnectionSettings::Vpn(s) => s.id.as_str(),
ConnectionSettings::Wireguard { id } => id.as_str(),
};
let name_b = match &b.1 {
ConnectionSettings::Vpn(s) => s.id.as_str(),
ConnectionSettings::Wireguard { id } => id.as_str(),
};
name_a.to_lowercase().cmp(&name_b.to_lowercase())
});
self.known_connections = connections.into_iter().collect();
}
Message::UpdateDevices(devices) => {
self.update_devices(devices);
@ -481,9 +494,8 @@ impl Page {
Message::WireGuardConfig => {
if let Some(VpnDialog::WireGuardName(device, filename, path)) = self.dialog.take() {
return cosmic::task::future(async move {
let new_path = path.replace(&filename, &device);
_ = std::fs::rename(&path, &new_path);
match super::nm_add_vpn_file("wireguard", new_path).await {
let _ = filename;
match network_manager::import_wireguard(path, &device).await {
Ok(_) => Message::Refresh,
Err(why) => Message::Error(ErrorKind::Config, why.to_string()),
}
@ -496,18 +508,12 @@ impl Page {
if let Some(settings) = self.known_connections.get(&uuid) {
let settings = match settings {
ConnectionSettings::Vpn(settings) => settings,
ConnectionSettings::Wireguard { id } => {
let connection_name = id.clone();
return cosmic::task::future(async move {
if let Err(why) = nmcli::connect(&connection_name).await {
return Message::Error(
ErrorKind::Connect,
format!("failed to connect to WireGuard VPN: {why}"),
);
}
Message::Refresh
});
ConnectionSettings::Wireguard { .. } => {
if let Some(NmState { ref sender, .. }) = self.nm_state {
_ = sender
.unbounded_send(network_manager::Request::ActivateVpn(uuid));
}
return Task::none();
}
};
@ -527,28 +533,10 @@ impl Page {
return task::message(Message::FocusSecureInput);
}
_ => {
let connection_name = settings.id.clone();
let username = settings.username.clone();
return cosmic::task::future(async move {
if let Err(why) = nmcli::connect(&connection_name).await {
return Message::VpnDialogError(VpnDialog::Password {
error: Some((
ErrorKind::Connect,
format!("failed to connect to VPN: {why}"),
)),
id: connection_name.clone(),
uuid,
username: username.clone(),
description: None,
password: SecureString::from(""),
password_hidden: true,
// TODO grab from the current dialog
tx: Arc::new(Mutex::new(None)),
});
}
Message::Refresh
});
if let Some(NmState { ref sender, .. }) = self.nm_state {
_ = sender
.unbounded_send(network_manager::Request::ActivateVpn(uuid));
}
}
}
}
@ -615,89 +603,55 @@ impl Page {
};
if let VpnDialog::Password {
id,
id: _,
uuid,
username,
password,
tx,
..
} = dialog
&& let Some(NmState { ref sender, .. }) = self.nm_state
{
let username_unwrapped = username.clone().unwrap_or_default();
let task = self.activate_with_password(
id.clone(),
uuid.clone(),
username_unwrapped.clone(),
password.clone(),
);
let sec_tx = self.secret_tx.clone();
return task
.then(move |_| {
let sec_tx = sec_tx.clone();
let uuid = uuid.clone();
let username = username.clone();
let password = password.clone();
let tx = tx.clone();
let id = id.clone();
Task::future(async move {
let mut guard = tx.lock().await;
if let Some(sender) = guard.take() {
let _ = sender.send(password);
} else {
// apply password and username then
if let Some(sec_tx) = sec_tx {
let (applied_tx, applied_rx) =
tokio::sync::oneshot::channel();
if let Err(err) = sec_tx
.send(nm_secret_agent::Request::SetSecrets {
setting_name: "vpn".to_string(),
uuid: uuid.to_string(),
secrets: HashMap::from_iter([
// username and password
(
"username".to_string(),
username.clone().unwrap_or_default().into(),
),
("password".to_string(), password.clone()),
]),
applied_tx,
})
.await
{
tracing::error!(%err, "failed to apply secret");
}
// wait max 1s for the applied signal
if let Err(err) = tokio::time::timeout(
std::time::Duration::from_secs(1),
applied_rx,
)
.await
{
tracing::error!(%err, "failed to apply secret");
}
}
// activate
if let Err(why) = nmcli::connect(&id).await {
return Message::VpnDialogError(VpnDialog::Password {
error: Some((
ErrorKind::Connect,
format!("failed to connect to VPN: {why}"),
)),
id: id.clone(),
uuid,
username: username.clone(),
description: None,
password,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
});
}
let nm_sender = sender.clone();
return Task::future(async move {
let mut guard = tx.lock().await;
if let Some(sender) = guard.take() {
let _ = sender.send(password);
} else {
if let Some(sec_tx) = sec_tx {
let (applied_tx, applied_rx) = tokio::sync::oneshot::channel();
if let Err(err) = sec_tx
.send(nm_secret_agent::Request::SetSecrets {
setting_name: "vpn".to_string(),
uuid: uuid.to_string(),
secrets: HashMap::from_iter([
("username".to_string(), username_unwrapped.into()),
("password".to_string(), password),
]),
applied_tx,
})
.await
{
tracing::error!(%err, "failed to apply secret");
}
if let Err(err) = tokio::time::timeout(
std::time::Duration::from_secs(1),
applied_rx,
)
.await
{
tracing::error!(%err, "failed to apply secret");
}
}
_ = nm_sender
.unbounded_send(network_manager::Request::ActivateVpn(uuid));
}
Message::Refresh
})
})
.map(crate::app::Message::from);
Message::Refresh
})
.map(crate::app::Message::from);
}
}
Message::RetryWithPassword => {
@ -706,53 +660,39 @@ impl Page {
};
if let VpnDialog::Password {
id,
id: _,
uuid,
username,
password,
..
} = dialog
&& let Some(NmState { ref sender, .. }) = self.nm_state
{
let username_unwrapped = username.unwrap_or_default();
let sec_tx = self.secret_tx.clone();
let task = self.activate_with_password(
id.clone(),
uuid.clone(),
username_unwrapped.clone(),
password.clone(),
);
return task
.then(move |_| {
let sec_tx = sec_tx.clone();
let uuid = uuid.clone();
let username = username_unwrapped.clone();
let password = password.clone();
Task::future(async move {
if let Some(sec_tx) = sec_tx {
let (applied_tx, applied_rx) = tokio::sync::oneshot::channel();
let _ = sec_tx
.send(nm_secret_agent::Request::SetSecrets {
setting_name: "vpn".to_string(),
uuid: uuid.to_string(),
secrets: HashMap::from_iter([
// username and password
("username".to_string(), username.clone().into()),
("password".to_string(), password.clone()),
]),
applied_tx,
})
.await;
// wait max 1s for the applied signal
let _ = tokio::time::timeout(
std::time::Duration::from_secs(1),
applied_rx,
)
let nm_sender = sender.clone();
return Task::future(async move {
if let Some(sec_tx) = sec_tx {
let (applied_tx, applied_rx) = tokio::sync::oneshot::channel();
let _ = sec_tx
.send(nm_secret_agent::Request::SetSecrets {
setting_name: "vpn".to_string(),
uuid: uuid.to_string(),
secrets: HashMap::from_iter([
("username".to_string(), username_unwrapped.into()),
("password".to_string(), password),
]),
applied_tx,
})
.await;
let _ =
tokio::time::timeout(std::time::Duration::from_secs(1), applied_rx)
.await;
}
Message::Activate(uuid)
})
})
.map(crate::app::Message::from);
}
_ = nm_sender.unbounded_send(network_manager::Request::ActivateVpn(uuid));
Message::Refresh
})
.map(crate::app::Message::from);
}
}
Message::UsernameUpdate(user) => {
@ -859,56 +799,14 @@ impl Page {
Task::none()
}
fn activate_with_password(
&mut self,
connection_name: String,
uuid: Arc<str>,
username: String,
password: SecureString,
) -> Task<Message> {
cosmic::task::future(async move {
if let Err(why) = nmcli::set_username(&connection_name, &username).await {
return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::WithPassword("username"), why.to_string())),
id: connection_name.clone(),
uuid,
username: Some(username),
description: None,
password,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
});
}
if let Err(why) = nmcli::add_fallback(&connection_name).await {
return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::Config, why.to_string())),
id: connection_name.clone(),
uuid,
username: Some(username),
password,
description: None,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
});
}
Message::Refresh
})
}
fn connect(&mut self, conn: zbus::Connection) -> Task<crate::app::Message> {
fn connect(&mut self, conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
if self.nm_task.is_none() {
let (canceller, task) =
crate::utils::forward_event_loop(move |mut sender| async move {
let (tx, mut rx) = futures::channel::mpsc::channel(1);
let watchers = std::pin::pin!(async move {
futures::join!(
network_manager::watch(conn.clone(), tx.clone()),
network_manager::active_conns::watch(conn.clone(), tx.clone()),
network_manager::devices::watch(conn, true, tx)
)
network_manager::watch(conn, tx).await;
});
let forwarder = std::pin::pin!(async move {
@ -1005,7 +903,20 @@ fn devices_view() -> Section<crate::pages::Message> {
widget::text::body(fl!("no-vpn")).into(),
])));
} else {
let known_networks = page.known_connections.iter().fold(
let mut known_connections: Vec<_> = page.known_connections.iter().collect();
known_connections.sort_by(|a, b| {
let name_a = match a.1 {
ConnectionSettings::Vpn(s) => s.id.as_str(),
ConnectionSettings::Wireguard { id } => id.as_str(),
};
let name_b = match b.1 {
ConnectionSettings::Vpn(s) => s.id.as_str(),
ConnectionSettings::Wireguard { id } => id.as_str(),
};
name_a.to_lowercase().cmp(&name_b.to_lowercase())
});
let known_networks = known_connections.into_iter().fold(
vpn_connections,
|networks, (uuid, connection)| {
let id = match connection {
@ -1114,7 +1025,7 @@ fn popup_button(message: Message, text: &str) -> Element<'_, Message> {
.into()
}
fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
fn update_state(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
match NetworkManagerState::new(&conn).await {
Ok(state) => Message::UpdateState(state),
@ -1123,7 +1034,7 @@ fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
})
}
fn update_devices(conn: zbus::Connection) -> Task<crate::app::Message> {
fn update_devices(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
let filter =
|device_type| matches!(device_type, network_manager::devices::DeviceType::WireGuard);
@ -1174,8 +1085,16 @@ fn add_network() -> Task<crate::app::Message> {
return Message::AddWireGuardDevice(device, filename.to_owned(), path);
} else {
super::nm_add_vpn_file("openvpn", response.url().to_file_path().unwrap())
let Ok(path) = response.url().to_file_path() else {
return Message::Error(
ErrorKind::OpenVpnConfigPath,
fl!("vpn-error", "openvpn-config-path-desc"),
);
};
network_manager::import_openvpn(path)
.await
.map_err(|why| why.to_string())
};
match result {
@ -1190,108 +1109,96 @@ fn add_network() -> Task<crate::app::Message> {
.apply(cosmic::task::future)
}
fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
let settings = async move {
let settings = network_manager::dbus::settings::NetworkManagerSettings::new(&conn).await?;
_ = settings.load_connections(&[]).await;
let settings = settings
// Get a list of known connections.
.list_connections()
.await?
// Prepare for wrapping in a concurrent stream.
.into_iter()
.map(|conn| async move { conn })
// Create a concurrent stream for each connection.
.apply(futures::stream::FuturesOrdered::from_iter)
// Concurrently fetch settings for each connection, and filter for VPN.
.filter_map(|conn| async move {
let settings = conn.get_settings().await.ok()?;
let connection = settings.get("connection")?;
match connection
.get("type")?
.downcast_ref::<String>()
.ok()?
.as_str()
{
"vpn" => (),
"wireguard" => {
let id = connection.get("id")?.downcast_ref::<String>().ok()?;
let uuid = connection.get("uuid")?.downcast_ref::<String>().ok()?;
return Some((Arc::from(uuid), ConnectionSettings::Wireguard { id }));
}
_ => return None,
}
let vpn = settings.get("vpn")?;
let id = connection.get("id")?.downcast_ref::<String>().ok()?;
let uuid = connection.get("uuid")?.downcast_ref::<String>().ok()?;
let (connection_type, username, password_flag) = vpn
.get("data")
.and_then(|data| data.downcast_ref::<zbus::zvariant::Dict>().ok())
.map(|dict| {
let (mut connection_type, mut password_flag) = (None, None);
let mut username = vpn
.get("user-name")
.and_then(|u| u.downcast_ref::<String>().ok());
if dict
.get::<String, String>(&String::from("connection-type"))
.ok()
.flatten()
.as_deref()
// may be "password" or "password-tls"
.is_some_and(|p| p.starts_with("password"))
{
connection_type = Some(ConnectionType::Password);
username = Some(username.unwrap_or_default());
password_flag = dict
.get::<String, String>(&String::from("password-flags"))
.ok()
.flatten()
.and_then(|value| match value.as_str() {
"0" => Some(PasswordFlag::None),
"1" => Some(PasswordFlag::AgentOwned),
"2" => Some(PasswordFlag::NotSaved),
"4" => Some(PasswordFlag::NotRequired),
_ => None,
});
}
(connection_type, username, password_flag)
})
.unwrap_or_default();
Some((
Arc::from(uuid),
ConnectionSettings::Vpn(VpnConnectionSettings {
id,
connection_type,
password_flag,
username,
}),
))
})
// Reduce the settings list into
.fold(IndexMap::new(), |mut set, (uuid, data)| async move {
set.insert(uuid, data);
set
})
.await;
Ok::<_, zbus::Error>(settings)
};
fn connection_settings(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
let settings = async move {
let vpns = conn.list_vpn_connections().await?;
let mut settings = IndexMap::new();
for vpn in vpns {
let uuid = Arc::from(vpn.uuid.as_str());
let data = match vpn.vpn_type {
nmrs::VpnType::WireGuard { .. } => ConnectionSettings::Wireguard { id: vpn.id },
nmrs::VpnType::OpenVpn {
connection_type,
user_name,
password_flags,
..
} => ConnectionSettings::Vpn(VpnConnectionSettings {
id: vpn.id,
username: user_name,
connection_type: connection_type
.filter(|ct| {
matches!(
ct,
nmrs::OpenVpnConnectionType::Password
| nmrs::OpenVpnConnectionType::PasswordTls
)
})
.map(|_| ConnectionType::Password),
password_flag: Some(password_flag(password_flags.0)),
}),
nmrs::VpnType::OpenConnect {
user_name,
password_flags,
..
}
| nmrs::VpnType::StrongSwan {
user_name,
password_flags,
..
}
| nmrs::VpnType::Pptp {
user_name,
password_flags,
..
}
| nmrs::VpnType::L2tp {
user_name,
password_flags,
..
} => ConnectionSettings::Vpn(VpnConnectionSettings {
id: vpn.id,
username: user_name,
connection_type: Some(ConnectionType::Password),
password_flag: Some(password_flag(password_flags.0)),
}),
nmrs::VpnType::Generic {
user_name,
password_flags,
..
} => ConnectionSettings::Vpn(VpnConnectionSettings {
id: vpn.id,
username: user_name,
connection_type: Some(ConnectionType::Password),
password_flag: Some(password_flag(password_flags.0)),
}),
_ => ConnectionSettings::Vpn(VpnConnectionSettings {
id: vpn.id,
username: None,
connection_type: None,
password_flag: None,
}),
};
settings.insert(uuid, data);
}
Ok::<_, nmrs::ConnectionError>(settings)
};
settings.await.map_or_else(
|why| Message::Error(ErrorKind::ConnectionSettings, why.to_string()),
Message::KnownConnections,
)
})
}
fn password_flag(value: u32) -> PasswordFlag {
match value {
0 => PasswordFlag::None,
1 => PasswordFlag::AgentOwned,
2 => PasswordFlag::NotSaved,
4 => PasswordFlag::NotRequired,
_ => PasswordFlag::AgentOwned,
}
}

View file

@ -1,38 +0,0 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::Apply;
use std::process::Stdio;
pub async fn set_username(connection_name: &str, username: &str) -> Result<(), String> {
tokio::process::Command::new("nmcli")
.args(["con", "mod", connection_name, "vpn.user-name", username])
.stderr(Stdio::piped())
.output()
.await
.apply(crate::utils::map_stderr_output)
}
pub async fn add_fallback(connection_name: &str) -> Result<(), String> {
tokio::process::Command::new("nmcli")
.args([
"con",
"mod",
connection_name,
"+vpn.data",
"data-ciphers=AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305:AES-256-CBC:AES-128-CBC",
])
.stderr(Stdio::piped())
.output()
.await
.apply(crate::utils::map_stderr_output)
}
pub async fn connect(connection_name: &str) -> Result<(), String> {
tokio::process::Command::new("nmcli")
.args(["con", "up", connection_name])
.stderr(Stdio::piped())
.output()
.await
.apply(crate::utils::map_stderr_output)
}

View file

@ -1,31 +1,27 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::{
collections::{BTreeMap, BTreeSet},
sync::{Arc, LazyLock},
};
use std::collections::{BTreeMap, BTreeSet};
use std::sync::{Arc, LazyLock};
use anyhow::Context;
use cosmic::{
Apply, Element, Task,
app::ContextDrawer,
iced::core::text::Wrapping,
iced::{Alignment, Length, widget::operation::focus_next},
task,
widget::{self, column, icon, space::horizontal, text_input::focus},
};
use cosmic_settings_network_manager_subscription::{
self as network_manager, NetworkManagerState,
available_wifi::{AccessPoint, NetworkType},
current_networks::ActiveConnectionInfo,
nm_secret_agent,
};
use cosmic::app::ContextDrawer;
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::widget::operation::focus_next;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::space::horizontal;
use cosmic::widget::text_input::focus;
use cosmic::widget::{self, column, icon};
use cosmic::{Apply, Element, Task, task};
use cosmic_settings_page::{self as page, Section, section};
use futures::{SinkExt, StreamExt};
use secure_string::SecureString;
use tokio::sync::Mutex;
use super::backend as network_manager;
use super::backend::available_wifi::{AccessPoint, NetworkType};
use super::backend::current_networks::ActiveConnectionInfo;
use super::backend::{NetworkManagerState, nm_secret_agent};
use crate::pages::networking::SecretSender;
pub static SECURE_INPUT_WIFI: LazyLock<widget::Id> = LazyLock::new(widget::Id::unique);
@ -58,8 +54,8 @@ pub enum Message {
NetworkManager(network_manager::Event),
/// An update from the secret agent
SecretAgent(network_manager::nm_secret_agent::Event),
/// Successfully connected to the system dbus.
NetworkManagerConnect(zbus::Connection),
/// Successfully connected to NetworkManager.
NetworkManagerConnect(nmrs::NetworkManager),
/// Request an auth dialog
PasswordRequest(network_manager::SSID),
/// Update the password from the dialog
@ -105,6 +101,7 @@ enum WiFiDialog {
ssid: network_manager::SSID,
identity: Option<String>,
password: SecureString,
network_type: NetworkType,
password_hidden: bool,
tx: SecretSender,
},
@ -144,7 +141,7 @@ pub struct Page {
#[derive(Debug)]
pub struct NmState {
conn: zbus::Connection,
conn: nmrs::NetworkManager,
sender: futures::channel::mpsc::UnboundedSender<network_manager::Request>,
state: network_manager::NetworkManagerState,
devices: Vec<network_manager::devices::DeviceInfo>,
@ -297,19 +294,23 @@ impl page::Page<crate::pages::Message> for Page {
let (tx, rx) = tokio::sync::mpsc::channel(4);
self.secret_tx = Some(tx);
if self.nm_task.is_none() {
return Task::batch(vec![cosmic::Task::future(async move {
zbus::Connection::system()
.await
.context("failed to create system dbus connection")
.map_or_else(
|why| Message::Error(why.to_string()),
Message::NetworkManagerConnect,
)
.apply(crate::pages::Message::WiFi)
}), cosmic::Task::stream(
cosmic_settings_network_manager_subscription::nm_secret_agent::secret_agent_stream("com.system76.CosmicSettings.WiFi.NetworkManager.SecretAgent", rx),
)
.map(|m| crate::pages::Message::WiFi(Message::SecretAgent(m)))]);
return Task::batch(vec![
cosmic::Task::future(async move {
nmrs::NetworkManager::new()
.await
.context("failed to connect to NetworkManager")
.map_or_else(
|why| Message::Error(why.to_string()),
Message::NetworkManagerConnect,
)
.apply(crate::pages::Message::WiFi)
}),
cosmic::Task::stream(nm_secret_agent::secret_agent_stream(
"com.system76.CosmicSettings.WiFi.NetworkManager.SecretAgent",
rx,
))
.map(|m| crate::pages::Message::WiFi(Message::SecretAgent(m))),
]);
}
Task::none()
@ -355,15 +356,22 @@ impl Page {
}
match req {
network_manager::Request::Authenticate { ssid, identity, .. } => {
network_manager::Request::Authenticate {
ssid,
identity,
network_type,
..
} => {
if success {
self.connecting.remove(ssid.as_str());
} else {
self.connecting.remove(ssid.as_str());
// Request to retry
self.dialog = Some(WiFiDialog::Password {
ssid: ssid.into(),
identity,
password: SecureString::from(""),
network_type,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
});
@ -380,11 +388,13 @@ impl Page {
if success || matches!(network_type, NetworkType::Open) {
self.connecting.remove(ssid.as_ref());
} else {
self.connecting.remove(ssid.as_ref());
self.dialog = Some(WiFiDialog::Password {
ssid,
identity: matches!(network_type, NetworkType::EAP)
.then(String::new),
password: SecureString::from(""),
network_type,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
});
@ -452,6 +462,7 @@ impl Page {
let qr_string = if let Some(ref pass) = password {
let security = match security_type {
NetworkType::PskOrSae => "WPA",
NetworkType::Sae => "WPA",
NetworkType::EAP => "WPA",
NetworkType::Open => "",
};
@ -524,6 +535,7 @@ impl Page {
ssid,
identity: matches!(ap.network_type, NetworkType::EAP).then(String::new),
password: SecureString::from(""),
network_type: ap.network_type,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
});
@ -547,6 +559,7 @@ impl Page {
ssid,
identity,
password,
network_type,
tx,
..
} = dialog
@ -565,6 +578,7 @@ impl Page {
ssid: ssid.to_string(),
identity,
password,
network_type,
secret_tx,
interface,
});
@ -617,6 +631,7 @@ impl Page {
}
Message::Disconnect(ssid) => {
self.close_popup_and_apply_updates();
self.connecting.remove(ssid.as_ref());
if let Some(nm) = self.nm_state.as_mut() {
_ = nm
.sender
@ -630,6 +645,7 @@ impl Page {
Message::Forget(ssid) => {
self.dialog = None;
self.close_popup_and_apply_updates();
self.connecting.remove(ssid.as_ref());
if let Some(nm) = self.nm_state.as_mut() {
_ = nm
.sender
@ -651,6 +667,9 @@ impl Page {
}
}
Message::WiFiEnable(enable) => {
if !enable {
self.connecting.clear();
}
if let Some(nm) = self.nm_state.as_mut() {
_ = nm
.sender
@ -659,7 +678,9 @@ impl Page {
}
}
Message::CancelDialog => {
self.dialog = None;
if let Some(WiFiDialog::Password { ssid, .. }) = self.dialog.take() {
self.connecting.remove(ssid.as_ref());
}
}
Message::Error(why) => {
tracing::error!(why);
@ -718,15 +739,20 @@ impl Page {
password: previous,
password_hidden: true,
identity: matches!(ap.network_type, NetworkType::EAP).then(String::new),
network_type: ap.network_type,
tx,
});
return task::message(Message::FocusSecureInput);
}
nm_secret_agent::Event::CancelGetSecrets { uuid: _, name: _ } => {
self.dialog = self
.dialog
.take()
.filter(|d| !matches!(d, &WiFiDialog::Password { .. }));
match self.dialog.take() {
Some(WiFiDialog::Password { ssid, .. }) => {
self.connecting.remove(ssid.as_ref());
}
other => {
self.dialog = other;
}
}
}
nm_secret_agent::Event::Failed(error) => {
tracing::error!(%error, "secret agent failure");
@ -734,15 +760,18 @@ impl Page {
ssid,
password,
identity,
network_type,
..
}) = self.dialog.take()
{
self.connecting.remove(ssid.as_ref());
self.dialog = Some(WiFiDialog::Password {
password,
password_hidden: true,
tx: Arc::new(Mutex::new(None)),
ssid,
identity,
network_type,
});
return task::message(Message::FocusSecureInput);
}
@ -773,19 +802,14 @@ impl Page {
Task::none()
}
fn connect(&mut self, conn: zbus::Connection) -> Task<crate::app::Message> {
fn connect(&mut self, conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
if self.nm_task.is_none() {
let (canceller, task) =
crate::utils::forward_event_loop(move |mut sender| async move {
let (tx, mut rx) = futures::channel::mpsc::channel(1);
let watchers = std::pin::pin!(async move {
futures::join!(
network_manager::watch(conn.clone(), tx.clone()),
network_manager::active_conns::watch(conn.clone(), tx.clone()),
network_manager::wireless_enabled::watch(conn.clone(), tx.clone()),
network_manager::watch_connections_changed(conn, tx)
);
network_manager::watch(conn, tx).await;
});
let forwarder = std::pin::pin!(async move {
@ -835,6 +859,12 @@ impl Page {
/// Withholds updates if the view more popup is displayed.
fn update_state(&mut self, state: NetworkManagerState) {
for active in &state.active_conns {
if let ActiveConnectionInfo::WiFi { name, .. } = active {
self.connecting.remove(name.as_str());
}
}
if let Some(ref mut nm_state) = self.nm_state {
if self.view_more_popup.is_some() {
self.withheld_state = Some(state);
@ -1176,10 +1206,9 @@ fn devices_view() -> Section<crate::pages::Message> {
// Search input (only shown when 15+ networks)
if show_search {
let search_input =
widget::search_input(fl!("type-to-search"), &page.search_query)
.on_input(Message::SearchQuery)
.on_clear(Message::SearchQuery(String::new()));
let search_input = widget::search_input("", &page.search_query)
.on_input(Message::SearchQuery)
.on_clear(Message::SearchQuery(String::new()));
visible_section = visible_section.push(search_input);
}
@ -1275,51 +1304,9 @@ fn popup_button(message: Message, text: &str) -> Element<'_, Message> {
.into()
}
fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
let settings = async move {
let settings = network_manager::dbus::settings::NetworkManagerSettings::new(&conn).await?;
_ = settings.load_connections(&[]).await;
let settings = settings
// Get a list of known connections.
.list_connections()
.await?
// Prepare for wrapping in a concurrent stream.
.into_iter()
.map(|conn| async move { conn })
// Create a concurrent stream for each connection.
.apply(futures::stream::FuturesOrdered::from_iter)
// Concurrently fetch settings for each connection.
.filter_map(|conn| async move {
conn.get_settings()
.await
.map(network_manager::Settings::new)
.ok()
})
// Reduce the settings list into a SSID->UUID map.
.fold(BTreeMap::new(), |mut set, settings| async move {
if let Some(ref wifi) = settings.wifi
&& let Some(ssid) = wifi
.ssid
.clone()
.and_then(|ssid| String::from_utf8(ssid).ok())
&& let Some(ref connection) = settings.connection
&& let Some(uuid) = connection.uuid.clone()
{
set.insert(ssid.into(), uuid.into());
return set;
}
set
})
.await;
Ok::<_, zbus::Error>(settings)
};
fn connection_settings(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
settings
network_manager::wifi_connection_settings(conn)
.await
.context("failed to get connection settings")
.map_or_else(
@ -1330,7 +1317,7 @@ fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
})
}
pub fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
pub fn update_state(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
match NetworkManagerState::new(&conn).await {
Ok(state) => Message::UpdateState(state),
@ -1339,7 +1326,7 @@ pub fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
})
}
pub fn update_devices(conn: zbus::Connection) -> Task<crate::app::Message> {
pub fn update_devices(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
let filter =
|device_type| matches!(device_type, network_manager::devices::DeviceType::Wifi);

View file

@ -1,22 +1,23 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::{collections::BTreeSet, sync::Arc};
use std::collections::BTreeSet;
use std::sync::Arc;
use anyhow::Context;
use cosmic::{
Apply, Element, Task,
iced::core::text::Wrapping,
iced::{Alignment, Length},
widget::{self, icon, space::horizontal as horizontal_space},
};
use cosmic_dbus_networkmanager::interface::enums::DeviceState;
use cosmic_settings_network_manager_subscription::{
self as network_manager, NetworkManagerState, current_networks::ActiveConnectionInfo,
};
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::space::horizontal as horizontal_space;
use cosmic::widget::{self, icon};
use cosmic::{Apply, Element, Task};
use cosmic_settings_page::{self as page, Section, section};
use futures::{SinkExt, StreamExt};
use super::backend as network_manager;
use super::backend::NetworkManagerState;
use super::backend::current_networks::ActiveConnectionInfo;
use super::backend::devices::DeviceState;
pub type ConnectionId = Arc<str>;
#[derive(Clone, Debug)]
@ -33,8 +34,8 @@ pub enum Message {
Error(String),
/// An update from the network manager daemon
NetworkManager(network_manager::Event),
/// Successfully connected to the system dbus.
NetworkManagerConnect(zbus::Connection),
/// Successfully connected to NetworkManager.
NetworkManagerConnect(nmrs::NetworkManager),
/// Refresh devices and their connection profiles
Refresh,
/// Create a dialog to ask for confirmation of removal.
@ -92,7 +93,7 @@ pub struct Page {
#[derive(Debug)]
pub struct NmState {
conn: zbus::Connection,
conn: nmrs::NetworkManager,
sender: futures::channel::mpsc::UnboundedSender<network_manager::Request>,
active_conns: Vec<ActiveConnectionInfo>,
devices: Vec<Arc<network_manager::devices::DeviceInfo>>,
@ -155,9 +156,9 @@ impl page::Page<crate::pages::Message> for Page {
fn on_enter(&mut self) -> cosmic::Task<crate::pages::Message> {
if self.nm_task.is_none() {
return cosmic::task::future(async move {
zbus::Connection::system()
nmrs::NetworkManager::new()
.await
.context("failed to create system dbus connection")
.context("failed to connect to NetworkManager")
.map_or_else(
|why| Message::Error(why.to_string()),
Message::NetworkManagerConnect,
@ -357,18 +358,14 @@ impl Page {
Task::none()
}
fn connect(&mut self, conn: zbus::Connection) -> Task<crate::app::Message> {
fn connect(&mut self, conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
if self.nm_task.is_none() {
let (canceller, task) =
crate::utils::forward_event_loop(move |mut sender| async move {
let (tx, mut rx) = futures::channel::mpsc::channel(1);
let watchers = std::pin::pin!(async move {
futures::join!(
network_manager::watch(conn.clone(), tx.clone()),
network_manager::active_conns::watch(conn.clone(), tx.clone()),
network_manager::devices::watch(conn, true, tx)
)
network_manager::watch(conn, tx).await;
});
let forwarder = std::pin::pin!(async move {
@ -623,7 +620,7 @@ fn popup_button(message: Message, text: &str) -> Element<'_, Message> {
.into()
}
fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
fn update_state(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
match NetworkManagerState::new(&conn).await {
Ok(state) => Message::UpdateState(state),
@ -632,7 +629,7 @@ fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
})
}
fn update_devices(conn: zbus::Connection) -> Task<crate::app::Message> {
fn update_devices(conn: nmrs::NetworkManager) -> Task<crate::app::Message> {
cosmic::task::future(async move {
let filter =
|device_type| matches!(device_type, network_manager::devices::DeviceType::Ethernet);

View file

@ -1,7 +1,9 @@
use futures::{FutureExt, Stream, StreamExt, future::join_all};
use futures::future::join_all;
use futures::{FutureExt, Stream, StreamExt};
use jiff::{Span, SpanRelativeTo, SpanRound, ToSpan, Unit};
use upower_dbus::{BatteryState, BatteryType, DeviceProxy};
use zbus::{Connection, zvariant::ObjectPath};
use zbus::Connection;
use zbus::zvariant::ObjectPath;
mod ppdaemon;
mod s76powerdaemon;

View file

@ -3,12 +3,11 @@ mod backend;
use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
use backend::{Battery, ConnectedDevice, PowerProfile};
use cosmic::Task;
use cosmic::iced::core::text::{Ellipsize, EllipsizeHeightLimit};
use cosmic::iced::widget::{column, row};
use cosmic::iced::{self, Alignment, Length, stream};
use cosmic::widget::{self, settings, space, text};
use cosmic::{Apply, surface};
use cosmic::{Apply, Task, surface};
use cosmic_config::{Config, CosmicConfigEntry};
use cosmic_idle_config::CosmicIdleConfig;
use cosmic_settings_page::{self as page, Section, section};

View file

@ -1,13 +1,26 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{Apply, widget};
use super::model;
use cosmic::iced::futures;
use cosmic::{Apply, iced, widget};
use cosmic_settings_audio_client::{self as audio_client, CosmicAudioProxy};
use cosmic_settings_page::{self as page, Section, section};
use cosmic_settings_sound_subscription::{self as subscription};
use futures::executor::block_on;
use slotmap::SlotMap;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub enum Message {}
pub enum Message {
/// Update for the model.
Model(cosmic_settings_sound::Message),
/// Set the profile of a sound device.
SetProfile(u32, u32),
/// Surface Action
Surface(cosmic::surface::Action),
}
impl From<Message> for crate::pages::Message {
fn from(message: Message) -> Self {
@ -24,6 +37,8 @@ impl From<Message> for crate::Message {
#[derive(Default)]
pub struct Page {
entity: page::Entity,
model: model::Model,
client: Option<Rc<RefCell<audio_client::Client>>>,
}
impl page::AutoBind<crate::pages::Message> for Page {}
@ -41,55 +56,83 @@ impl page::Page<crate::pages::Message> for Page {
Some(vec![sections.insert(view())])
}
fn on_leave(&mut self) -> cosmic::Task<crate::pages::Message> {
cosmic::Task::done(crate::pages::Message::Sound(super::Message::Reload))
}
fn set_id(&mut self, entity: cosmic_settings_page::Entity) {
self.entity = entity;
}
fn subscription(
&self,
_core: &cosmic::Core,
) -> cosmic::iced::Subscription<crate::pages::Message> {
cosmic::iced::Subscription::run(subscription::watch)
.map(|message| super::Message::Subscription(message).into())
fn on_leave(&mut self) -> cosmic::Task<crate::pages::Message> {
*self = Page {
entity: self.entity,
..Page::default()
};
cosmic::Task::none()
}
fn subscription(&self, _core: &cosmic::Core) -> iced::Subscription<crate::pages::Message> {
iced::Subscription::run(|| {
iced::stream::channel(
1,
move |emitter: futures::channel::mpsc::Sender<crate::pages::Message>| async move {
cosmic_settings_sound::subscribe(emitter, |m| Message::Model(m).into()).await
},
)
})
}
}
impl Page {
pub fn update(&mut self, _message: Message) -> cosmic::Task<crate::app::Message> {
pub fn update(&mut self, message: Message) -> cosmic::Task<crate::app::Message> {
match message {
Message::Model(cosmic_settings_sound::Message::Subscription(message)) => {
self.model.update(message);
}
Message::Model(cosmic_settings_sound::Message::Client(client)) => {
if let Some(client) = Arc::into_inner(client) {
self.client = Some(Rc::new(RefCell::new(client)));
self.model = model::Model {
text: model::Text {
hd_audio: fl!("sound-hd-audio"),
usb_audio: fl!("sound-usb-audio"),
},
..model::Model::default()
};
}
}
Message::Surface(a) => return cosmic::task::message(crate::app::Message::Surface(a)),
Message::SetProfile(id, index) => {
if let Some(client) = self.client.clone() {
block_on(async move {
_ = client.borrow_mut().conn.set_profile(id, index, true).await;
});
}
}
}
cosmic::Task::none()
}
}
pub fn view() -> Section<crate::pages::Message> {
Section::default().view::<Page>(move |binder, _page, _section| {
let sound_page_id = binder.find_page_by_id("sound").unwrap().0;
let sound_page = binder.page[sound_page_id]
.downcast_ref::<super::Page>()
.unwrap();
let devices = sound_page
.model
.device_profile_dropdowns
.iter()
.cloned()
.map(|(device_id, name, active_profile, indexes, descriptions)| {
Section::default().view::<Page>(move |_, page, _section| {
let devices = page.model.device_profile_dropdowns.iter().cloned().map(
|(device_id, name, active_profile, indexes, descriptions)| {
let dropdown = widget::dropdown::popup_dropdown(
descriptions,
active_profile,
move |id| super::Message::SetProfile(device_id, indexes[id]),
move |id| Message::SetProfile(device_id, indexes[id]),
cosmic::iced::window::Id::RESERVED,
super::Message::Surface,
Message::Surface,
crate::Message::from,
)
.apply(cosmic::Element::from)
.map(crate::pages::Message::from);
widget::settings::item::builder(name).control(dropdown)
});
},
);
widget::settings::section().extend(devices).into()
})

View file

@ -3,16 +3,19 @@
pub mod device_profiles;
use cosmic::{
Apply, Element, Task,
iced::{Alignment, Length, window},
surface,
widget::{self, settings, space::horizontal as horizontal_space},
};
use cosmic::iced::{self, Alignment, Length, window};
use cosmic::widget::space::horizontal as horizontal_space;
use cosmic::widget::{self, settings};
use cosmic::{Apply, Element, Task, surface};
use cosmic_config::{Config, ConfigGet, ConfigSet};
use cosmic_settings_audio_client::{self as audio_client, Client, CosmicAudioProxy};
use cosmic_settings_page::{self as page, Section, section};
use cosmic_settings_sound_subscription as subscription;
use cosmic_settings_sound::model;
use futures::executor::block_on;
use slotmap::SlotMap;
use std::sync::atomic::AtomicU32;
use std::sync::{Arc, atomic};
use std::time::Duration;
const AUDIO_CONFIG: &str = "com.system76.CosmicAudio";
const AMPLIFICATION_SINK: &str = "amplification_sink";
@ -20,22 +23,20 @@ const AMPLIFICATION_SOURCE: &str = "amplification_source";
#[derive(Clone, Debug)]
pub enum Message {
/// Reload the model
Reload,
/// Updates for the model.
Model(cosmic_settings_sound::Message),
/// Reattach the client
ReattachClient(Arc<Client>),
/// Change the default output.
SetDefaultSink(usize),
/// Change the default input output.
SetDefaultSource(usize),
/// Set the profile of a sound device.
SetProfile(u32, u32),
/// Change the balance of the active sink.
SetSinkBalance(u32),
SetSinkBalance(f32),
/// Request to change the default output volume.
SetSinkVolume(u32),
/// Request to change the input volume.
SetSourceVolume(u32),
/// Messages handled by the sound module in cosmic-settings-subscriptions
Subscription(subscription::Message),
/// Surface Action
Surface(surface::Action),
/// Toggle the mute status of the output.
@ -60,32 +61,36 @@ impl From<Message> for crate::Message {
}
}
impl From<subscription::Message> for Message {
fn from(val: subscription::Message) -> Self {
Message::Subscription(val)
}
}
pub struct Page {
entity: page::Entity,
device_profiles: page::Entity,
pub(self) model: subscription::Model,
client: Option<audio_client::Client>,
model: model::Model,
sound_config: Option<Config>,
applied_sink_volume: Arc<AtomicU32>,
applied_sink_volume_balance: Arc<AtomicU32>,
applied_source_volume: Arc<AtomicU32>,
amplification_sink: bool,
amplification_source: bool,
}
impl Default for Page {
fn default() -> Self {
let mut model = subscription::Model::default();
model.unplugged_text = fl!("sound-device-port-unplugged");
model.hd_audio_text = fl!("sound-hd-audio");
model.usb_audio_text = fl!("sound-usb-audio");
Self {
entity: page::Entity::default(),
device_profiles: page::Entity::default(),
model,
client: None,
model: model::Model {
text: model::Text {
hd_audio: fl!("sound-hd-audio"),
usb_audio: fl!("sound-usb-audio"),
},
..model::Model::default()
},
sound_config: None,
applied_sink_volume: Arc::new(AtomicU32::new(0)),
applied_sink_volume_balance: Arc::new(AtomicU32::new(0)),
applied_source_volume: Arc::new(AtomicU32::new(0)),
amplification_sink: false,
amplification_source: false,
}
@ -131,12 +136,15 @@ impl page::Page<crate::pages::Message> for Page {
self.entity = entity;
}
fn subscription(
&self,
_core: &cosmic::Core,
) -> cosmic::iced::Subscription<crate::pages::Message> {
cosmic::iced::Subscription::run(subscription::watch)
.map(|message| Message::Subscription(message).into())
fn subscription(&self, _core: &cosmic::Core) -> iced::Subscription<crate::pages::Message> {
iced::Subscription::run(|| {
iced::stream::channel(
1,
move |emitter: futures::channel::mpsc::Sender<crate::pages::Message>| async move {
cosmic_settings_sound::subscribe(emitter, |m| Message::Model(m).into()).await
},
)
})
}
fn on_leave(&mut self) -> Task<crate::pages::Message> {
@ -163,53 +171,126 @@ impl page::AutoBind<crate::pages::Message> for Page {
impl Page {
pub fn update(&mut self, message: Message) -> Task<crate::app::Message> {
tracing::debug!(target: "sound", ?message, "update");
match message {
Message::Surface(a) => return cosmic::task::message(crate::app::Message::Surface(a)),
Message::Subscription(message) => {
return self
.model
.update(message)
.map(|message| Message::Subscription(message).into());
Message::Model(cosmic_settings_sound::Message::Subscription(message)) => {
self.model.update(message);
}
Message::SetSinkBalance(balance) => {
return self
.model
.set_sink_balance(balance)
.map(|message| Message::Subscription(message).into());
Message::Model(cosmic_settings_sound::Message::Client(client)) => {
if let Some(client) = Arc::into_inner(client) {
self.client = Some(client);
self.model = model::Model {
text: model::Text {
hd_audio: fl!("sound-hd-audio"),
usb_audio: fl!("sound-usb-audio"),
},
..model::Model::default()
};
}
}
Message::ReattachClient(client) => {
if let Some(client) = Arc::into_inner(client) {
self.client = Some(client);
}
}
Message::SetDefaultSink(pos) => {
return self
.model
.set_default_sink(pos)
.map(|message| Message::Subscription(message).into());
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.client.as_mut()
{
block_on(async {
_ = client.conn.set_default(node_id, true).await;
});
}
}
Message::SetDefaultSource(pos) => {
return self
.model
.set_default_source(pos)
.map(|message| Message::Subscription(message).into());
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.client.as_mut()
{
block_on(async {
_ = client.conn.set_default(node_id, true).await;
});
}
}
Message::ToggleSinkMute => self.model.toggle_sink_mute(),
Message::ToggleSinkMute => {
if let Some(ref mut client) = self.client {
block_on(async {
_ = client.conn.sink_mute_toggle().await;
});
}
}
Message::ToggleSourceMute => self.model.toggle_source_mute(),
Message::ToggleSourceMute => {
if let Some(ref mut client) = self.client {
block_on(async {
_ = client.conn.source_mute_toggle().await;
});
}
}
Message::SetSinkVolume(volume) => {
return self
.model
.set_sink_volume(volume)
.map(|message| Message::Subscription(message).into());
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.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) => {
return self
.model
.set_source_volume(volume)
.map(|message| Message::Subscription(message).into());
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.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()
});
}
}
Message::SetSinkBalance(balance) => {
self.applied_sink_volume_balance
.store((balance * 1000.0) as u32, atomic::Ordering::Relaxed);
self.model.active_sink.balance = Some(balance);
if let Some((mut client, sink_id)) = self.client.take().zip(self.model.default_sink)
{
let balance = Arc::clone(&self.applied_sink_volume_balance);
return cosmic::Task::future(async move {
tokio::time::sleep(Duration::from_millis(128)).await;
_ = client
.conn
.set_node_volume_balance(
sink_id,
Some(balance.load(atomic::Ordering::Relaxed) as f32 / 1000.0),
)
.await;
Message::ReattachClient(Arc::new(client)).into()
});
}
}
Message::ToggleOverAmplificationSink(enabled) => {
@ -231,18 +312,6 @@ impl Page {
tracing::error!(?why, "Failed to save over amplification setting");
}
}
Message::SetProfile(object_id, index) => {
self.model.set_profile(object_id, index, true);
}
Message::Reload => {
let mut model = subscription::Model::default();
model.hd_audio_text = std::mem::take(&mut self.model.hd_audio_text);
model.unplugged_text = std::mem::take(&mut self.model.unplugged_text);
model.usb_audio_text = std::mem::take(&mut self.model.usb_audio_text);
self.model = model;
}
}
Task::none()
@ -262,17 +331,17 @@ fn input() -> Section<crate::pages::Message> {
.title(fl!("sound-input"))
.descriptions(descriptions)
.view::<Page>(move |_binder, page, section| {
if page.model.sources().is_empty() {
if page.model.sources.id.is_empty() {
return widget::space().into();
}
let slider = if page.amplification_source {
widget::slider(0..=150, page.model.source_volume, |change| {
widget::slider(0..=150, page.model.active_source.volume, |change| {
Message::SetSourceVolume(change).into()
})
.breakpoints(&[100])
} else {
widget::slider(0..=100, page.model.source_volume, |change| {
widget::slider(0..=100, page.model.active_source.volume, |change| {
Message::SetSourceVolume(change).into()
})
}
@ -283,23 +352,25 @@ fn input() -> Section<crate::pages::Message> {
let volume_control = widget::row::with_capacity(4)
.align_y(Alignment::Center)
.push(
widget::button::icon(widget::icon::from_name(if page.model.source_mute {
"microphone-sensitivity-muted-symbolic"
} else {
"audio-input-microphone-symbolic"
}))
widget::button::icon(widget::icon::from_name(
if page.model.active_source.mute {
"microphone-sensitivity-muted-symbolic"
} else {
"audio-input-microphone-symbolic"
},
))
.on_press(Message::ToggleSourceMute.into()),
)
.push(
widget::text::body(&page.model.source_volume_text)
widget::text::body(&page.model.active_source.volume_text)
.width(Length::Fixed(22.0))
.align_x(Alignment::Center),
)
.push(horizontal_space().width(8.))
.push(slider);
let devices = widget::dropdown::popup_dropdown(
page.model.sources(),
Some(page.model.active_source().unwrap_or(0)),
&page.model.sources.sorted_display,
page.model.sources.active(),
Message::SetDefaultSource,
window::Id::RESERVED,
Message::Surface,
@ -346,12 +417,12 @@ fn output() -> Section<crate::pages::Message> {
.descriptions(descriptions)
.view::<Page>(move |_binder, page, section| {
let slider = if page.amplification_sink {
widget::slider(0..=150, page.model.sink_volume, |change| {
widget::slider(0..=150, page.model.active_sink.volume, |change| {
Message::SetSinkVolume(change).into()
})
.breakpoints(&[100])
} else {
widget::slider(0..=100, page.model.sink_volume, |change| {
widget::slider(0..=100, page.model.active_sink.volume, |change| {
Message::SetSinkVolume(change).into()
})
}
@ -362,7 +433,7 @@ fn output() -> Section<crate::pages::Message> {
let volume_control = widget::row::with_capacity(4)
.align_y(Alignment::Center)
.push(
widget::button::icon(if page.model.sink_mute {
widget::button::icon(if page.model.active_sink.mute {
widget::icon::from_name("audio-volume-muted-symbolic")
} else {
widget::icon::from_name("audio-volume-high-symbolic")
@ -370,7 +441,7 @@ fn output() -> Section<crate::pages::Message> {
.on_press(Message::ToggleSinkMute.into()),
)
.push(
widget::text::body(&page.model.sink_volume_text)
widget::text::body(&page.model.active_sink.volume_text)
.width(Length::Fixed(22.0))
.align_x(Alignment::Center),
)
@ -378,8 +449,8 @@ fn output() -> Section<crate::pages::Message> {
.push(slider);
let devices = widget::dropdown::popup_dropdown(
page.model.sinks(),
Some(page.model.active_sink().unwrap_or(0)),
&page.model.sinks.sorted_display,
page.model.sinks.active(),
Message::SetDefaultSink,
window::Id::RESERVED,
Message::Surface,
@ -412,12 +483,12 @@ fn output() -> Section<crate::pages::Message> {
.push(horizontal_space().width(8.))
.push(
widget::slider(
0..=200,
(page.model.sink_balance.unwrap_or(1.0).max(0.) * 100.).round()
as u32,
0.0..=2.0,
page.model.active_sink.balance.unwrap_or(1.0),
|change| Message::SetSinkBalance(change).into(),
)
.breakpoints(&[100]),
.step(0.01)
.breakpoints(&[1.0]),
)
.push(horizontal_space().width(8.))
.push(
@ -453,24 +524,13 @@ fn device_profiles() -> Section<crate::pages::Message> {
.descriptions(descriptions)
.view::<Page>(move |_binder, page, section| {
let descriptions = &section.descriptions;
let button = widget::row::with_children(vec![
horizontal_space().into(),
widget::icon::from_name("go-next-symbolic").size(16).into(),
]);
let device_profiles = settings::item::builder(&*descriptions[button_txt])
.control(button)
.spacing(16)
.apply(widget::container)
.width(Length::Fill)
.class(cosmic::theme::Container::List)
.apply(widget::button::custom)
.width(Length::Fill)
.class(cosmic::theme::Button::Transparent)
.on_press(crate::pages::Message::Page(page.device_profiles))
.width(Length::Fill);
settings::section().add(device_profiles).into()
settings::section()
.add(crate::widget::go_next_item(
&descriptions[button_txt],
crate::pages::Message::Page(page.device_profiles),
))
.into()
})
}

View file

@ -66,7 +66,8 @@ impl page::Page<crate::pages::Message> for Page {
fn on_enter(&mut self) -> Task<crate::pages::Message> {
let (task, handle) = Task::future(async move {
crate::pages::Message::About(Message::Info(Box::new(Info::load())))
let info = Info::load().await;
crate::pages::Message::About(Message::Info(Box::new(info)))
})
.abortable();

View file

@ -3,7 +3,9 @@
#[cfg(feature = "wgpu")]
use cosmic::iced::wgpu;
use std::{collections::HashMap, collections::HashSet, ffi::OsStr, process::Command};
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::process::Command;
#[must_use]
#[derive(Clone, Debug, Default)]
@ -29,7 +31,7 @@ impl Info {
}
#[cfg(feature = "wgpu")]
fn wgpu_graphics() -> Vec<String> {
async fn wgpu_graphics() -> Vec<String> {
let mut graphics = Vec::new();
// Use wgpu to enumerate GPUs. Works cross-platform and doesn't require external tools
@ -43,7 +45,7 @@ impl Info {
let mut seen_devices = HashSet::new();
let mut seen_names = HashSet::new();
for adapter in adapters {
for adapter in adapters.await {
let adapter_info = adapter.get_info();
if adapter_info.device_type == wgpu::DeviceType::Cpu {
@ -140,7 +142,7 @@ impl Info {
graphics
}
pub fn load() -> Info {
pub async fn load() -> Info {
let mut info = Info {
os_architecture: architecture(),
kernel_version: kernel_version(),
@ -192,7 +194,7 @@ impl Info {
#[cfg(feature = "wgpu")]
{
info.graphics = Self::wgpu_graphics();
info.graphics = Self::wgpu_graphics().await;
}
info

View file

@ -4,25 +4,22 @@
mod getent;
use crate::pages;
use cosmic::{
Apply, Element,
dialog::file_chooser,
iced::{Alignment, Length},
widget::{self, column, icon, list, row, settings, space::horizontal, text},
};
use cosmic::dialog::file_chooser;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::space::horizontal;
use cosmic::widget::{self, column, icon, list, row, settings, text};
use cosmic::{Apply, Element};
use cosmic_settings_page::{self as page, Section, section};
use image::GenericImageView;
use pwhash::{bcrypt, md5_crypt, sha256_crypt, sha512_crypt};
use regex::Regex;
use slotmap::SlotMap;
use std::{
collections::HashMap,
fs::File,
future::Future,
io::{BufRead, BufReader},
path::{Path, PathBuf},
sync::Arc,
};
use std::collections::HashMap;
use std::fs::File;
use std::future::Future;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use url::Url;
use zbus_polkit::policykit1::CheckAuthorizationFlags;

View file

@ -2,24 +2,19 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::widget::selection_context_item;
use cosmic::{
Apply, Element, Task,
app::ContextDrawer,
cosmic_config::{self, ConfigGet, ConfigSet},
iced::core::text::Wrapping,
surface,
widget::{self, dropdown, list, settings},
};
use cosmic::app::ContextDrawer;
use cosmic::cosmic_config::{self, ConfigGet, ConfigSet};
use cosmic::iced::core::text::Wrapping;
use cosmic::widget::{self, dropdown, settings};
use cosmic::{Apply, Element, Task, surface};
use cosmic_settings_page::{self as page, Section, section};
use icu::{
calendar::{Gregorian, types::Weekday, week},
datetime::{
DateTimeFormatter, DateTimeFormatterPreferences, fieldsets,
input::{Date, DateTime, Time},
options::TimePrecision,
},
locale::{Locale, preferences::extensions::unicode::keywords::HourCycle},
};
use icu::calendar::types::Weekday;
use icu::calendar::{Gregorian, week};
use icu::datetime::input::{Date, DateTime, Time};
use icu::datetime::options::TimePrecision;
use icu::datetime::{DateTimeFormatter, DateTimeFormatterPreferences, fieldsets};
use icu::locale::Locale;
use icu::locale::preferences::extensions::unicode::keywords::HourCycle;
use slotmap::{Key, SlotMap};
pub use timedate_zbus::TimeDateProxy;
use tracing::error;

View file

@ -7,22 +7,20 @@ use std::sync::Arc;
use crate::widget::selection_context_item;
use cosmic::app::{ContextDrawer, context_drawer};
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{self, button, list};
use cosmic::widget::{self, button};
use cosmic::{Apply, Element};
use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use cosmic_settings_page::{self as page, Section, section};
use eyre::Context;
use icu::{
calendar::{types::Weekday, week},
datetime::{
DateTimeFormatter, DateTimeFormatterPreferences, fieldsets,
input::{Date, DateTime, Time},
},
decimal::{DecimalFormatter, input::Decimal},
locale::Locale,
};
use icu::calendar::types::Weekday;
use icu::calendar::week;
use icu::datetime::input::{Date, DateTime, Time};
use icu::datetime::{DateTimeFormatter, DateTimeFormatterPreferences, fieldsets};
use icu::decimal::DecimalFormatter;
use icu::decimal::input::Decimal;
use icu::locale::Locale;
use locales_rs as locale;
use regex::Regex;
use slotmap::{DefaultKey, SlotMap};
static GNOME_LANGUAGE_SELECTOR: &str = "gnome-language-selector";
@ -242,9 +240,7 @@ impl Page {
let region = region.lang_code.clone();
return cosmic::task::future(async move {
if let Ok(exit_status) = set_locale(lang, region.clone()).await
&& exit_status.success()
{
if set_locale(lang, region.clone()).await.is_ok() {
update_time_settings_after_region_change(region);
}
@ -498,11 +494,8 @@ mod preferred_languages {
use crate::pages::time::region::localized_iso_codes;
use super::Message;
use cosmic::{
Apply,
iced::{Alignment, Length},
widget,
};
use cosmic::iced::{Alignment, Length};
use cosmic::{Apply, widget};
use cosmic_settings_page::Section;
pub fn section() -> Section<crate::pages::Message> {
@ -710,20 +703,26 @@ pub async fn page_reload() -> eyre::Result<PageRefresh> {
let mut available_languages_set = BTreeSet::new();
let output = tokio::process::Command::new("localectl")
.arg("list-locales")
// Use 'locale -a' instead of 'localectl list-locales' for OpenRC compatibility
let output_result = tokio::process::Command::new("locale")
.arg("-a")
.output()
.await
.expect("Failed to run localectl");
.await;
let output = String::from_utf8(output.stdout).unwrap_or_default();
for line in output.lines() {
if line == "C.UTF-8" {
continue;
let locale_list = match output_result {
Ok(output) => {
let output_str = String::from_utf8(output.stdout).unwrap_or_default();
parse_locale_output(&output_str)
}
Err(why) => {
tracing::error!(?why, "failed to list available locales using 'locale -a'");
Vec::new()
}
};
if let Some(locale) = registry.locale(line) {
available_languages_set.insert(localized_locale(&locale, line.to_owned()));
for line in locale_list {
if let Some(locale) = registry.locale(&line) {
available_languages_set.insert(localized_locale(&locale, line));
}
}
@ -841,27 +840,28 @@ fn popover_menu_row(
.apply(Element::from)
}
pub async fn set_locale(
lang: String,
region: String,
) -> Result<std::process::ExitStatus, std::io::Error> {
eprintln!("setting locale lang={lang}, region={region}");
tokio::process::Command::new("localectl")
.arg("set-locale")
.args(&[
["LANG=", &lang].concat(),
["LC_ADDRESS=", &region].concat(),
["LC_IDENTIFICATION=", &region].concat(),
["LC_MEASUREMENT=", &region].concat(),
["LC_MONETARY=", &region].concat(),
["LC_NAME=", &region].concat(),
["LC_NUMERIC=", &region].concat(),
["LC_PAPER=", &region].concat(),
["LC_TELEPHONE=", &region].concat(),
["LC_TIME=", &region].concat(),
])
.status()
/// Sets the system locale using D-Bus instead of localectl for OpenRC compatibility.
pub async fn set_locale(lang: String, region: String) -> eyre::Result<()> {
tracing::debug!("setting locale lang={lang}, region={region}");
let conn = zbus::Connection::system()
.await
.wrap_err("failed to connect to system D-Bus")?;
let proxy = locale1::locale1Proxy::new(&conn)
.await
.wrap_err("failed to create locale1 D-Bus proxy")?;
let locale_settings = build_locale_settings(&lang, &region);
let locale_strs: Vec<&str> = locale_settings.iter().map(|s| s.as_str()).collect();
proxy
.set_locale(&locale_strs, true)
.await
.wrap_err("failed to set locale via D-Bus")?;
tracing::debug!("successfully set locale via D-Bus");
Ok(())
}
/// Sets the user's preferred language list via AccountsService D-Bus.
@ -883,7 +883,7 @@ pub async fn set_user_language(language_list: String) -> eyre::Result<()> {
.await
.wrap_err("failed to set language via AccountsService")?;
eprintln!("set user language via AccountsService: {language_list}");
tracing::debug!("set user language via AccountsService: {language_list}");
Ok(())
}
@ -1016,3 +1016,206 @@ fn strip_locale_suffix(locale: &str) -> String {
.unwrap_or(without_codeset)
.to_string()
}
/// Parses the output from `locale -a` command and returns a vector of locale strings.
/// Filters out pseudo-locales (C, POSIX) and accepts only allowed character encodings.
fn parse_locale_output(output: &str) -> Vec<String> {
// Regex to match pseudo-locales: C or POSIX, optionally followed by .anything
let pseudo_locale_re = Regex::new(r"^(C|POSIX)(\.|$)").unwrap();
// Regex to match UTF-8 encoded locales (case-insensitive)
// Supports optional modifiers after encoding (e.g., @euro, @valencia)
let utf8_encoding_re = Regex::new(r"(?i)\.(utf-?8)(@.*)?$").unwrap();
output
.lines()
.map(|line| line.trim())
.filter(|line| !pseudo_locale_re.is_match(line))
.filter(|line| utf8_encoding_re.is_match(line))
.map(|line| line.to_string())
.collect()
}
/// Builds the locale settings array for D-Bus SetLocale call.
/// Sets LANG to the language parameter and all LC_* variables to the region parameter.
fn build_locale_settings(lang: &str, region: &str) -> Vec<String> {
vec![
format!("LANG={}", lang),
format!("LC_ADDRESS={}", region),
format!("LC_IDENTIFICATION={}", region),
format!("LC_MEASUREMENT={}", region),
format!("LC_MONETARY={}", region),
format!("LC_NAME={}", region),
format!("LC_NUMERIC={}", region),
format!("LC_PAPER={}", region),
format!("LC_TELEPHONE={}", region),
format!("LC_TIME={}", region),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_locale_output_handles_empty_input() {
let output = "";
let result = parse_locale_output(output);
assert_eq!(result.len(), 0);
}
#[test]
fn test_parse_locale_output_preserves_locale_strings() {
let output = "en_US.utf8\nde_DE.utf8\nfr_FR.utf8\n";
let result = parse_locale_output(output);
assert_eq!(result.len(), 3);
assert!(result.contains(&"en_US.utf8".to_string()));
}
#[test]
fn test_build_locale_settings_includes_all_lc_variables() {
let lang = "en_US.UTF-8";
let region = "de_DE.UTF-8";
let settings = build_locale_settings(lang, region);
assert_eq!(settings.len(), 10);
assert!(settings.contains(&format!("LANG={}", lang)));
assert!(settings.contains(&format!("LC_ADDRESS={}", region)));
assert!(settings.contains(&format!("LC_IDENTIFICATION={}", region)));
assert!(settings.contains(&format!("LC_MEASUREMENT={}", region)));
assert!(settings.contains(&format!("LC_MONETARY={}", region)));
assert!(settings.contains(&format!("LC_NAME={}", region)));
assert!(settings.contains(&format!("LC_NUMERIC={}", region)));
assert!(settings.contains(&format!("LC_PAPER={}", region)));
assert!(settings.contains(&format!("LC_TELEPHONE={}", region)));
assert!(settings.contains(&format!("LC_TIME={}", region)));
}
#[test]
fn test_build_locale_settings_uses_correct_values() {
let lang = "fr_FR.UTF-8";
let region = "en_GB.UTF-8";
let settings = build_locale_settings(lang, region);
// LANG should use the lang parameter
assert!(settings.iter().any(|s| s == "LANG=fr_FR.UTF-8"));
// LC_* variables should use the region parameter
assert!(settings.iter().any(|s| s == "LC_TIME=en_GB.UTF-8"));
}
#[test]
fn test_parse_locale_output_filters_pseudo_locales() {
let output = "C\nC.utf8\nC.UTF-8\nPOSIX\nen_US.utf8\nde_DE.UTF-8\n";
let result = parse_locale_output(output);
// Should filter out all C and POSIX variants
assert!(!result.contains(&"C".to_string()));
assert!(!result.contains(&"C.utf8".to_string()));
assert!(!result.contains(&"C.UTF-8".to_string()));
assert!(!result.contains(&"POSIX".to_string()));
// Should keep actual locales
assert!(result.contains(&"en_US.utf8".to_string()));
assert!(result.contains(&"de_DE.UTF-8".to_string()));
assert_eq!(result.len(), 2);
}
#[test]
fn test_parse_locale_output_accepts_only_utf8_locales() {
let output =
"en_US\nen_US.utf8\nen_US.UTF-8\nar_IN\nar_IN.utf8\nde_DE.iso88591\nfr_FR.UTF-8\n";
let result = parse_locale_output(output);
// Should accept UTF-8 variants
assert!(result.contains(&"en_US.utf8".to_string()));
assert!(result.contains(&"en_US.UTF-8".to_string()));
assert!(result.contains(&"ar_IN.utf8".to_string()));
assert!(result.contains(&"fr_FR.UTF-8".to_string()));
// Should filter out non-UTF-8 encoded locales
assert!(!result.contains(&"en_US".to_string()));
assert!(!result.contains(&"ar_IN".to_string()));
assert!(!result.contains(&"de_DE.iso88591".to_string()));
assert_eq!(result.len(), 4);
}
#[test]
fn test_parse_locale_output_filters_any_c_posix_variant() {
let output = "C\nC.iso88591\nC.anything\nPOSIX\nPOSIX.utf8\nen_US.utf8\n";
let result = parse_locale_output(output);
// Should filter out any C or POSIX variant regardless of encoding
assert!(!result.contains(&"C".to_string()));
assert!(!result.contains(&"C.iso88591".to_string()));
assert!(!result.contains(&"C.anything".to_string()));
assert!(!result.contains(&"POSIX".to_string()));
assert!(!result.contains(&"POSIX.utf8".to_string()));
// Should keep actual locales
assert!(result.contains(&"en_US.utf8".to_string()));
assert_eq!(result.len(), 1);
}
#[test]
fn test_parse_locale_output_handles_whitespace() {
let output = " en_US.utf8 \n\t de_DE.UTF-8\t\n fr_FR.utf8 \n";
let result = parse_locale_output(output);
// Should handle leading/trailing whitespace
assert!(result.contains(&"en_US.utf8".to_string()));
assert!(result.contains(&"de_DE.UTF-8".to_string()));
assert!(result.contains(&"fr_FR.utf8".to_string()));
assert_eq!(result.len(), 3);
}
#[test]
fn test_parse_locale_output_handles_empty_lines() {
let output = "en_US.utf8\n\n\nde_DE.UTF-8\n\n";
let result = parse_locale_output(output);
// Should skip empty lines
assert!(result.contains(&"en_US.utf8".to_string()));
assert!(result.contains(&"de_DE.UTF-8".to_string()));
assert_eq!(result.len(), 2);
}
#[test]
fn test_parse_locale_output_catalan_not_filtered_as_pseudo() {
let output = "C\nca_ES.UTF-8\nca_ES.utf8\ncs_CZ.UTF-8\nen_US.utf8\n";
let result = parse_locale_output(output);
// Should filter out C but not Catalan (ca_*) or Czech (cs_*)
assert!(!result.contains(&"C".to_string()));
assert!(result.contains(&"ca_ES.UTF-8".to_string()));
assert!(result.contains(&"ca_ES.utf8".to_string()));
assert!(result.contains(&"cs_CZ.UTF-8".to_string()));
assert_eq!(result.len(), 4);
}
#[test]
fn test_parse_locale_output_handles_locale_modifiers() {
let output = "en_US.UTF-8@euro\nca_ES.UTF-8@valencia\nde_DE.utf8\n";
let result = parse_locale_output(output);
// Locales with modifiers should be accepted
assert!(result.contains(&"en_US.UTF-8@euro".to_string()));
assert!(result.contains(&"ca_ES.UTF-8@valencia".to_string()));
assert!(result.contains(&"de_DE.utf8".to_string()));
assert_eq!(result.len(), 3);
}
#[test]
fn test_parse_locale_output_case_variations() {
let output = "en_US.UTF-8\nen_US.utf-8\nen_US.utf8\nen_US.UTF8\nde_DE.Utf8\n";
let result = parse_locale_output(output);
// All case variations should be accepted (case-insensitive regex)
assert!(result.contains(&"en_US.UTF-8".to_string()));
assert!(result.contains(&"en_US.utf-8".to_string()));
assert!(result.contains(&"en_US.utf8".to_string()));
assert!(result.contains(&"en_US.UTF8".to_string()));
assert!(result.contains(&"de_DE.Utf8".to_string()));
assert_eq!(result.len(), 5);
}
}

View file

@ -0,0 +1,631 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
//! Service manager abstraction for managing system services.
//!
//! This module provides a trait-based interface for interacting with system
//! service managers (like systemd, OpenRC, etc.), allowing for testability
//! and flexibility across different init systems.
use std::future::Future;
use std::pin::Pin;
/// Trait for managing system services.
///
/// Implementations are bound to a specific service name and provide
/// methods to query service state and perform lifecycle operations.
pub trait ServiceManager {
/// Whether the service is configured to start on boot.
fn is_enabled(&self) -> bool;
/// Whether the service is currently running.
fn is_active(&self) -> bool;
/// Start the service.
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
/// Enable the service to start on boot and start it now.
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
/// Whether the service is installed on this system.
fn is_installed(&self) -> bool;
}
/// A newtype around `Box<dyn ServiceManager>` that enables `#[derive(Default)]`
/// on containing structs. Methods on `ServiceManager` are accessible via `Deref`.
///
/// The `Default` impl creates a manager for the `"bluetooth"` service, which is
/// currently the only consumer. If other services need this in the future the
/// default can be made configurable.
pub struct ServiceManagerHandle(Box<dyn ServiceManager>);
impl ServiceManagerHandle {
pub fn new(manager: Box<dyn ServiceManager>) -> Self {
Self(manager)
}
}
impl std::ops::Deref for ServiceManagerHandle {
type Target = dyn ServiceManager;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl Default for ServiceManagerHandle {
fn default() -> Self {
Self(create_default_service_manager("bluetooth"))
}
}
/// Log a warning or error after a privileged command completes.
fn log_command_result(
result: &Result<std::process::ExitStatus, std::io::Error>,
description: &str,
service: &str,
) {
match result {
Ok(status) if status.success() => {}
Ok(status) => tracing::warn!(
"{} for '{}' failed with exit code: {:?}",
description,
service,
status.code(),
),
Err(e) => tracing::error!("Failed to execute {} for '{}': {}", description, service, e,),
}
}
/// Run a privileged command via `pkexec`.
///
/// This is the common async entry point for service management actions
/// that require elevated privileges.
async fn run_pkexec_command(args: &[&str], description: &str, service: &str) {
let result = tokio::process::Command::new("pkexec")
.args(args)
.status()
.await;
log_command_result(&result, description, service);
}
/// Mock ServiceManager that returns configurable boolean values.
#[cfg(test)]
pub struct MockServiceManager {
enabled: bool,
active: bool,
installed: bool,
}
#[cfg(test)]
impl MockServiceManager {
pub fn new(enabled: bool, active: bool) -> Self {
Self {
enabled,
active,
installed: true,
}
}
pub fn with_installed(mut self, installed: bool) -> Self {
self.installed = installed;
self
}
}
#[cfg(test)]
impl ServiceManager for MockServiceManager {
fn is_enabled(&self) -> bool {
self.enabled
}
fn is_active(&self) -> bool {
self.active
}
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn is_installed(&self) -> bool {
self.installed
}
}
/// SystemD implementation of ServiceManager.
#[cfg(feature = "systemd")]
pub struct SystemDServiceManager {
service_name: String,
}
#[cfg(feature = "systemd")]
impl SystemDServiceManager {
pub fn new(service_name: impl Into<String>) -> Self {
Self {
service_name: service_name.into(),
}
}
}
#[cfg(feature = "systemd")]
impl ServiceManager for SystemDServiceManager {
fn is_enabled(&self) -> bool {
std::process::Command::new("systemctl")
.args(["is-enabled", &self.service_name])
.status()
.map(|status| status.success())
.unwrap_or(true)
}
fn is_active(&self) -> bool {
std::process::Command::new("systemctl")
.args(["is-active", &self.service_name])
.status()
.map(|status| status.success())
.unwrap_or(true)
}
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let service = self.service_name.clone();
Box::pin(async move {
run_pkexec_command(
&["systemctl", "start", &service],
"systemctl start",
&service,
)
.await;
})
}
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let service = self.service_name.clone();
Box::pin(async move {
run_pkexec_command(
&["systemctl", "enable", "--now", &service],
"systemctl enable --now",
&service,
)
.await;
})
}
fn is_installed(&self) -> bool {
std::process::Command::new("systemctl")
.args(["cat", &self.service_name])
.status()
.map(|status| status.success())
.unwrap_or(false)
}
}
/// OpenRC implementation of ServiceManager.
#[cfg(feature = "openrc")]
pub struct OpenRcServiceManager {
service_name: String,
}
#[cfg(feature = "openrc")]
impl OpenRcServiceManager {
pub fn new(service_name: impl Into<String>) -> Self {
Self {
service_name: service_name.into(),
}
}
}
/// Parses `rc-update show` output to check if a service is in any runlevel.
///
/// Uses exact service name matching to avoid false positives from prefix matches
/// (e.g., "bluetoothd" should not match a lookup for "bluetooth").
#[cfg(any(feature = "openrc", test))]
fn is_service_in_runlevel_output(output: &str, service_name: &str) -> bool {
output.lines().any(|line| {
line.split('|')
.next()
.map(|name| name.trim() == service_name)
.unwrap_or(false)
})
}
#[cfg(feature = "openrc")]
impl ServiceManager for OpenRcServiceManager {
fn is_enabled(&self) -> bool {
std::process::Command::new("rc-update")
.args(["show"])
.output()
.map(|output| {
is_service_in_runlevel_output(
&String::from_utf8_lossy(&output.stdout),
&self.service_name,
)
})
.unwrap_or(true)
}
fn is_active(&self) -> bool {
std::process::Command::new("rc-service")
.args([&self.service_name, "status"])
.status()
.map(|status| status.success())
.unwrap_or(true)
}
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let service = self.service_name.clone();
Box::pin(async move {
run_pkexec_command(
&["rc-service", &service, "start"],
"rc-service start",
&service,
)
.await;
})
}
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let service = self.service_name.clone();
Box::pin(async move {
run_pkexec_command(
&["rc-update", "add", &service, "default"],
"rc-update add",
&service,
)
.await;
run_pkexec_command(
&["rc-service", &service, "start"],
"rc-service start",
&service,
)
.await;
})
}
fn is_installed(&self) -> bool {
std::path::Path::new("/etc/init.d")
.join(&self.service_name)
.exists()
}
}
/// Detect the running service manager and create a manager for the named service.
///
/// Checks runtime directories (`/run/systemd/system`, `/run/openrc`) to determine
/// which service manager is actively running, rather than relying on which binaries
/// are installed. This avoids false detections when multiple service managers are
/// installed but only one is actively managing services.
pub fn detect_service_manager(
service_name: impl Into<String>,
) -> Result<Box<dyn ServiceManager>, String> {
let service_name = service_name.into();
#[cfg(feature = "systemd")]
{
if std::path::Path::new("/run/systemd/system").exists() {
tracing::debug!("Detected systemd service manager via /run/systemd/system");
return Ok(Box::new(SystemDServiceManager::new(service_name)));
}
}
#[cfg(feature = "openrc")]
{
if std::path::Path::new("/run/openrc").exists() {
tracing::debug!("Detected OpenRC service manager via /run/openrc");
return Ok(Box::new(OpenRcServiceManager::new(service_name)));
}
}
{
let mut checked = Vec::new();
#[cfg(feature = "systemd")]
checked.push("systemd (/run/systemd/system)");
#[cfg(feature = "openrc")]
checked.push("openrc (/run/openrc)");
Err(format!(
"Could not detect a running service manager. Checked for: {}. \
None of these service managers appear to be running on this system.",
checked.join(", ")
))
}
}
/// Factory: returns `MockServiceManager` in tests, detects the running service manager in production.
#[cfg(test)]
pub fn create_default_service_manager(_service_name: impl Into<String>) -> Box<dyn ServiceManager> {
Box::new(MockServiceManager::new(false, false))
}
#[cfg(not(test))]
pub fn create_default_service_manager(service_name: impl Into<String>) -> Box<dyn ServiceManager> {
let service_name = service_name.into();
detect_service_manager(&service_name).unwrap_or_else(|e| {
tracing::warn!(
"Failed to detect service manager: {}. Service management features will not be available.",
e
);
tracing::warn!(
"Using no-op service manager for '{}'. Service status will always report as enabled/active, but operations will not actually execute.",
service_name
);
// Graceful degradation — app continues but service management won't work
Box::new(NoOpServiceManager)
})
}
/// Fallback when no service manager is detected — reports everything as active but does nothing.
struct NoOpServiceManager;
impl ServiceManager for NoOpServiceManager {
fn is_enabled(&self) -> bool {
true
}
fn is_active(&self) -> bool {
true
}
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn is_installed(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use tracing_subscriber::util::SubscriberInitExt;
#[test]
fn test_service_manager_trait_is_enabled() {
struct TestServiceManager {
enabled: bool,
installed: bool,
}
impl ServiceManager for TestServiceManager {
fn is_enabled(&self) -> bool {
self.enabled
}
fn is_active(&self) -> bool {
true
}
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn is_installed(&self) -> bool {
self.installed
}
}
let test_service = TestServiceManager {
enabled: true,
installed: true,
};
assert!(test_service.is_enabled());
let disabled_service = TestServiceManager {
enabled: false,
installed: true,
};
assert!(!disabled_service.is_enabled());
}
#[test]
fn test_service_manager_trait_is_active() {
struct TestServiceManager {
active: bool,
installed: bool,
}
impl ServiceManager for TestServiceManager {
fn is_enabled(&self) -> bool {
true
}
fn is_active(&self) -> bool {
self.active
}
fn activate(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn enable(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async {})
}
fn is_installed(&self) -> bool {
self.installed
}
}
let test_service = TestServiceManager {
active: true,
installed: true,
};
assert!(test_service.is_active());
let inactive_service = TestServiceManager {
active: false,
installed: true,
};
assert!(!inactive_service.is_active());
}
#[test]
fn test_mock_service_manager_returns_configured_values() {
let bluetooth = MockServiceManager::new(true, true);
assert!(bluetooth.is_enabled());
assert!(bluetooth.is_active());
let disabled_bluetooth = MockServiceManager::new(false, false);
assert!(!disabled_bluetooth.is_enabled());
assert!(!disabled_bluetooth.is_active());
}
#[test]
#[cfg(feature = "systemd")]
fn test_systemd_service_manager_implements_trait() {
let bluetooth = SystemDServiceManager::new("bluetooth");
// Can't test actual systemd calls in unit tests, but verify the struct exists
let _enabled: bool = bluetooth.is_enabled();
let _active: bool = bluetooth.is_active();
}
#[tokio::test]
async fn test_service_manager_trait_has_activate_method() {
let bluetooth = MockServiceManager::new(false, false);
bluetooth.activate().await;
}
#[tokio::test]
async fn test_service_manager_trait_has_enable_method() {
let bluetooth = MockServiceManager::new(false, false);
bluetooth.enable().await;
}
#[tokio::test]
async fn test_service_manager_activate_returns_future() {
let bluetooth = MockServiceManager::new(false, false);
let future = bluetooth.activate();
future.await;
}
#[tokio::test]
async fn test_service_manager_enable_returns_future() {
let bluetooth = MockServiceManager::new(false, false);
let future = bluetooth.enable();
future.await;
}
#[test]
fn test_default_service_manager_uses_mock_in_tests() {
let bluetooth = create_default_service_manager("bluetooth");
assert!(!bluetooth.is_enabled());
assert!(!bluetooth.is_active());
}
#[test]
#[cfg(feature = "openrc")]
fn test_openrc_service_manager_implements_trait() {
let bluetooth = OpenRcServiceManager::new("bluetooth");
// Can't test actual OpenRC calls in unit tests, but verify the struct exists
let _enabled: bool = bluetooth.is_enabled();
let _active: bool = bluetooth.is_active();
}
#[test]
fn test_detect_service_manager_returns_result() {
let result = detect_service_manager("test-service");
match result {
Ok(_) => {}
Err(e) => {
assert!(!e.is_empty());
assert!(
e.contains("service manager"),
"Error message should mention service manager: {}",
e
);
}
}
}
#[test]
fn test_mock_service_manager_accepts_service_name() {
let bluetooth = MockServiceManager::new(true, true);
assert!(bluetooth.is_enabled());
}
#[test]
fn test_mock_service_manager_is_installed_defaults_to_true() {
let bluetooth = MockServiceManager::new(true, true);
assert!(bluetooth.is_installed());
}
#[test]
fn test_mock_service_manager_with_installed_configures_state() {
let not_installed = MockServiceManager::new(true, true).with_installed(false);
assert!(!not_installed.is_installed());
let installed = MockServiceManager::new(true, true).with_installed(true);
assert!(installed.is_installed());
}
#[test]
fn test_no_op_service_manager_reports_not_installed() {
let no_op = NoOpServiceManager;
// When no service manager is detected at runtime, the fallback must report
// not-installed so DBusServiceUnknown falls through to the unknown-service path
// instead of showing a non-functional toggle.
assert!(!no_op.is_installed());
}
#[test]
fn test_parse_rc_update_output_exact_service_name() {
// Must use exact matching, not prefix matching — "bluetoothd" must not match "bluetooth".
let output = " bluetoothd | default\n";
let found = is_service_in_runlevel_output(output, "bluetooth");
assert!(!found);
}
#[test]
fn test_parse_rc_update_output_matches_exact_name() {
let output = " bluetooth | default\n";
let found = is_service_in_runlevel_output(output, "bluetooth");
assert!(found);
}
#[test]
fn test_parse_rc_update_output_not_found() {
let output = " dbus | default\n network | sysinit\n";
let found = is_service_in_runlevel_output(output, "bluetooth");
assert!(!found);
}
#[test]
fn test_log_command_result_logs_all_branches() {
let _guard = tracing_subscriber::fmt().with_test_writer().set_default();
let err_result: Result<std::process::ExitStatus, std::io::Error> = Err(
std::io::Error::new(std::io::ErrorKind::NotFound, "command not found"),
);
log_command_result(&err_result, "test command", "test-service");
let fail_result = std::process::Command::new("false").status();
log_command_result(&fail_result, "test false", "test-service");
let ok_result = std::process::Command::new("true").status();
log_command_result(&ok_result, "test true", "test-service");
}
#[test]
fn test_parse_rc_update_output_multi_line() {
let output = " dbus | default\n bluetooth | default\n network | sysinit\n";
let found = is_service_in_runlevel_output(output, "bluetooth");
assert!(found);
}
}

View file

@ -2,11 +2,9 @@ use std::any::TypeId;
use ashpd::desktop::location::{Location, LocationProxy};
use chrono::NaiveDate;
use cosmic::iced::{
Subscription,
futures::{SinkExt, StreamExt, channel::mpsc::Sender, future},
stream,
};
use cosmic::iced::futures::channel::mpsc::Sender;
use cosmic::iced::futures::{SinkExt, StreamExt, future};
use cosmic::iced::{Subscription, stream};
use sunrise::{Coordinates, SolarDay, SolarEvent};
use tokio::select;

View file

@ -1,8 +1,5 @@
use cosmic::iced::{
Subscription,
futures::{self, SinkExt},
stream,
};
use cosmic::iced::futures::{self, SinkExt};
use cosmic::iced::{Subscription, stream};
use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::fmt::Debug;
use std::hash::Hash;

View file

@ -1,10 +1,8 @@
use std::path::PathBuf;
use cosmic::iced::{
Subscription,
futures::{SinkExt, StreamExt, channel::mpsc::Sender, future},
stream,
};
use cosmic::iced::futures::channel::mpsc::Sender;
use cosmic::iced::futures::{SinkExt, StreamExt, future};
use cosmic::iced::{Subscription, stream};
use image::{ImageBuffer, Rgba};
#[derive(Clone, Debug)]

View file

@ -1,7 +1,8 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{iced::Border, theme};
use cosmic::iced::Border;
use cosmic::theme;
#[must_use]
pub fn display_container_frame() -> cosmic::theme::Container<'static> {

View file

@ -1,4 +1,5 @@
use std::{future::Future, io, process};
use std::future::Future;
use std::{io, process};
use futures::future::select;

View file

@ -8,10 +8,9 @@ use cosmic::cosmic_theme::Spacing;
use cosmic::iced::core::text::Wrapping;
use cosmic::iced::{Alignment, Length};
use cosmic::widget::color_picker::ColorPickerUpdate;
use cosmic::widget::space::{horizontal, vertical};
use cosmic::widget::{
self, ColorPickerModel, button, column, container, divider, icon, list, row, settings,
space::{horizontal, vertical},
text,
self, ColorPickerModel, button, column, container, divider, icon, list, row, settings, text,
};
use cosmic::{Apply, Element, theme};
use cosmic_settings_page as page;
@ -206,8 +205,10 @@ pub fn go_next_with_item<'a, Msg: 'static>(
msg_opt: impl Into<Option<Msg>>,
) -> list::ListButton<'a, Msg> {
settings::item_row(vec![
text::body(description).wrapping(Wrapping::Word).into(),
horizontal().into(),
text::body(description)
.width(Length::Fill)
.wrapping(Wrapping::Word)
.into(),
row::with_capacity(2)
.push(item)
.push(icon::from_name("go-next-symbolic").size(16).icon())

View file

@ -1,21 +0,0 @@
[package]
name = "cosmic-pipewire"
version = "1.0.7"
edition = "2024"
repository = "https://github.com/pop-os/cosmic-settings"
rust-version.workspace = true
license = "MPL-2.0"
publish = true
[dependencies]
intmap = "3.1.3"
libspa = "0.9.2"
libspa-sys = "0.9.2"
pipewire = "0.9"
serde = { version = "1.0.228", features = ["derive"]}
serde_json = "1.0.149"
tracing = "0.1.44"
[features]
# Cache route port types
route-port-type = []

View file

@ -1,358 +0,0 @@
Mozilla Public License Version 2.0
==================================
## 1. Definitions
### 1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
### 1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
### 1.3. "Contribution"
means Covered Software of a particular Contributor.
### 1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
### 1.5. "Incompatible With Secondary Licenses"
means
+ (a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
+ (b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
### 1.6. "Executable Form"
means any form of the work other than Source Code Form.
### 1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
### 1.8. "License"
means this document.
### 1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
### 1.10. "Modifications"
means any of the following:
+ (a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
+ (b) any new file in Source Code Form that contains any Covered
Software.
### 1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
### 1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
### 1.13. "Source Code Form"
means the form of the work preferred for making modifications.
### 1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
## 2. License Grants and Conditions
### 2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
+ (a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
+ (b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
### 2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
### 2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
+ (a) for any code that a Contributor has removed from Covered Software;
or
+ (b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
+ (c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
### 2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
### 2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
### 2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
### 2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
## 3. Responsibilities
### 3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
### 3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
+ (a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
+ (b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
### 3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
### 3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
### 3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
## 4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
## 5. Termination
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
## 6. Disclaimer of Warranty
**Covered Software is provided under this License on an "as is"
basis, without warranty of any kind, either expressed, implied, or
statutory, including, without limitation, warranties that the
Covered Software is free of defects, merchantable, fit for a
particular purpose or non-infringing. The entire risk as to the
quality and performance of the Covered Software is with You.
Should any Covered Software prove defective in any respect, You
(not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an
essential part of this License. No use of any Covered Software is
authorized under this License except under this disclaimer.**
#7. Limitation of Liability
**Under no circumstances and under no legal theory, whether tort
(including negligence), contract, or otherwise, shall any
Contributor, or anyone who distributes Covered Software as
permitted above, be liable to You for any direct, indirect,
special, incidental, or consequential damages of any character
including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any
and all other commercial damages or losses, even if such party
shall have been informed of the possibility of such damages. This
limitation of liability shall not apply to liability for death or
personal injury resulting from such party's negligence to the
extent applicable law prohibits such limitation. Some
jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and
limitation may not apply to You.**
## 8. Litigation
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
## 9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
## 10. Versions of the License
### 10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
### 10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
### 10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
### 10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
## Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
## Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -1,27 +0,0 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use pipewire::device::DeviceInfoRef;
/// Device information
#[must_use]
#[derive(Clone, Debug)]
pub struct Device {
pub id: u32,
pub name: String,
}
impl Device {
/// Attains process info from a pipewire info node.
#[must_use]
pub fn from_device(info: &DeviceInfoRef) -> Option<Self> {
let props = info.props()?;
let device = Device {
id: props.get("object.id")?.parse::<u32>().ok()?,
name: props.get("device.description")?.to_owned(),
};
Some(device)
}
}

View file

@ -21,20 +21,19 @@ pub use route::{Route, RouteProps};
mod spa_utils;
pub use spa_utils::Channel;
use libspa::{
param::{ParamType, format::FormatProperties},
pod::{self, Pod, serialize::PodSerializer},
utils::SpaTypes,
};
use pipewire::{
device::{DeviceChangeMask, DeviceListener},
main_loop::MainLoopWeak,
metadata::MetadataListener,
node::NodeListener,
proxy::{ProxyListener, ProxyT},
types::ObjectType,
};
use std::{cell::RefCell, rc::Rc};
use libspa::param::ParamType;
use libspa::param::format::FormatProperties;
use libspa::pod::serialize::PodSerializer;
use libspa::pod::{self, Pod};
use libspa::utils::SpaTypes;
use pipewire::device::{DeviceChangeMask, DeviceListener};
use pipewire::main_loop::MainLoopWeak;
use pipewire::metadata::MetadataListener;
use pipewire::node::NodeListener;
use pipewire::proxy::{ProxyListener, ProxyT};
use pipewire::types::ObjectType;
use std::cell::RefCell;
use std::rc::Rc;
pub type NodeId = u32;
pub type RouteId = u32;

View file

@ -1,8 +1,10 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use crate::{Channel, spa_utils::array_from_pod};
use libspa::{pod::Pod, utils::Id};
use crate::Channel;
use crate::spa_utils::array_from_pod;
use libspa::pod::Pod;
use libspa::utils::Id;
use pipewire::node::{NodeInfoRef, NodeState};
use std::ffi::c_float;

View file

@ -1,98 +0,0 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
//! Currently unusued
use crate::pipewire::Direction;
use pipewire::port::PortInfoRef;
#[must_use]
#[derive(Clone, Debug)]
pub struct Port {
pub node_id: u32,
pub object_id: u32,
pub port_id: u32,
pub audio_channel: String,
pub format_dsp: String,
pub object_path: String,
pub port_direction: Direction,
pub port_group: String,
pub port_name: String,
pub port_alias: String,
pub port_physical: bool,
pub port_terminal: bool,
pub port_monitor: bool,
}
impl Port {
/// Attains process info from a pipewire info port.
#[must_use]
pub fn from_port(info: &PortInfoRef) -> Option<Self> {
let props = info.props()?;
let object_id = info.id();
let port_direction = match info.direction() {
libspa::utils::Direction::Input => Direction::Input,
libspa::utils::Direction::Output => Direction::Output,
_ => return None,
};
let mut node_id = 0;
let mut port_id = 0;
let mut port_monitor = false;
let mut port_physical = false;
let mut port_terminal = false;
let mut audio_channel = String::new();
let mut format_dsp = String::new();
let mut object_path = String::new();
let mut port_alias = String::new();
let mut port_group = String::new();
let mut port_name = String::new();
for (entry, value) in props.iter() {
match entry {
// 32 bit float mono audio
"format.dsp" => format_dsp = value.to_owned(),
// FR
"audio.channel" => audio_channel = value.to_owned(),
// playback
"port.group" => port_group = value.to_owned(),
// 1
"port.id" => port_id = value.parse::<u32>().ok()?,
// false
"port.monitor" => port_monitor = value == "true",
// true
"port.physical" => port_physical = value == "true",
// true
"port.terminal" => port_terminal = value == "true",
// alsa:acp:Device:3:playback:playback_1
"object.path" => object_path = value.to_owned(),
// playback_FR
"port.name" => port_name = value.to_owned(),
// MosArt USB Audio Device:playback_FR
"port.alias" => port_alias = value.to_owned(),
// 59
"node.id" => node_id = value.parse::<u32>().ok()?,
_ => (),
}
}
let port = Port {
format_dsp,
audio_channel,
port_id,
port_direction,
object_path,
port_name,
port_alias,
port_group,
port_monitor,
port_physical,
port_terminal,
node_id,
object_id,
};
Some(port)
}
}

View file

@ -1,7 +1,8 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use crate::{Availability, spa_utils::string_from_pod};
use crate::Availability;
use crate::spa_utils::string_from_pod;
use libspa::pod::Pod;
#[derive(Clone, Debug, Default)]

View file

@ -3,11 +3,10 @@
use std::ffi::{c_float, c_int};
use crate::{
Availability, Channel, Direction,
spa_utils::{array_from_pod, string_from_pod},
};
use libspa::{pod::Pod, utils::Id};
use crate::spa_utils::{array_from_pod, string_from_pod};
use crate::{Availability, Channel, Direction};
use libspa::pod::Pod;
use libspa::utils::Id;
#[derive(Clone, Debug, Default)]
pub struct Route {

View file

@ -1,150 +0,0 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use libspa::pod::Pod;
use std::ffi::CStr;
/// Read a `Pod`'s string if it contains a string.
pub fn string_from_pod(pod: &Pod) -> Option<String> {
if !pod.is_string() {
return None;
}
let mut cstr = std::ptr::null();
unsafe {
// SAFETY: Pod is checked to be a string beforehand
if libspa_sys::spa_pod_get_string(pod.as_raw_ptr(), &mut cstr) == 0 && !cstr.is_null() {
return Some(String::from_utf8_lossy(CStr::from_ptr(cstr).to_bytes()).into_owned());
}
}
None
}
/// SAFETY: Must be absolutely certain that the array is a compatible array.
pub unsafe fn array_from_pod<CType: Copy>(pod: &Pod) -> Option<Vec<CType>> {
if !pod.is_array() {
return None;
}
let mut len = 0;
unsafe {
let array: *mut CType = libspa_sys::spa_pod_get_array(pod.as_raw_ptr(), &mut len).cast();
if array.is_null() {
return None;
}
Some(std::slice::from_raw_parts(array, len as usize).to_vec())
}
}
#[repr(u32)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum Channel {
#[default]
UNKNOWN = 0, // unspecified
NA, // N/A, silent
MONO, // mono stream
FL, // front left
FR, // front right
FC, // front center
LFE, // LFE
SL, // side left
SR, // side right
FLC, // front left center
FRC, // front right center
RC, // rear center
RL, // rear left
RR, // rear right
TC, // top center
TFL, // top front left
TFC, // top front center
TFR, // top front right
TRL, // top rear left
TRC, // top rear center
TRR, // top rear right
RLC, // rear left center
RRC, // rear right center
FLW, // front left wide
FRW, // front right wide
LFE2, // LFE 2
FLH, // front left high
FCH, // front center high
FRH, // front right high
TFLC, // top front left center
TFRC, // top front right center
TSL, // top side left
TSR, // top side right
LLFE, // left LFE
RLFE, // right LFE
BC, // bottom center
BLC, // bottom left center
BRC = 37, // bottom right center
AUX0 = 4096, // aux channels
AUX1,
AUX2,
AUX3,
AUX4,
AUX5,
AUX6,
AUX7,
AUX8,
AUX9,
AUX10,
AUX11,
AUX12,
AUX13,
AUX14,
AUX15,
AUX16,
AUX17,
AUX18,
AUX19,
AUX20,
AUX21,
AUX22,
AUX23,
AUX24,
AUX25,
AUX26,
AUX27,
AUX28,
AUX29,
AUX30,
AUX31,
AUX32,
AUX33,
AUX34,
AUX35,
AUX36,
AUX37,
AUX38,
AUX39,
AUX40,
AUX41,
AUX42,
AUX43,
AUX44,
AUX45,
AUX46,
AUX47,
AUX48,
AUX49,
AUX50,
AUX51,
AUX52,
AUX53,
AUX54,
AUX55,
AUX56,
AUX57,
AUX58,
AUX59,
AUX60,
AUX61,
AUX62,
AUX63 = 4159,
}

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
cosmic-settings (1.0.12) noble; urgency=medium
* release: 1.0.12
-- Michael Murphy <mmstick@pop-os> Mon, 27 Apr 2026 15:24:27 +0200
cosmic-settings (1.0.7) noble; urgency=medium
* release: 1.0.7

4
debian/control vendored
View file

@ -8,15 +8,14 @@ Build-Depends:
fonts-open-sans,
just,
libclang-dev,
libdav1d-dev,
libexpat1-dev,
libfontconfig-dev,
libfreetype-dev,
libinput-dev,
libpipewire-0.3-dev,
libudev-dev,
libwayland-dev,
libxkbcommon-dev,
mold,
pkg-config,
rust-all,
Standards-Version: 4.6.2
@ -29,6 +28,7 @@ Depends:
${shlibs:Depends},
accountsservice,
cosmic-randr,
cosmic-settings-daemon,
gettext,
iso-codes,
network-manager-gnome,

7
debian/rules vendored
View file

@ -1,6 +1,7 @@
#!/usr/bin/make -f
export DESTDIR = debian/tmp
OS_VERSION = $(shell rg VERSION_ID /etc/os-release | awk -F\" '{print $2}')
%:
dh $@
@ -9,7 +10,11 @@ override_dh_auto_clean:
ischroot || just vendor
override_dh_auto_build:
test -e vendor.tar && just build-vendored || just
ifeq ($(OS_VERSION),22.04)
test -e vendor.tar && just build-vendored || just build-release --no-default-features
else
test -e vendor.tar && just build-vendored || just build-release
endif
override_dh_auto_install:
just rootdir=$(DESTDIR) install

View file

@ -107,6 +107,8 @@ vpn-error = خطأ في VPN
.connect = فشل الاتصال بـ VPN
.connection-editor = فشل محرر الاتصال
.connection-settings = فشل الحصول على إعدادات الاتصالات النشطة
.openvpn-config-path = مسار ملف غير صالح لإعدادات OpenVPN
.openvpn-config-path-desc = يجب أن يكون الملف المختار موجودًا على نظام الملفات المحلي.
.updating-state = فشل تحديث حالة مدير الشبكة
.wireguard-config-path = مسار ملف غير صالح لتكوين WireGuard
.wireguard-config-path-desc = يجب أن يكون الملف المختار موجودًا على نظام ملفات محلي.
@ -911,3 +913,9 @@ xdg-entry-displays-keywords = COSMIC;عرض;
xdg-entry-workspaces = مساحات العمل
xdg-entry-wired-comment = الاتصالات السلكية وملفات تعريف الاتصال
no-search-results = لا توجد شبكات تطابق بحثك.
workspaces-navigation = التنقل
.wraparound = تنقل بين مساحة العمل الأولى والأخيرة باستخدام اختصارات لوحة المفاتيح والإيماءات
bluetooth-display-passkey = إقران البلوتوث
.description = يُرجى إدخال مفتاح المرور التالي على { $device }، ثم الضغط على مفتاح Enter
bluetooth-display-pin = إقتران البلوتوث
.description = يُرجى إدخال رمز PIN التالي على { $device }، ثم الضغط على Enter

View file

@ -0,0 +1,7 @@
cancel = Cancel·lar
confirm = Confirmar
password = Contrasenya
identity = Identitat
password-confirm = Confirmeu la contrasenya
connected = Connectat
settings = Configuració

View file

@ -280,7 +280,7 @@ keyboard-sources = Vstupní zdroje
keyboard-special-char = Zadávání speciálních znaků
.alternate = Klávesa pro náhradní znaky
.compose = Klávesa Compose
.compose-desc = Klávesa Compose umožňuje zadávat širokou škálu znaků. Pro její použití stiskněte Compose a poté zadejte sekvenci znaků. Například [Compose, C, O] vloží znak „©“, zatímco [Compose, A, ] znak „á“.
.compose-desc = Klávesa Compose umožňuje zadávat širokou škálu znaků. Pro její použití stiskněte Compose a poté zadejte sekvenci znaků. Například Compose, C, O vloží znak „©“, zatímco Compose, A, vloží znak „á“.
.caps = Klávesa Caps Lock
## Input: Keyboard: Shortcuts
@ -418,6 +418,8 @@ vpn-error = Chyba VPN
.connect = Nepodařilo se připojit k VPN
.connection-editor = Editor připojení selhal
.connection-settings = Nepodařilo se získat nastavení aktivních připojení
.openvpn-config-path = Chybně zadaná cesta souboru nastavení OpenVPN
.openvpn-config-path-desc = Vybraný soubor musí být na lokálním souborovém systému
.updating-state = Nepodařilo se aktualizovat stav správce sítě
.wireguard-config-path = Chybně zadaná cesta souboru nastavení WireGuard
.wireguard-config-path-desc = Vybraný soubor musí být na lokálním souborovém systému
@ -627,13 +629,13 @@ power-saving = Možnosti úspory energie
disable-while-typing = Zakázat při psaní
input-devices = Vstupní zařízení
modified = { $count } změněných
slow = Pomalá
fast = Rychlá
short = Krátké
long = Dlouhé
slow = Pomalu
fast = Rychle
short = Krátká
long = Dlouhá
keyboard-typing-assist = Psaní
.repeat-rate = Frekvence opakování
.repeat-delay = Zpoždění opakování
.repeat-rate = Rychlost opakování
.repeat-delay = Prodleva před opakováním
keyboard-numlock-boot = Num Lock
.boot-state = Stav klávesy při spuštění systému
.last-boot = Poslední spuštění systému
@ -893,3 +895,9 @@ xdg-entry-displays-keywords = COSMIC;Obrazovka;Obrazovky;Displeje;Monitory;Zobra
xdg-entry-workspaces-keywords = COSMIC;Pracovní;Plocha;Plochy;Orientace;Přehled;Sledování;
xdg-entry-x11-applications-keywords = COSMIC;X11;Aplikace;Hra;Hry;Kompatibilita;
no-search-results = Žádné sítě neodpovídají vašemu vyhledávání.
workspaces-navigation = Navigace
.wraparound = Přecházet mezi první a poslední pracovní plochou pomocí klávesových zkratek a gest
bluetooth-display-passkey = Párování Bluetooth
.description = Zadejte prosím následující párovací kód na zařízení { $device } a poté stiskněte Enter
bluetooth-display-pin = Párování Bluetooth
.description = Zadejte prosím následující PIN na zařízení { $device } a poté stiskněte Enter

View file

@ -60,6 +60,8 @@ vpn-error = VPN-Fehler
.connect = Verbinden mit dem VPN ist fehlgeschlagen
.connection-editor = Verbindungseditor ist fehlgeschlagen
.connection-settings = Abrufen der Einstellungen für aktive Verbindungen ist fehlgeschlagen
.openvpn-config-path = Ungültiger Dateipfad für OpenVPN-Konfiguration
.openvpn-config-path-desc = Die ausgewählte Datei muss sich im lokalen Dateisystem befinden.
.updating-state = Aktualisieren des Netzwerkmanager-Status ist fehlgeschlagen
.wireguard-config-path = Ungültiger Dateipfad für WireGuard-Konfiguration
.wireguard-config-path-desc = Die ausgewählte Datei muss sich im lokalen Dateisystem befinden.
@ -912,3 +914,9 @@ xdg-entry-desktop-keywords = COSMIC;Desktop;
xdg-entry-desktop = Desktop
xdg-entry-a11y-keywords = COSMIC;Barrierefreiheit;A11y;Bildschirm;Leser;Vergrößerungsglas;Kontrast;Farbe;
xdg-entry-a11y-comment = Bildschirmleser, Vergrößerungsglas, hoher Kontrast und Farbfilter
workspaces-navigation = Navigation
.wraparound = Mit Tastenkombinationen und Gesten zwischen der ersten und letzten Arbeitsfläche wechseln
bluetooth-display-passkey = Bluetooth-Kopplung
.description = Bitte gib den folgenden Passkey auf { $device } ein und drücke dann die Eingabetaste
bluetooth-display-pin = Bluetooth-Kopplung
.description = Bitte gib die folgende PIN auf { $device } ein und drücke dann die Eingabetaste

View file

@ -1 +1,828 @@
share = Κοινοποίηση δικτύου
connect = Σύνδεση
close = Κλείσιμο
save = Αποθήκευση
password = Κωδικός πρόσβασης
remove = Αφαίρεση
import = Εισαγωγή
username = Όνομα χρήστη
light = Ανοιχτόχρωμο
ok = OK
applets = Μικροεφαρμογές
dbus-connection-error = Αποτυχία σύνδεσης στο DBus
dark = Σκουρόχρωμο
cancel = Ακύρωση
settings = Ρυθμίσεις
replace = Αντικατάσταση
add = Προσθήκη
appearance = Εμφάνιση
panel-left = Αριστερά
panel-top = Πάνω
region = Περιοχή
forget-dialog = Διαγραφή αυτού του δικτύου Wi-Fi;
.description = Θα χρειαστεί να εισαγάγετε ξανά τον κωδικό πρόσβασης για να χρησιμοποιήσετε αυτό το δίκτυο Wi-Fi στο μέλλον.
default = Προεπιλογή
unknown = Άγνωστο
connecting = Σύνδεση…
export = Εξαγωγή
panel-right = Δεξιά
rgb = RGB
desktop = Επιφάνεια εργασίας
identity = Ταυτότητα
disconnect = Αποσύνδεση
app = Ρυθμίσεις COSMIC
debug = Εντοπισμός σφαλμάτων
number = { $number }
small = Μικρό
terminate = Τερματισμός
password-confirm = Επιβεβαίωση κωδικού πρόσβασης
applications = Εφαρμογές
connected = Συνδέθηκε
large = Μεγάλο
confirm = Επιβεβαίωση
enable = Ενεργοποίηση
never = Ποτέ
activate = Ενεργοποίηση
dialog-add = Προσθήκη
command = Εντολή
panel-bottom = Κάτω
navigate = Πλοήγηση
colors = Χρώματα
xdg-entry-touchpad = Επιφάνεια αφής
xdg-entry-bluetooth-comment = Διαχείριση συσκευών Bluetooth
xdg-entry-appearance-keywords = Έμφαση;Χρώμα;Εικονίδιο;Γραμματοσειρά;Θέμα;COSMIC;Accent;Color;Icon;Font;Theme
xdg-entry-mouse = Ποντίκι
firmware = Υλικολογισμικό
xdg-entry-a11y = Προσβασιμότητα
xdg-entry-desktop-keywords = Επιφάνεια;Εργασίας;COSMIC;Desktop;
xdg-entry-users = Χρήστες
xdg-entry-default-apps = Προεπιλεγμένες εφαρμογές
xdg-entry-applications-comment = Προεπιλεγμένες εφαρμογές, εφαρμογές εκκίνησης και ρυθμίσεις συμβατότητας εφαρμογών με το X11
xdg-entry-about-keywords = Πληροφορίες;Σχετικά;Περί;COSMIC;About
xdg-entry-notifications = Ειδοποιήσεις
xdg-entry-a11y-keywords = Προσβασιμότητα;Προσιτότητα;Οθόνη;Ανάγνωση;Αναγνώστης;Μεγεθυντικός;Φακός;Αντίθεση;Χρώμα;COSMIC;Accessibility;A11y;Screen;Reader;Magnifier;Contrast;Color;
xdg-entry-desktop = Επιφάνεια εργασίας
xdg-entry-date-time-keywords = Ζώνη;Ώρα;COSMIC;Time;Zone;
xdg-entry-wallpaper = Ταπετσαρία
xdg-entry-keyboard = Πληκτρολόγιο
xdg-entry-a11y-comment = Ανάγνωση οθόνης, μεγεθυντικός φακός, υψηλή αντίθεση και χρωματικά φίλτρα
xdg-entry-displays = Οθόνες
sound = Ήχος
xdg-entry-date-time = Ημερομηνία και ώρα
notifications = Ειδοποιήσεις
xdg-entry-bluetooth-keywords = Σύνδεση;COSMIC;Bluetooth;
about = Πληροφορίες
workspaces = Χώροι εργασίας
xdg-entry-wireless = Wi-Fi
xdg-entry-about-comment = Όνομα συσκευής, πληροφορίες υλικού, προεπιλογές λειτουργικού συστήματος
xdg-entry-appearance-comment = Χρώματα έμφασης και θέματα
xdg-entry-comment = Εφαρμογή ρυθμίσεων για το περιβάλλον επιφάνειας εργασίας COSMIC
xdg-entry-applications = Εφαρμογές
xdg-entry-default-apps-keywords = Προεπιλογή;Προεπιλεγμένη;Εφαρμογή;COSMIC;Default;Application
xdg-entry-keywords = Προτιμήσεις;Ρυθμίσεις;COSMIC;Settings;
xdg-entry-about = Πληροφορίες
xdg-entry-input = Συσκευές εισόδου
keyboard = Πληκτρολόγιο
xdg-entry-appearance = Εμφάνιση
xdg-entry-sound = Ήχος
xdg-entry-displays-keywords = Οθόνη;COSMIC;Display;
xdg-entry-workspaces = Χώροι εργασίας
xdg-entry-date-time-comment = Ζώνη ώρας, αυτόματη ρύθμιση ρολογιού και μορφοποίηση ώρας
system-shortcut = Σύστημα
.app-library = Άνοιγμα βιβλιοθήκης εφαρμογών
.brightness-down = Μείωση φωτεινότητας οθόνης
.brightness-up = Αύξηση φωτεινότητας οθόνης
.display-toggle = Εναλλαγή εσωτερικής οθόνης
.home-folder = Άνοιγμα προσωπικού φακέλου
.keyboard-brightness-down = Μείωση φωτεινότητας πληκτρολογίου
.keyboard-brightness-up = Αύξηση φωτεινότητας πληκτρολογίου
.launcher = Άνοιγμα εκκινητή
.log-out = Αποσύνδεση
.lock-screen = Κλείδωμα οθόνης
.mute = Σίγαση εξόδου ήχου
.mute-mic = Σίγαση εισόδου μικροφώνου
.play-pause = Αναπαραγωγή/παύση
.play-next = Επόμενο κομμάτι
.play-prev = Προηγούμενο κομμάτι
.poweroff = Τερματισμός λειτουργίας
.screenshot = Λήψη στιγμιότυπου οθόνης
.suspend = Αναστολή λειτουργίας
.terminal = Άνοιγμα τερματικού
.touchpad-toggle = Εναλλαγή λειτουργίας επιφάνειας αφής
.volume-lower = Μείωση έντασης εξόδου ήχου
.volume-raise = Αύξηση έντασης εξόδου ήχου
.web-browser = Άνοιγμα προγράμματος περιήγησης ιστού
.window-switcher = Εναλλαγή μεταξύ των ανοικτών παραθύρων
.window-switcher-previous = Εναλλαγή μεταξύ των ανοικτών παραθύρων (αντίστροφα)
.workspace-overview = Άνοιγμα επισκόπησης χώρων εργασίας
super-key = Ενέργεια πλήκτρου Super
.launcher = Άνοιγμα μενού εκκίνησης
.workspaces = Άνοιγμα χώρων εργασίας
.applications = Άνοιγμα εφαρμογών
.disable = Απενεργοποίηση
x-minutes =
{ $number } { $number ->
[one] λεπτό
*[other] λεπτά
}
vrr = Μεταβλητός ρυθμός ανανέωσης
.enabled = Ενεργός
.force = Πάντα
.auto = Αυτόματα
.disabled = Ανενεργός
window-hint-accent = Χρώμα ένδειξης ενεργού παραθύρου
fit-to-screen = Προσαρμογή στην οθόνη
control-tint = Απόχρωση στοιχείων ελέγχου
.desc = Χρησιμοποιείται για το φόντο των τυπικών κουμπιών, εισόδων αναζήτησης, εισόδων κειμένου και παρόμοιων στοιχείων
vpn-error = Σφάλμα VPN
.config = Αποτυχία προσθήκης διαμόρφωσης VPN
.connect = Αποτυχία σύνδεσης στο VPN
.connection-editor = Αποτυχία επεξεργαστή συνδέσεων
.connection-settings = Αποτυχία λήψης ρυθμίσεων για ενεργές συνδέσεις
.openvpn-config-path = Μη έγκυρη διαδρομή αρχείου για τη διαμόρφωση του OpenVPN
.openvpn-config-path-desc = Το επιλεγμένο αρχείο πρέπει να βρίσκεται σε τοπικό σύστημα αρχείων.
.updating-state = Αποτυχία ενημέρωσης κατάστασης διαχειριστή δικτύου
.wireguard-config-path = Μη έγκυρη διαδρομή αρχείων για τη διαμόρφωση του WireGuard
.wireguard-config-path-desc = Το επιλεγμένο αρχείο πρέπει να βρίσκεται στο τοπικό σύστημα αρχείων.
.wireguard-device = Αποτυχία δημιουργίας συσκευής WireGuard
.with-password =
Αποτυχία ορισμού { $field ->
*[username] ονόματος χρήστη
[password] κωδικού πρόσβασης
[password-flags] σημαιών κωδικού πρόσβασης
} VPN με το nmcli
toggle-stacking = Εναλλαγή στοίβαξης παραθύρων
no-applets-found = Δεν βρέθηκαν μικροεφαρμογές...
zoom-in = Μεγέθυνση
place-here = Τοποθετήστε τις μικροεφαρμογές εδώ
-requires-restart = Απαιτεί επανεκκίνηση
workspaces-orientation = Προσανατολισμός χώρων εργασίας
.vertical = Κάθετος
.horizontal = Οριζόντιος
acceleration-desc = Προσαρμόζει αυτόματα την ευαισθησία ιχνηλάτησης με βάση την ταχύτητα
all-displays = Όλες οι οθόνες
scheduling = Προγραμματισμός
.manual = Χειροκίνητο πρόγραμμα
frosted = Εφέ ημιδιαφανούς γυαλιού στο περιβάλλον συστήματος
.desc = Εφαρμόζει θόλωση του φόντου της γραμμής συστήματος, της μπάρας εφαρμογών, του μενού εκκίνησης και της βιβλιοθήκης εφαρμογών
about-hardware = Υλικό
.model = Μοντέλο υλικού
.memory = Μνήμη
.processor = Επεξεργαστής
.graphics = Γραφικά
.disk-capacity = Χωρητικότητα δίσκου
short = Σύντομη
vpn = VPN
.connections = Συνδέσεις VPN
.error = Αποτυχία προσθήκης διαμόρφωσης VPN
.remove = Αφαίρεση προφίλ σύνδεσης
.select-file = Επιλογή αρχείου διαμόρφωσης VPN
power-mode = Λειτουργία ενέργειας
.battery = Εκτεταμένη διάρκεια μπαταρίας
.battery-desc = Μειωμένη χρήση ενέργειας και χαμηλές επιδόσεις
.balanced = Ισορροπημένη
.balanced-desc = Μέτριες επιδόσεις και ήπια χρήση ενέργειας
.performance = Υψηλές επιδόσεις
.performance-desc = Κορυφαίες επιδόσεις και υψηλή χρήση ενέργειας
.no-backend = Δεν βρέθηκε υπηρεσία παρασκηνίου. Εγκαταστήστε το system76-power ή το power-profiles-daemon.
wired = Ενσύρματη δικτύωση
.adapter = Προσαρμογέας ενσύρματου δικτύου { $id }
.connections = Ενσύρματες συνδέσεις
.devices = Ενσύρματες συσκευές
.remove = Αφαίρεση προφίλ σύνδεσης
bluetooth-available = Κοντινές συσκευές
cable-unplugged = Αποσυνδεδεμένο καλώδιο
color-picker = Εργαλείο επιλογής χρωμάτων
interface-font = Γραμματοσειρά συστήματος
zoom-out = Σμίκρυνση
hot-corner = Γωνία συντόμευσης
.top-left-corner = Ενεργοποίηση της πάνω αριστερής γωνίας συντόμευσης για τους χώρους εργασίας
keyboard-typing-assist = Πληκτρολόγηση
.repeat-rate = Ρυθμός επανάληψης
.repeat-delay = Καθυστέρηση επανάληψης
edge-gravity = Έλξη αιωρούμενων παραθύρων στις κοντινές άκρες
amplification = Ενίσχυση ήχου
.desc = Επιτρέπει την αύξηση της έντασης στο 150%
recent-colors = Πρόσφατα χρώματα
about-os = Λειτουργικό σύστημα
.os = Λειτουργικό σύστημα
.os-architecture = Αρχιτεκτονική λειτουργικού συστήματος
.kernel = Έκδοση πυρήνα
.desktop-environment = Περιβάλλον επιφάνειας εργασίας
.windowing-system = Σύστημα διαχείρισης παραθύρων
panel-behavior-and-position = Συμπεριφορά και θέσεις
.autohide = Αυτόματη απόκρυψη γραμμής συστήματος
.dock-autohide = Αυτόματη απόκρυψη μπάρας εφαρμογών
.position = Θέση στην οθόνη
.display = Εμφάνιση στην οθόνη
add-language = Προσθήκη γλώσσας
.context = Προσθήκη γλώσσας
password-mismatch = Ο κωδικός πρόσβασης και το πεδίο επιβεβαίωσης θα πρέπει να είναι ίδια
accessibility = Προσβασιμότητα
.vision = Όραση
.on = Ενεργό
.off = Ανενεργό
.unavailable = Μη διαθέσιμο
.screen-reader = Ανάγνωση οθόνης
.high-contrast = Λειτουργία υψηλής αντίθεσης
.invert-colors = Αναστροφή χρωμάτων
.color-filters = Φίλτρα χρωμάτων
container-background = Φόντο πλαισίων
.desc-detail = Το χρώμα φόντου πλαισίων χρησιμοποιείται για την πλαϊνή στήλη πλοήγησης, το πλευρικό συρτάρι, τα παράθυρα διαλόγων και παρόμοια γραφικά στοιχεία. Από προεπιλογή, το χρώμα φόντου πλαισίων προκύπτει αυτόματα από το φόντο των παραθύρων.
.reset = Επαναφορά στο αυτόματο
.desc = Χρησιμοποιείται για την πλαϊνή στήλη πλοήγησης, το πλευρικό συρτάρι, τα παράθυρα διαλόγων και παρόμοια γραφικά στοιχεία
primary-button = Κύριο κουμπί
.desc = Ορίζει τη σειρά των φυσικών κουμπιών
.left = Αριστερό
.right = Δεξί
sound-alerts = Ειδοποιήσεις
.volume = Ένταση ήχου ειδοποιήσεων
.sound = Ήχος ειδοποιήσεων
icon-theme = Θέμα εικονιδίων
.desc = Εφαρμόζει ένα διαφορετικό σύνολο εικονιδίων για εφαρμογές
keyboard-shortcuts = Συντομεύσεις πληκτρολογίου
.desc = Προβολή και προσαρμογή συντομεύσεων
switch-between-windows = Εναλλαγή μεταξύ των παραθύρων
text-tint = Απόχρωση κειμένου περιβάλλοντος χρήστη
.desc = Χρησιμοποιείται για τον προσδιορισμό των χρωμάτων του κειμένου του περιβάλλοντος χρήστη ώστε να έχουν επαρκή αντίθεση σε διάφορες επιφάνειες
online-accounts = Διαδικτυακοί λογαριασμοί
.desc = Προσθήκη λογαριασμών, IMAP και SMTP, εταιρικών συνδέσεων
open-workspaces-view = Άνοιγμα επισκόπησης χώρων εργασίας
all = Όλα
remove-user = Αφαίρεση χρήστη
sound-input = Είσοδος ήχου
.volume = Ένταση εισόδου
.device = Συσκευή εισόδου
.level = Επίπεδο εισόδου
add-another-keybinding = Προσθήκη άλλου συνδυασμού πλήκτρων
copy-to-clipboard = Αντιγραφή στο πρόχειρο
monospace-font = Γραμματοσειρά σταθερού πλάτους
experimental-settings = Πειραματικές ρυθμίσεις
recent-folders = Πρόσφατοι φάκελοι
color-filter = Τύπος φίλτρου χρωμάτων
.unknown = Άγνωστο φίλτρο ενεργό
.greyscale = Κλίμακα του γκρι
.deuteranopia = Πράσινο/κόκκινο (αδυναμία στο πράσινο, Δευτερανοπία)
.protanopia = Κόκκινο/πράσινο (αδυναμία στο κόκκινο, Πρωτανοπία)
.tritanopia = Μπλε/κίτρινο (αδυναμία στο μπλε, Τριτανοπία)
migrate-workspace =
Μεταφορά χώρου εργασίας { $direction ->
*[down] στην κάτω έξοδο
[left] στην αριστερή έξοδο
[right] στη δεξιά έξοδο
[up] στην πάνω έξοδο
}
battery = Μπαταρία
.minute =
{ $value } { $value ->
[one] λεπτό
*[other] λεπτά
}
.hour =
{ $value } { $value ->
[one] ώρα
*[other] ώρες
}
.day =
{ $value } { $value ->
[one] ημέρα
*[other] ημέρες
}
.less-than-minute = Λιγότερο από ένα λεπτό
.and = και
.remaining-time =
{ $time } μέχρι την { $action ->
[full] πλήρη φόρτιση
*[other] αποφόρτιση
}
search-applets = Αναζήτηση μικροεφαρμογών...
pinch-to-zoom = Τσίμπημα για ζουμ
.desc = Χρησιμοποιήστε δύο δάχτυλα για να κάνετε ζουμ στο περιεχόμενο, σε εφαρμογές που το υποστηρίζουν
wifi = Wi-Fi
.adapter = Προσαρμογέας Wi-Fi { $id }
.forget = Διαγραφή αυτού του δικτύου
x-hours =
{ $number } { $number ->
[one] ώρα
*[other] ώρες
}
added = Προστέθηκε
input-source-switch = Εναλλαγή πηγών εισόδου γλώσσας πληκτρολογίου
focus-navigation = Πλοήγηση εστίασης
.focus-follows-cursor = Η εστίαση ακολουθεί τον κέρσορα
.focus-follows-cursor-delay = Η εστίαση ακολουθεί την καθυστέρηση του κέρσορα σε ms
.cursor-follows-focus = Ο κέρσορας ακολουθεί την εστίαση
full-name = Ονοματεπώνυμο
app-background = Φόντο παραθύρων
keyboard-sources = Πηγές εισόδου
.desc = Μπορείτε να αλλάξετε τις πηγές εισόδου με τον συνδυασμό πλήκτρων Super+Space, τον οποίο μπορείτε να προσαρμόσετε στις ρυθμίσεις συντομεύσεων πληκτρολογίου.
.move-up = Μετακίνηση πάνω
.move-down = Μετακίνηση κάτω
.settings = Ρυθμίσεις
.view-layout = Προβολή διάταξης πληκτρολογίου
.remove = Αφαίρεση
.add = Προσθήκη πηγής εισόδου
nav-shortcuts = Πλοήγηση
.prev-output = Εστίαση στην προηγούμενη έξοδο
.next-output = Εστίαση στην επόμενη έξοδο
.last-workspace = Εστίαση στον τελευταίο χώρο εργασίας
.prev-workspace = Εστίαση στον προηγούμενο χώρο εργασίας
.next-workspace = Εστίαση στον επόμενο χώρο εργασίας
.focus =
Εστίαση κατά ένα παράθυρο { $direction ->
*[down] προς τα κάτω
[in] προς τα μέσα
[left] προς τα αριστερά
[out] προς τα έξω
[right] προς τα δεξιά
[up] προς τα πάνω
}
.output =
Εναλλαγή { $direction ->
*[down] στην κάτω έξοδο
[left] στην αριστερή έξοδο
[right] στη δεξιά έξοδο
[up] στην πάνω έξοδο
}
.workspace = Μετάβαση στον χώρο εργασίας { $num }
hearing = Ακοή
.mono = Αναπαραγωγή στερεοφωνικού ήχου ως μονοφωνικού
preferred-languages = Προτιμώμενες γλώσσες
.desc = Η σειρά των γλωσσών καθορίζει ποια γλώσσα θα χρησιμοποιείται για το περιβάλλον χρήστη. Οι αλλαγές θα εφαρμοστούν στην επόμενη σύνδεση.
panel-style = Στυλ
.anchor-gap = Κενό ανάμεσα στη γραμμή συστήματος και στις άκρες της οθόνης
.dock-anchor-gap = Κενό ανάμεσα στην μπάρα εφαρμογών και στις άκρες της οθόνης
.extend = Επέκταση της γραμμής συστήματος στις άκρες της οθόνης
.dock-extend = Επέκταση της μπάρας εφαρμογών στις άκρες της οθόνης
.appearance = Εμφάνιση
.size = Μέγεθος
.background-opacity = Αδιαφάνεια φόντου
panel-appearance = Εμφάνιση
.match = Συμφωνία με την επιφάνεια εργασίας
.light = Ανοιχτόχρωμη
.dark = Σκουρόχρωμη
workspaces-multi-behavior = Συμπεριφορά πολλαπλών οθονών
.span = Κοινοί χώροι εργασίας σε όλες τις οθόνες
.separate = Ξεχωριστοί χώροι εργασίας ανά οθόνη
replace-shortcut-dialog = Αντικατάσταση συντόμευσης;
.desc = Η συντόμευση { $shortcut } χρησιμοποιείται από το { $name }. Εάν την αντικαταστήσετε, το { $name } θα απενεργοποιηθεί.
about-device = Όνομα συσκευής
.desc = Αυτό το όνομα εμφανίζεται σε άλλα δίκτυα ή συσκευές Bluetooth
no-vpn = Δεν διατίθενται συνδέσεις VPN.
start-segment = Αρχικό τμήμα
reset-to-default = Επαναφορά προεπιλογών
enable-export = Εφαρμογή τρέχοντος θέματος σε εφαρμογές GNOME
.desc = Δεν υποστηρίζουν όλα τα σύνολα εργαλείων την αυτόματη εναλλαγή. Ενδέχεται να χρειαστεί επανεκκίνηση των μη-COSMIC εφαρμογών μετά από αλλαγή του θέματος.
shortcut-name = Όνομα συντόμευσης
style = Στυλ
.round = Στρογγυλό
.slightly-round = Ελαφρώς στρογγυλό
.square = Τετράγωνο
panel-applets = Διαμόρφωση
.dock-desc = Διαμόρφωση των μικροεφαρμογών της μπάρας εφαρμογών
.desc = Διαμόρφωση των μικροεφαρμογών της γραμμής συστήματος
qr-code-unavailable = Δεν διατίθεται κωδικός QR
window-tiling = Παράθεση παραθύρων
.horizontal = Ορισμός οριζόντιου προσανατολισμού
.vertical = Ορισμός κάθετου προσανατολισμού
.swap-window = Εναλλαγή παραθύρου
.toggle-tiling = Εναλλαγή παράθεσης παραθύρων
.toggle-stacking = Εναλλαγή στοίβαξης παραθύρων
.toggle-floating = Εναλλαγή αιώρησης παραθύρων
.toggle-orientation = Εναλλαγή προσανατολισμού
change-password = Αλλαγή κωδικού πρόσβασης
disable-while-typing = Απενεργοποίηση κατά την πληκτρολόγηση
long = Μεγάλη
copied-to-clipboard = Αντιγράφηκε στο πρόχειρο
bluetooth-confirm-pin = Επιβεβαίωση PIN για σύνδεση Bluetooth
.description = Επιβεβαιώστε ότι το ακόλουθο PIN είναι ίδιο με αυτό που εμφανίζεται στο { $device }
legacy-app-scaling = Κλιμάκωση εφαρμογών στο σύστημα παραθύρων X11
.scaled-gaming = Βελτιστοποίηση για παιχνίδια και εφαρμογές πλήρους οθόνης
.gaming-description = Οι εφαρμογές σε X11 ενδέχεται να εμφανίζονται ελαφρώς μεγαλύτερες/μικρότερες σε σύγκριση με τις εφαρμογές σε Wayland
.scaled-applications = Βελτιστοποίηση για εφαρμογές
.applications-description = Τα παιχνίδια και οι εφαρμογές πλήρους οθόνης σε X11 ενδέχεται να μην αντιστοιχούν στην ανάλυση της οθόνης σας
.scaled-compatibility = Λειτουργία μέγιστης συμβατότητας
.compatibility-description = Οι εφαρμογές σε X11 ενδέχεται να εμφανίζονται θολές σε οθόνες HiDPI
.preferred-display = Προτιμώμενη οθόνη για παιχνίδια και εφαρμογές πλήρους οθόνης σε X11
.no-display = Καμία
night-light = Νυχτερινός φωτισμός
.auto = Αυτόματα (από τη δύση έως την ανατολή)
.desc = Μείωση του μπλε φωτός με θερμότερα χρώματα
custom-shortcuts = Προσαρμοσμένες συντομεύσεις
.add = Προσθήκη συντόμευσης
.context = Προσθήκη προσαρμοσμένης συντόμευσης
.none = Δεν υπάρχουν προσαρμοσμένες συντομεύσεις
visible-networks = Ορατά δίκτυα
window-hint-accent-toggle = Χρήση του χρώματος έμφασης για την ένδειξη ενεργού παραθύρου
type-to-search = Πληκτρολογήστε για αναζήτηση...
workspaces-behavior = Συμπεριφορά χώρων εργασίας
.dynamic = Δυναμικοί χώροι εργασίας
.dynamic-desc = Αυτόματη κατάργηση των κενών χώρων εργασίας.
.fixed = Σταθερός αριθμός χώρων εργασίας
.fixed-desc = Προσθήκη ή κατάργηση χώρων εργασίας στην επισκόπηση.
wireguard-dialog = Προσθήκη συσκευής WireGuard
.description = Επιλέξτε ένα όνομα συσκευής για τη διαμόρφωση του WireGuard.
remove-connection-dialog = Αφαίρεση προφίλ σύνδεσης;
.vpn-description = Θα χρειαστεί να εισαγάγετε ξανά τον κωδικό πρόσβασης για να χρησιμοποιήσετε αυτό το δίκτυο στο μέλλον.
.wired-description = Θα χρειαστεί να δημιουργήσετε εκ νέου αυτό το προφίλ για να το χρησιμοποιήσετε στο μέλλον.
click-behavior = Συμπεριφορά κλικ
.click-finger = Δευτερεύον κλικ με δύο δάχτυλα και μεσαίο κλικ με τρία δάχτυλα
.button-areas = Δευτερεύον κλικ στην κάτω δεξιά γωνία και μεσαίο κλικ στο κάτω κεντρικό μέρος
move-windows = Μετακίνηση παραθύρων
.direction =
Μετακίνηση παραθύρου { $direction ->
*[down] προς τα κάτω
[left] προς τα αριστερά
[right] προς τα δεξιά
[up] προς τα πάνω
}
.display =
Μετακίνηση παραθύρου κατά μία οθόνη { $direction ->
*[down] προς τα κάτω
[left] προς τα αριστερά
[right] προς τα δεξιά
[up] προς τα πάνω
}
.workspace =
Μετακίνηση παραθύρου κατά έναν χώρο εργασίας { $direction ->
*[below] προς τα κάτω
[left] προς τα αριστερά
[right] προς τα δεξιά
[above] προς τα πάνω
}
.workspace-num = Μετακίνηση παραθύρου στον χώρο εργασίας { $num }
.prev-workspace = Μετακίνηση παραθύρου στον προηγούμενο χώρο εργασίας
.next-workspace = Μετακίνηση παραθύρου στον επόμενο χώρο εργασίας
.last-workspace = Μετακίνηση παραθύρου στον τελευταίο χώρο εργασίας
.next-display = Μετακίνηση παραθύρου στην επόμενη οθόνη
.prev-display = Μετακίνηση παραθύρου στην προηγούμενη οθόνη
.send-to-prev-workspace = Μετακίνηση παραθύρου στον προηγούμενο χώρο εργασίας
.send-to-next-workspace = Μετακίνηση παραθύρου στον επόμενο χώρο εργασίας
switch-workspaces = Εναλλαγή χώρων εργασίας
.horizontal = Ολίσθηση με τέσσερα δάχτυλα προς τα αριστερά/δεξιά
.vertical = Ολίσθηση με τέσσερα δάχτυλα προς τα πάνω/κάτω
system-controls = Έλεγχος συστήματος
auto-switch = Αυτόματη εναλλαγή μεταξύ ανοιχτόχρωμης και σκουρόχρωμης λειτουργίας
.sunrise = Εναλλαγή στην ανοιχτόχρωμη λειτουργία την ανατολή
.sunset = Εναλλαγή στη σκουρόχρωμη λειτουργία τη δύση
.next-sunrise = Εναλλαγή στην ανοιχτόχρωμη λειτουργία στην επόμενη ανατολή
.next-sunset = Εναλλαγή στην ανοιχτόχρωμη λειτουργία στην επόμενη δύση
add-vpn = Προσθήκη VPN
keyboard-numlock-boot = Πλήκτρο Numlock
.boot-state = Κατάσταση κατά την εκκίνηση
.last-boot = Τελευταία εκκίνηση
.on = Ενεργό
.off = Ανενεργό
.set = Ορισμός κατάστασης Numlock κατά την εκκίνηση
about-related = Σχετικές ρυθμίσεις
.support = Λήψη υποστήριξης
tap-to-click = Πάτημα για κλικ
.desc = Ενεργοποιεί το πάτημα με ένα δάχτυλο για κύριο κλικ, με δύο δάχτυλα για δευτερεύον κλικ και με τρία δάχτυλα για μεσαίο κλικ
interface-density = Πυκνότητα περιβάλλοντος χρήστη
.comfortable = Άνετο
.compact = Πυκνό
.spacious = Ευρύχωρο
open-application-library = Άνοιγμα βιβλιοθήκης εφαρμογών
slow = Αργός
network-name = Όνομα δικτύου
scrolling = Κύλιση
.two-finger = Κύλιση με δύο δάχτυλα
.edge = Κύλιση κατά μήκος της άκρης με ένα δάχτυλο
.speed = Ταχύτητα κύλισης
.natural = Φυσική κατεύθυνση κύλισης
.natural-desc = Κύλιση του περιεχομένου, αντί της προβολής
auth-dialog = Απαιτείται πιστοποίηση
.vpn-description = Εισαγάγετε το όνομα χρήστη και τον κωδικό πρόσβασης που απαιτούνται από την υπηρεσία VPN.
.wifi-description = Εισαγάγετε τον κωδικό πρόσβασης ή το κλειδί κρυπτογράφησης. Μπορείτε επίσης να συνδεθείτε πατώντας το κουμπί «WPS» του δρομολογητή.
add-user = Προσθήκη χρήστη
add-image = Προσθήκη εικόνας
migrate-workspace-next = Μεταφορά χώρου εργασίας στην επόμενη έξοδο
airplane-on = Η λειτουργία πτήσης είναι ενεργή.
formatting = Μορφοποίηση
.dates = Ημερομηνίες
.time = Ώρα
.date-and-time = Ημερομηνία και ώρα
.numbers = Αριθμοί
.measurement = Μονάδα μέτρησης
.paper = Χαρτί
disabled = Ανενεργό
add-applet = Προσθήκη μικροεφαρμογής
show-extended-input-sources = Εμφάνιση εκτεταμένων πηγών εισόδου
mode-and-colors = Λειτουργία και χρώματα
forget = Διαγραφή
center-segment = Κεντρικό τμήμα
panel-missing = Απουσία διαμόρφωσης γραμμής συστήματος
.desc = Το αρχείο διαμόρφωσης της γραμμής συστήματος απουσιάζει λόγω της χρήσης μιας προσαρμοσμένης διαμόρφωσης ή είναι κατεστραμμένο.
.fix = Επαναφορά προεπιλογής
invalid-username = Μη έγκυρο όνομα χρήστη
mirroring = Κατοπτρισμός
.id = Κατοπτρισμός { $id }
.dont = Να μην γίνει κατοπτρισμός
.mirror = Κατοπτρισμός { $display }
.project =
Προβολή σε { $display ->
[all] όλες τις οθόνες
*[other] { $display }
}
.project-count =
Προβολή σε { $count } { $count ->
[1] άλλη οθόνη
*[other] άλλες οθόνες
}
orientation = Προσανατολισμός
.standard = Τυπικός
.rotate-90 = Περιστροφή 90°
.rotate-180 = Περιστροφή 180°
.rotate-270 = Περιστροφή 270°
auto = Αυτόματο
end-segment = Τελικό τμήμα
bluetooth-adapters = Προσαρμογείς Bluetooth
time-format = Μορφή ημερομηνίας και ώρας
.twenty-four = 24ωρη μορφή
.show-seconds = Εμφάνιση δευτερολέπτων
.first = Πρώτη ημέρα εβδομάδας
.show-date = Εμφάνιση ημερομηνίας στη μικροεφαρμογή ώρας
.friday = Παρασκευή
.saturday = Σάββατο
.sunday = Κυριακή
.monday = Δευτέρα
accent-color = Χρώμα έμφασης
type-key-combination = Εισαγάγετε τον συνδυασμό πλήκτρων
manage-windows = Διαχείριση παραθύρων
.close = Κλείσιμο παραθύρου
.maximize = Μεγιστοποίηση παραθύρου
.fullscreen = Παράθυρο σε πλήρη οθόνη
.minimize = Ελαχιστοποίηση παραθύρου
.resize-inwards = Αλλαγή μεγέθους παραθύρου προς τα μέσα
.resize-outwards = Αλλαγή μεγέθους παραθύρου προς τα έξω
.toggle-sticky = Εναλλαγή καρφιτσωμένου παραθύρου
install-additional-languages = Εγκατάσταση πρόσθετων γλωσσών
sound-output = Έξοδος ήχου
.volume = Ένταση εξόδου
.device = Συσκευή εξόδου
.level = Επίπεδο εξόδου
.config = Διαμόρφωση
.balance = Ισορροπία
.left = Αριστερό
.right = Δεξί
window-controls = Στοιχεία ελέγχου παραθύρων
.maximize = Εμφάνιση κουμπιού μεγιστοποίησης
.minimize = Εμφάνιση κουμπιού ελαχιστοποίησης
.active-window-hint = Εμφάνιση ένδειξης ενεργού παραθύρου
window-management-appearance = Διαχείριση παραθύρων
.active-hint = Μέγεθος ένδειξης ενεργού παραθύρου
.gaps = Κενά γύρω από τα παράθυρα σε παράθεση
migrate-workspace-prev = Μεταφορά χώρου εργασίας στην προηγούμενη έξοδο
legacy-app-global-shortcuts = Καθολικές συντομεύσεις σε εφαρμογές X11
.desc = Οι καθολικές συντομεύσεις επιτρέπουν σε πατήματα πλήκτρων και συμβάντα κουμπιών ποντικιού που εκτελούνται στις εφαρμογές να αναγνωρίζονται από άλλες εφαρμογές για λειτουργίες, όπως πάτημα για ομιλία ή πάτημα για σίγαση. Από προεπιλογή, οι καθολικές συντομεύσεις είναι απενεργοποιημένες στις εφαρμογές X11 για να διασφαλιστεί ότι οι άλλες εφαρμογές δεν μπορούν να παρακολουθούν τα συμβάντα πληκτρολογίου και ποντικιού που περιέχουν ευαίσθητες πληροφορίες.
.none = Κανένα πλήκτρο
.modifiers = Πλήκτρα τροποποίησης (Super, Shift, Control, Alt)
.combination = Όλα τα πλήκτρα, ενώ είναι πατημένα τα Super, Control ή Alt
.all = Όλα τα πλήκτρα
.mouse = Συμβάντα κουμπιών ποντικιού σε εφαρμογές X11
power-saving = Επιλογές εξοικονόμησης ενέργειας
.turn-off-screen-after = Απενεργοποίηση οθόνης μετά από
.auto-suspend = Αυτόματη αναστολή
.auto-suspend-ac = Αυτόματη αναστολή με τροφοδοσία ρεύματος
.auto-suspend-battery = Αυτόματη αναστολή με τροφοδοσία μπαταρίας
connected-devices = Συνδεδεμένες συσκευές
.unknown = Άγνωστη συσκευή
icons-and-toolkit = Εικονίδια και θέματα συνόλου εργαλείων
sound-applications = Εφαρμογές
.desc = Ένταση και ρυθμίσεις ήχου εφαρμογών
time-zone = Ζώνη ώρας
.auto = Αυτόματη ζώνη ώρας
.auto-info = Απαιτεί τη χρήση υπηρεσιών τοποθεσίας και πρόσβαση στο διαδίκτυο
fast = Γρήγορος
add-color = Προσθήκη χρώματος
add-network = Προσθήκη δικτύου
.profile = Προσθήκη προφίλ
network-device-state =
.activated = Συνδεδεμένο
.config = Σύνδεση
.deactivating = Αποσύνδεση
.disconnected = Αποσυνδεδεμένο
.failed = Αποτυχία σύνδεσης
.ip-check = Έλεγχος σύνδεσης
.ip-config = Αίτημα για διεύθυνση IP και πληροφορίες δρομολόγησης
.need-auth = Απαιτεί πιστοποίηση
.prepare = Προετοιμασία για σύνδεση
.secondaries = Αναμονή για δευτερεύουσα σύνδεση
.unavailable = Μη διαθέσιμο
.unknown = Άγνωστη κατάσταση
.unmanaged = Χωρίς διαχείριση
.unplugged = Αποσυνδεδεμένο καλώδιο
gestures = Χειρονομίες
.four-finger-down = Ολίσθηση με τέσσερα δάχτυλα προς τα κάτω
.four-finger-left = Ολίσθηση με τέσσερα δάχτυλα προς τα αριστερά
.four-finger-right = Ολίσθηση με τέσσερα δάχτυλα προς τα δεξιά
.four-finger-up = Ολίσθηση με τέσσερα δάχτυλα προς τα πάνω
.three-finger-any = Ολίσθηση με τρία δάχτυλα προς κάθε κατεύθυνση
system = Σύστημα και λογαριασμοί
no-networks = Δεν βρέθηκαν δίκτυα.
open-new-folder = Άνοιγμα νέου φακέλου
fill = Συμπλήρωση
custom = Προσαρμοσμένες
bluetooth-paired = Παλαιότερες συνδεδεμένες συσκευές
.connect = Σύνδεση
.battery = { $percentage }% μπαταρία
dialog = Παράθυρο διαλόγου
.title = Διατήρηση αυτών των ρυθμίσεων οθόνης;
.keep-changes = Διατήρηση αλλαγών
.change-prompt = Οι αλλαγές των ρυθμίσεων θα αναιρεθούν αυτόματα σε { $time } δευτερόλεπτα.
.revert-settings = Επαναφορά ρυθμίσεων
workspaces-overview-thumbnails = Μικρογραφίες επισκόπησης χώρων εργασίας
.show-number = Εμφάνιση αριθμού χώρων εργασίας
.show-name = Εμφάνιση ονόματος χώρων εργασίας
color = Χρώμα
.depth = Βάθος χρωμάτων
.profile = Προφίλ χρωμάτων
.sidebar = Προφίλ χρωμάτων
.temperature = Θερμοκρασία χρωμάτων
hex = Δεκαεξαδικό
magnifier = Μεγεθυντικός φακός
.controls =
Ή χρησιμοποιήστε αυτές τις συντομεύσεις: { $zoom_in ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_in } για μεγέθυνση,
}{ $zoom_out ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_out } για σμίκρυνση,
}
Super + κύλιση με το ποντίκι
.scroll_controls = Ενεργοποίηση ζουμ ποντικιού ή επιφάνειας αφής με Super + κύλιση
.show_overlay = Εμφάνιση της επικάλυψης μεγεθυντικού φακού
.increment = Αύξηση ζουμ
.signin = Έναρξη μεγεθυντικού φακού κατά τη σύνδεση
.applet = Εναλλαγή μεγεθυντικού φακού στη γραμμή συστήματος
.movement = Η ζουμαρισμένη προβολή μετακινείται
.continuous = Συνέχεια με τον κέρσορα
.onedge = Όταν ο κέρσορας φτάνει στην άκρη
.centered = Για να διατηρεί τον κέρσορα στο κέντρο
administrator = Διαχειριστής
.desc = Οι διαχειριστές μπορούν να αλλάζουν ρυθμίσεις για όλους τους χρήστες, να προσθέτουν και να καταργούν άλλους χρήστες
scan-to-connect-description = Σαρώστε τον κωδικό QR για να συνδεθείτε σε αυτό το δίκτυο.
modified = { $count } τροποποιήσεις
sound-usb-audio = Ήχος USB
sound-device-profiles = Προφίλ συσκευών
sound-hd-audio = Ήχος HD
shadow-and-corners = Σκίαση και γωνίες παραθύρων
shadows-tiling = Παράθεση παραθύρων
.clip = Συμφωνία με τις γωνίες συστήματος
.shadow = Εφαρμογή σκιών
shadows-floating = Αιωρούμενα παράθυρα
.clip = Συμφωνία με τις γωνίες συστήματος και εφαρμογή σκιών
sound-device-port-unplugged = Αποσυνδεδεμένο
workspaces-overview = Επισκόπηση χώρων εργασίας
.action-on-typing = Ενέργεια κατά την πληκτρολόγηση
.none = Καμία
.launcher = Άνοιγμα μενού εκκίνησης
.applications = Άνοιγμα εφαρμογών
time-region = Περιοχή και γλώσσα
power = Ενέργεια και μπαταρία
xdg-entry-wired = Ενσύρματη δικτύωση
xdg-entry-startup-apps-comment = Διαμόρφωση των εφαρμογών που εκτελούνται κατά τη σύνδεση
xdg-entry-region-language = Περιοχή και γλώσσα
time-date = Ημερομηνία και ώρα
.auto = Αυτόματη ρύθμιση
.auto-ntp = Η ημερομηνία και η ώρα θα ενημερώνονται αυτόματα όταν οριστεί ζώνη ώρας
xdg-entry-mouse-keywords = Ποντίκι;Επιτάχυνση;Κύλιση;COSMIC;Mouse;Acceleration;Scrolling;
xdg-entry-notifications-keywords = Ειδοποίηση;Κλείδωμα;COSMIC;Notification;Lock;
xdg-entry-startup-apps-keywords = Εφαρμογή;Εκκίνηση;COSMIC;Startup;Application;
xdg-entry-default-apps-comment = Προεπιλεγμένο πρόγραμμα περιήγησης ιστού, πρόγραμμα ηλεκτρονικού ταχυδρομείου, εφαρμογή περιήγησης αρχείων και άλλες εφαρμογές
xdg-entry-keyboard-comment = Πηγές εισόδου, εναλλαγή, εισαγωγή ειδικών χαρακτήρων, συντομεύσεις
xdg-entry-wired-keywords = Σύνδεση;Δίκτυο;Ενσύρματο;COSMIC;Wired;LAN;Network;Connection;
xdg-entry-power = Ενέργεια και μπαταρία
xdg-entry-dock-keywords = Μικροεφαρμογή;Μπάρα;Εφαρμογών;Γραμμή;Συστήματος;COSMIC;Dock;Panel;Applet
bluetooth = Bluetooth
.status = Αυτό το σύστημα είναι ορατό ως { $aliases } όσο είναι ανοικτές οι ρυθμίσεις του Bluetooth.
.connected = Συνδεδεμένο
.connecting = Σύνδεση
.disconnecting = Αποσύνδεση
.connect = Σύνδεση
.disconnect = Αποσύνδεση
.forget = Διαγραφή
.dbus-error = Προέκυψε σφάλμα κατά την αλληλεπίδραση με το DBus: { $why }
.disabled = Η υπηρεσία Bluetooth είναι απενεργοποιημένη
.inactive = Η υπηρεσία Bluetooth δεν είναι ενεργή
.unknown = Δεν ήταν δυνατή η ενεργοποίηση της υπηρεσίας Bluetooth. Είναι εγκατεστημένο το BlueZ;
xdg-entry-mouse-comment = Ταχύτητα ποντικιού, επιτάχυνση και φυσική κατεύθυνση κύλισης
xdg-entry-system = Σύστημα και λογαριασμοί
xdg-entry-panel-keywords = Μικροεφαρμογή;Γραμμή;Συστήματος;COSMIC;Panel;Applet
xdg-entry-window-management = Διαχείριση παραθύρων
xdg-entry-time-language-comment = Διαχείριση ημερομηνίας, ώρας, περιοχής και γλώσσας συστήματος
default-apps = Προεπιλεγμένες εφαρμογές
.web-browser = Περιήγηση ιστού
.file-manager = Διαχείριση αρχείων
.mail-client = Ηλεκτρονικό ταχυδρομείο
.music = Μουσική
.video = Βίντεο
.photos = Φωτογραφίες
.calendar = Ημερολόγιο
.terminal = Τερματικό
.other-associations = Άλλες συσχετίσεις
.text-editor = Επεξεργασία κειμένου
.not-installed = Δεν έχει εγκατασταθεί
xdg-entry-x11-applications-keywords = Εφαρμογή;Παιχνίδι;Συμβατότητα;COSMIC;X11;Application;Game;Compatibility;
xdg-entry-displays-comment = Διαχείριση ρυθμίσεων διαμόρφωσης οθονών
xdg-entry-touchpad-keywords = Επιφάνεια;Αφή;Χειρονομία;COSMIC;Touchpad;Gesture;
xdg-entry-time-language = Ώρα και γλώσσα
window-management = Διαχείριση παραθύρων
xdg-entry-system-keywords = Σύστημα;Πληροφορίες;Λογαριασμοί;Υλικολογισμικό;COSMIC;System;Info;Accounts;Firmware;
xdg-entry-wireless-keywords = Σύνδεση;Δίκτυο;COSMIC;WiFi;Wi-Fi;Network;Connection;
mouse = Ποντίκι
.speed = Ταχύτητα ποντικιού
.acceleration = Ενεργοποίηση επιτάχυνσης ποντικιού
dock = Μπάρα εφαρμογών
xdg-entry-panel = Γραμμή συστήματος
display = Οθόνες
.arrangement = Διάταξη οθονών
.arrangement-desc = Σύρετε τις οθόνες για να τις αναδιατάξετε
.enable = Ενεργοποίηση οθόνης
.external = Εξωτερική οθόνη { $size } { $output }
.laptop = Οθόνη φορητού υπολογιστή { $size }
.options = Επιλογές οθόνης
.refresh-rate = Ρυθμός ανανέωσης
.resolution = Ανάλυση
.scale = Κλίμακα
.additional-scale-options = Πρόσθετες επιλογές κλίμακας
xdg-entry-wireless-comment = Συνδέσεις Wi-Fi και προφίλ σύνδεσης
xdg-entry-desktop-comment = Ρυθμίσεις ταπετσαρίας, εμφάνισης, γραμμής συστήματος, μπάρας εφαρμογών, διαχείρισης παραθύρων και χώρων εργασίας
xdg-entry-network = Δίκτυα και ασύρματη σύνδεση
xdg-entry-users-comment = Πιστοποίηση και λογαριασμοί χρηστών
startup-apps = Εφαρμογές εκκίνησης
.add = Προσθήκη εφαρμογής
.user = Εφαρμογές που εκκινούνται όταν κάνετε σύνδεση
.none = Δεν έχουν προστεθεί εφαρμογές εκκίνησης
.remove-dialog-title = Αφαίρεση του «{ $name }»;
.remove-dialog-description = Αφαίρεση αυτής της εφαρμογής εκκίνησης;
.add-startup-app = Προσθήκη εφαρμογής εκκίνησης
xdg-entry-input-comment = Ρυθμίσεις πληκτρολογίου και ποντικιού
input-devices = Συσκευές εισόδου
xdg-entry-dock-comment = Μια προαιρετική μπάρα για εφαρμογές και μικροεφαρμογές
xdg-entry-network-keywords = Δίκτυο;Δικτύωση;Ασύρματο;COSMIC;Network;Wireless;WiFi;VPN;
xdg-entry-wallpaper-comment = Εικόνες ταπετσαρίας, χρώματα και επιλογές παρουσίασης διαφανειών
xdg-entry-dock = Μπάρα εφαρμογών
xdg-entry-startup-apps = Εφαρμογές εκκίνησης
xdg-entry-workspaces-keywords = Χώρος;Εργασίας;Προσανατολισμός;Επισκόπηση;Οθόνη;COSMIC;Workspace;Orientation;Overview;Monitor;
xdg-entry-system-comment = Πληροφορίες συστήματος, λογαριασμοί και ενημερώσεις υλικολογισμικού
xdg-entry-x11-applications-comment = Κλιμάκωση εφαρμογών συστήματος παραθύρων X11, κύρια οθόνη και καθολικές συντομεύσεις
xdg-entry-region-language-comment = Μορφοποίηση ημερομηνιών, ώρας και αριθμών με βάση την περιοχή σας
panel = Γραμμή συστήματος
xdg-entry-wallpaper-keywords = Ταπετσαρία;Φόντο;Παρουσίαση;Διαφανειών;COSMIC;Wallpaper;Background;Slideshow;
xdg-entry-users-keywords = Λογαριασμός;Χρήστης;COSMIC;User;Account;
xdg-entry-vpn-keywords = Δίκτυο;Σύνδεση;COSMIC;VPN;Network;Connection;OpenVPN;OpenConnect;
users = Χρήστες
.admin = Διαχειριστής
.standard = Τυπικός
.profile-add = Επιλογή εικόνας προφίλ
xdg-entry-time-language-keywords = Σύστημα;Ώρα;Ημερομηνία;Περιοχή;Γλώσσα;COSMIC;System;Time;Date;Region;Language;
xdg-entry-input-keywords = Εισαγωγή;Είσοδος;Πληκτρολόγιο;Ποντίκι;Ποντίκια;COSMIC;Input;Keyboard;Mouse;Mice;
xdg-entry-notifications-comment = Λειτουργία «Μην ενοχλείτε», ειδοποιήσεις οθόνης κλειδώματος και ρυθμίσεις ανά εφαρμογή
xdg-entry-sound-keywords = Ήχος;Ειδοποίηση;COSMIC;Sound;Audio;Alert;Pipewire;
xdg-entry-power-keywords = Μπαταρία;Ισχύς;Ενέργεια;COSMIC;Power;Battery
legacy-applications = Συμβατότητα εφαρμογών X11
xdg-entry-region-language-keywords = Περιοχή;Γλώσσα;Ημερομηνία;Μορφή;Ώρα;Τοπικές;Τοπικοποίηση;COSMIC;Region;Language;Date;Format;Time;Locale;Localization;
xdg-entry-applications-keywords = Προεπιλογή;Προεπιλεγμένες;Εφαρμογές;Εφαρμογή;Εκκίνηση;Συμβατότητα;COSMIC;Default;Application;Startup;X11;Compatibility
xdg-entry-sound-comment = Ρυθμίσεις ήχου για συσκευές, ειδοποιήσεις και εφαρμογές
wallpaper = Ταπετσαρία
.change = Αλλαγή εικόνας κάθε
.fit = Προσαρμογή ταπετσαρίας
.folder-dialog = Επιλογή φακέλου ταπετσαριών
.image-dialog = Επιλογή εικόνας ταπετσαρίας
.plural = Ταπετσαρίες
.same = Κοινή ταπετσαρία για όλες τις οθόνες
.slide = Παρουσίαση διαφανειών
xdg-entry-touchpad-comment = Ταχύτητα επιφάνειας αφής, επιλογές κλικ και χειρονομίες
xdg-entry-keyboard-keywords = Πληκτρολόγιο;Είσοδος;Πηγή;Συντομεύσεις;COSMIC;Keyboard;Input;Source;Shortcuts;
xdg-entry-vpn-comment = Συνδέσεις VPN και προφίλ σύνδεσης
xdg-entry-window-management-keywords = Παράθυρο;Διαχείριση;Παράθεση;Πλήκτρο;COSMIC;Window;Management;Tiling;Super;Key;
xdg-entry-workspaces-comment = Προσανατολισμός χώρων εργασίας, επισκόπηση και συμπεριφορά πολλαπλών οθονών
xdg-entry-network-comment = Διαχείριση συνδέσεων δικτύου
xdg-entry-panel-comment = Η κύρια γραμμή του συστήματος για μενού και μικροεφαρμογές
touchpad = Επιφάνεια αφής
.acceleration = Ενεργοποίηση επιτάχυνσης επιφάνειας αφής
.speed = Ταχύτητα επιφάνειας αφής
xdg-entry-power-comment = Λειτουργίες ενέργειας και επιλογές εξοικονόμησης ενέργειας
time = Ώρα και γλώσσα
xdg-entry-x11-applications = Συμβατότητα εφαρμογών X11
xdg-entry-window-management-comment = Ενέργεια πλήκτρου Super, επιλογές ελέγχου παραθύρων και πρόσθετες επιλογές παράθεσης παραθύρων
xdg-entry-wired-comment = Ενσύρματες συνδέσεις και προφίλ σύνδεσης
known-networks = Γνωστά δίκτυα
network-and-wireless = Δίκτυα και ασύρματη δικτύωση
keyboard-special-char = Εισαγωγή ειδικών χαρακτήρων
.alternate = Πλήκτρο εναλλακτικών χαρακτήρων
.compose = Πλήκτρο σύνθεσης
.compose-desc = Το πλήκτρο σύνθεσης επιτρέπει την εισαγωγή μιας ευρείας ποικιλίας χαρακτήρων. Για να το χρησιμοποιήσετε, πατήστε το πλήκτρο σύνθεσης και έπειτα, μια ακολουθία χαρακτήρων. Για παράδειγμα, μπορείτε να εισαγάγετε τον χαρακτήρα «©» πατώντας το πλήκτρο σύνθεσης, ακολουθούμενο από τα «C» και «o», ή τον χαρακτήρα «á» πατώντας το πλήκτρο σύνθεσης, ακολουθούμενο από τα «a» και «‘» .
.caps = Πλήκτρο Caps Lock
no-search-results = Δεν αντιστοιχεί κανένα δίκτυο στην αναζήτησή σας.
workspaces-navigation = Πλοήγηση
.wraparound = Μετακίνηση μεταξύ του πρώτου και του τελευταίου χώρου εργασίας με συντομεύσεις πληκτρολογίου και χειρονομίες
bluetooth-display-passkey = Σύζευξη Bluetooth
.description = Πληκτρολογήστε το ακόλουθο κλειδί πρόσβασης στο { $device } και πατήστε Enter
bluetooth-display-pin = Σύζευξη Bluetooth
.description = Πληκτρολογήστε το ακόλουθο PIN στο { $device } και πατήστε Enter

View file

@ -198,6 +198,8 @@ vpn-error = VPN Error
.connect = Failed to connect to VPN
.connection-editor = Connection editor failed
.connection-settings = Failed to get settings for active connections
.openvpn-config-path = Invalid file path for OpenVPN config
.openvpn-config-path-desc = Chosen file must be on a local file system.
.updating-state = Failed to update network manager state
.wireguard-config-path = Invalid file path for WireGuard config
.wireguard-config-path-desc = Chosen file must be on a local file system.
@ -252,6 +254,12 @@ bluetooth-paired = Previously connected devices
bluetooth-confirm-pin = Confirm Bluetooth PIN
.description = Please confirm that the following PIN matches the one displayed on { $device }
bluetooth-display-passkey = Bluetooth Pairing
.description = Please type the following passkey on { $device }, then press Enter
bluetooth-display-pin = Bluetooth Pairing
.description = Please type the following PIN on { $device }, then press Enter
bluetooth-available = Nearby devices
bluetooth-adapters = Bluetooth adapters
@ -393,7 +401,20 @@ style = Style
.round = Round
.slightly-round = Slightly round
.square = Square
.frosted-glass = Frosted glass
.frosted-system-interface = System interface
.frosted-system-interface-desc = Applies frosted glass to Launcher, application library, workspace overview, on-screen controls
.frosted-windows = Windows
.frosted-windows-desc = Applies frosted glass to windows and dialogs
.frosted-panels = Panels
.frosted-panels-desc = Applies frosted glass to panel and dock
.frosted-applets = Applets
.frosted-applets-desc = Applies frosted glass to applet menus on panel and dock
.frosted-thickness = Frost thickness
.less = less
.more = more
.glass-opacity = Glass opacity
interface-density = Interface density
.comfortable = Comfortable
.compact = Compact
@ -476,7 +497,7 @@ super-key = Super key action
.launcher = Open Launcher
.workspaces = Open Workspaces
.applications = Open Applications
.disable = Disable
.none = None
edge-gravity = Floating windows gravitate to nearby edges
@ -704,6 +725,22 @@ keyboard-special-char = Special Character Entry
.compose = Compose key
.compose-desc = The compose key allows a wide variety of characters to be entered. To use it, press compose and then a sequence of characters. For example, compose key followed by C and o will enter ©, while compose key followed by a and will enter á.
.caps = Caps Lock key
.ctrl = Control
.ctrl-right = Right Ctrl
.swap-with-ctrl = Swap with Control
.alt = Alt
.alt-left = Left Alt
.alt-right = Right Alt
.super = Super
.super-left = Left Super
.super-right = Right Super
.menu = Menu key
.backspace = Backspace
.escape = Escape
.swap-with-escape = Swap with Escape
.print-screen = Print Screen
.scroll-lock = Scroll Lock
.none = None
keyboard-typing-assist = Typing
.repeat-rate = Repeat rate

View file

@ -818,3 +818,5 @@ shadows-tiling = Asetellut ikkunat
.clip = Sovita järjestelmän kulmiin
.shadow = Käytä varjoja
no-search-results = Hakuasi vastaavia verkkoja ei löytynyt.
workspaces-navigation = Liikkuminen
.wraparound = Siirry ensimmäisen ja viimeisen työtilan välillä käyttäen pikanäppäimiä ja eleitä

View file

@ -117,6 +117,8 @@ vpn-error = Erreur VPN
.connect = Échec de la connexion au VPN
.connection-editor = Échec de l'éditeur de connexion
.connection-settings = Échec de l'obtention des paramètres des connexions actives
.openvpn-config-path = Chemin du fichier de configuration OpenVPN invalide
.openvpn-config-path-desc = Le fichier sélectionné doit se trouver sur un système de fichiers local.
.updating-state = Échec de la mise à jour de l'état du gestionnaire de réseau
.wireguard-config-path = Chemin de fichier invalide pour la configuration WireGuard
.wireguard-config-path-desc = Le fichier choisi doit se trouver sur un système de fichiers local.
@ -902,3 +904,9 @@ xdg-entry-keyboard-keywords = COSMIC;Clavier;Saisie;Source;Raccourcis;
xdg-entry-window-management-keywords = COSMIC;Fenêtre;Gestion;Agencement;Super;Touche;
xdg-entry-displays-keywords = COSMIC;Écran;
no-search-results = Aucun réseau ne correspond à votre recherche.
workspaces-navigation = Navigation
.wraparound = Passez du premier au dernier espace de travail à l'aide des raccourcis clavier et des gestes
bluetooth-display-passkey = Appairage Bluetooth
.description = Veuillez saisir le code dassociation sur { $device }, puis appuyer sur Entrée
bluetooth-display-pin = Appairage Bluetooth
.description = Veuillez saisir le code PIN suivant sur { $device }, puis appuyer sur Entrée

View file

@ -911,3 +911,5 @@ xdg-entry-x11-applications = Comhoiriúnacht Feidhmchláir X11
xdg-entry-window-management-comment = Gníomh na sár-eochrach, roghanna rialaithe fuinneoige, agus roghanna breise tíleála fuinneoige
xdg-entry-wired-comment = Naisc sreangaithe agus próifílí nasc
no-search-results = Níl aon líonra ag teacht leis an gcuardach seo.
workspaces-navigation = Nascleanúint
.wraparound = Bog idir an chéad spás oibre agus an spás oibre deireanach ag baint úsáide as aicearraí méarchláir agus gothaí

View file

@ -1,5 +1,5 @@
app = COSMIC Beállítások
dbus-connection-error = Nem sikerült csatlakozni a DBus-hoz
dbus-connection-error = Nem sikerült csatlakozni a D-Bushoz
ok = OK
unknown = Ismeretlen
number = { $number }
@ -23,7 +23,7 @@ xdg-entry-desktop = Asztal
xdg-entry-displays = Kijelzők
xdg-entry-displays-comment = Kijelzőbeállítások kezelése
xdg-entry-dock = Dokk
xdg-entry-dock-comment = Egy opcionális sáv alkalmazásokhoz és kisalkalmazásokhoz
xdg-entry-dock-comment = Választható sáv alkalmazásokhoz és kisalkalmazásokhoz
xdg-entry-input = Beviteli eszközök
xdg-entry-input-comment = Billentyűzet- és egérbeállítások
xdg-entry-keyboard = Billentyűzet
@ -33,7 +33,7 @@ xdg-entry-mouse-comment = Egérsebesség, gyorsítás és természetes görgeté
xdg-entry-network = Hálózat és Wi-Fi
xdg-entry-network-comment = Hálózati kapcsolatok kezelése
xdg-entry-notifications = Értesítések
xdg-entry-notifications-comment = Ne zavarjanak, zárolási képernyő értesítések és alkalmazásonkénti beállítások
xdg-entry-notifications-comment = Ne zavarjanak mód, a zárolási képernyő értesítései és alkalmazásonkénti beállítások
xdg-entry-panel-comment = Fő rendszersáv menükhöz és kisalkalmazásokhoz
xdg-entry-power = Energia és akkumulátor
xdg-entry-power-comment = Energiagazdálkodási módok és energiagazdálkodási beállítások
@ -56,10 +56,10 @@ xdg-entry-window-management = Ablakkezelés
xdg-entry-window-management-comment = Super billentyű funkció, ablakkezelési és csempézési beállítások
xdg-entry-wired = Vezetékes kapcsolatok
xdg-entry-wired-comment = Vezetékes kapcsolatok és kapcsolódási profilok
xdg-entry-wireless-comment = Wi-Fi kapcsolatok és kapcsolódási profilok
xdg-entry-wireless-comment = Wi-Fi-kapcsolatok és kapcsolódási profilok
xdg-entry-workspaces = Munkaterületek
xdg-entry-workspaces-comment = Munkaterület-tájolás, áttekintés és többképernyős viselkedés
xdg-entry-x11-applications = X11 alkalmazások támogatása
xdg-entry-workspaces-comment = Munkaterület-tájolás, áttekintés és többmonitoros viselkedés
xdg-entry-x11-applications = X11-alkalmazások támogatása
xdg-entry-x11-applications-comment = X11 ablakrendszer alkalmazásméretezés, elsődleges kijelző és globális gyorsbillentyűk
## Network & Wireless
@ -67,17 +67,17 @@ xdg-entry-x11-applications-comment = X11 ablakrendszer alkalmazásméretezés, e
add-network = Hálózat hozzáadása
.profile = Profil hozzáadása
add-vpn = VPN hozzáadása
airplane-on = A repülőgép üzemmód be van kapcsolva
airplane-on = A repülőgép üzemmód be van kapcsolva.
cable-unplugged = Kábel kihúzva
connect = Csatlakozás
connected = Csatlakoztatva
connecting = Csatlakozás…
connect = Kapcsolódás
connected = Kapcsolódva
connecting = Kapcsolódás…
disconnect = Bontás
forget = Elfelejtés
known-networks = Ismert hálózatok
network-and-wireless = Hálózat és Wi-Fi
no-networks = Nem található hálózat.
no-vpn = Nincs elérhető VPN-kapcsolat
no-vpn = Nincs elérhető VPN-kapcsolat.
password = Jelszó
password-confirm = Jelszó megerősítése
remove = Eltávolítás
@ -88,20 +88,20 @@ identity = Azonosító
auth-dialog = Azonosítás szükséges
.vpn-description = Írd be a VPN szolgáltatáshoz szükséges felhasználónevet és jelszót.
.wifi-description = Add meg a jelszót vagy a titkosítókulcsot. A router „WPS” gombjának megnyomásával is csatlakozhatsz.
forget-dialog = El akarod felejteni ezt a Wi-Fi hálózatot?
.description = A jelszót ismét meg kell adnod, ha újra csatlakozni szeretnél.
forget-dialog = Elfelejted ezt a Wi-Fi-hálózatot?
.description = A jelszót ismét meg kell majd adnod a kapcsolódáshoz.
network-device-state =
.activated = Csatlakoztatva
.config = Csatlakozás…
.activated = Kapcsolódva
.config = Kapcsolódás…
.deactivating = Bontás…
.disconnected = Kapcsolat bontva
.failed = Csatlakozás sikertelen
.failed = Nem sikerült kapcsolódni
.ip-check = Kapcsolat ellenőrzése
.ip-config = IP és router információk lekérése
.need-auth = Hitelesítés szükséges
.prepare = Felkészülés a kapcsolódáshoz
.secondaries = Várakozás másodlagos kapcsolatra
.unavailable = Nem elérhető
.unavailable = Nem érhető el
.unknown = Ismeretlen állapot
.unmanaged = Kezeletlen
.unplugged = Kábel kihúzva
@ -113,11 +113,13 @@ vpn = VPN
.error = Nem sikerült hozzáadni a VPN-konfigurációt
.remove = Kapcsolati profil törlése
.select-file = VPN-konfigurációs fájl kiválasztása
vpn-error = VPN hiba
vpn-error = VPN-hiba
.config = Nem sikerült hozzáadni a VPN-konfigurációt
.connect = Nem sikerült csatlakozni a VPN-hez
.connection-editor = A hálózati beállító nem működik
.connect = Nem sikerült kapcsolódni a VPN-hez
.connection-editor = A kapcsolatszerkesztő nem működik
.connection-settings = Nem sikerült lekérni az aktív kapcsolatok beállításait
.openvpn-config-path = Érvénytelen OpenVPN-konfiguráció-útvonal
.openvpn-config-path-desc = A kiválasztott fájlnak helyi fájlrendszeren kell lennie.
.updating-state = Nem sikerült frissíteni a hálózatkezelő állapotát
.wireguard-config-path = Érvénytelen fájlelérési út a WireGuard konfigurációhoz
.wireguard-config-path-desc = A kiválasztott fájlnak helyi fájlrendszeren kell lennie.
@ -129,15 +131,15 @@ vpn-error = VPN hiba
[password-flags] jelszóbeállításait
} az nmclivel
wired = Vezetékes
.adapter = Vezetékes adapter { $id }
.adapter = { $id }. vezetékes adapter
.connections = Vezetékes kapcsolatok
.devices = Vezetékes eszközök
.remove = Kapcsolati profil törlése
wifi = Wi-Fi
.adapter = Wi-Fi adapter { $id }
.adapter = { $id }. Wi-Fi-adapter
.forget = Hálózat elfelejtése
wireguard-dialog = WireGuard eszköz hozzáadása
.description = Válassz egy eszköznevet a WireGuard-konfigurációhoz.
.description = Válassz egy eszköznevet a WireGuard konfigurációhoz.
## Networking: Online Accounts
@ -150,19 +152,19 @@ activate = Aktiválás
confirm = Megerősítés
enable = Engedélyezés
bluetooth = Bluetooth
.status = Ez a rendszer { $aliases } néven látható, amíg a Bluetooth beállítások vannak megnyitva.
.connected = Csatlakoztatva
.connecting = Csatlakozás…
.status = Ez a rendszer { $aliases } néven látható, amíg a Bluetooth-beállítások meg vannak nyitva.
.connected = Kapcsolódva
.connecting = Kapcsolódás…
.disconnecting = Bontás…
.connect = Csatlakozás
.connect = Kapcsolódás
.disconnect = Bontás
.forget = Elfelejtés
.dbus-error = Hiba történt a DBus-szal való kommunikáció során: { $why }
.dbus-error = Hiba történt a D-Busszal való kommunikáció során: { $why }
.disabled = A Bluetooth szolgáltatás le van tiltva
.inactive = A Bluetooth szolgáltatás nem aktív
.unknown = A Bluetooth szolgáltatást nem sikerült aktiválni. A BlueZ telepítve van?
bluetooth-paired = Korábban párosított eszközök
.connect = Csatlakozás
bluetooth-paired = Korábban csatlakoztatott eszközök
.connect = Kapcsolódás
.battery = { $percentage }% töltöttség
bluetooth-confirm-pin = Bluetooth PIN megerősítése
.description = Erősítsd meg, hogy a következő PIN megegyezik a(z) { $device } eszközön megjelenített PIN-kóddal
@ -175,7 +177,7 @@ accessibility = Akadálymentesség
.vision = Látás
.on = Bekapcsolva
.off = Kikapcsolva
.unavailable = Nem elérhető
.unavailable = Nem érhető el
.screen-reader = Képernyőolvasó
.high-contrast = Magas kontraszt mód
.invert-colors = Színek invertálása
@ -222,8 +224,8 @@ desktop = Asztal
wallpaper = Háttérkép
.change = Háttérkép váltásának gyakorisága
.fit = Háttérkép illeszkedése
.folder-dialog = Válassz mappát a háttérképekhez
.image-dialog = Válassz képet a háttérképnek
.folder-dialog = Háttérkép-mappa kiválasztása
.image-dialog = Háttérkép kiválasztása
.plural = Háttérképek
.same = Ugyanaz a háttérkép legyen minden kijelzőn
.slide = Diavetítés
@ -272,10 +274,10 @@ window-hint-accent-toggle = Kiemelőszín használata az aktív ablak kiemelés
auto-switch = Automatikus váltás világos és sötét mód között
.sunrise = Napkeltekor világos módra váltás
.sunset = Napnyugtakor sötét módra váltás
.next-sunrise = Következő napfelkeltekor világos módra váltás
.next-sunrise = Következő napkeltekor világos módra váltás
.next-sunset = Következő napnyugtakor sötét módra váltás
container-background = Felületi elemek háttere
.desc-detail = A felületi elemek háttérszínét a navigációs oldalsáv, az oldalsó menü, a párbeszédablakok és hasonló modulok használják. Alapértelmezés szerint a felületi elemek háttérszíne automatikusan az ablak hátteréből származnak.
.desc-detail = A felületi elemek háttérszínét a navigációs oldalsáv, az oldalsó menü, a párbeszédablakok és hasonló modulok használják. Alapértelmezés szerint a felületi elemek háttérszíne automatikusan az ablak hátteréből származik.
.reset = Visszaállítás automatikusra
.desc = Navigációs oldalsávhoz, oldalsó menühöz, párbeszédablakhoz és hasonló modulokhoz használandó
control-tint = Vezérlőelemek színezése
@ -283,22 +285,22 @@ control-tint = Vezérlőelemek színezése
frosted = Tejüveg hatás a rendszerfelületen
.desc = A háttér elmosását alkalmazza a panelre, a dokkra, a kisalkalmazásokra, az indítóra és az alkalmazáskönyvtárra
enable-export = Jelenlegi téma alkalmazása a GNOME-alkalmazásokra
.desc = Nem minden eszközkészlet támogatja az automatikus váltást. Előfordulhat, hogy a téma módosítása után újra kell indítani a nem COSMIC-alapú alkalmazásokat.
.desc = Nem minden eszközkészlet támogatja az automatikus váltást. Előfordulhat, hogy a téma módosítása után újra kell indítani a nem COSMIC-alkalmazásokat.
icon-theme = Ikontéma
.desc = Más ikonkészletet alkalmaz az alkalmazásokra
text-tint = Felületi szöveg árnyalata
.desc = A felület szövegszíneinek meghatározására szolgál, hogy azok megfelelő kontrasztot biztosítsanak különböző felületeken
style = Stílus
.round = Lekerekített
.slightly-round = Némileg lekerekített
.square = Négyzetes
.slightly-round = Enyhén lekerekített
.square = Szögletes
interface-density = Felületsűrűség
.comfortable = Kényelmes
.compact = Kompakt
.spacious = Tágas
window-management-appearance = Ablakkezelés
.active-hint = Aktív ablak kiemelésének mérete
.gaps = Rések a csempézett ablakok körül
.gaps = Hézagok a csempézett ablakok körül
### Experimental
@ -371,7 +373,7 @@ window-controls = Ablakvezérlés
.minimize = Minimalizálás gomb megjelenítése
.active-window-hint = Aktív ablak kiemelése
focus-navigation = Fókusznavigáció
.focus-follows-cursor = Fókusz követi az egeret
.focus-follows-cursor = Fókusz követi a mutatót
.focus-follows-cursor-delay = Fókuszálás késleltetése (ms)
.cursor-follows-focus = Mutató követi a fókuszt
@ -382,7 +384,7 @@ workspaces-behavior = Munkaterületek viselkedése
.dynamic = Dinamikus munkaterületek
.dynamic-desc = Üres munkaterületek automatikus eltávolítása
.fixed = Megadott számú munkaterület
.fixed-desc = Munkaterületek hozzáadása vagy eltávolítása az áttekintésben
.fixed-desc = Munkaterületek hozzáadása vagy eltávolítása az áttekintésben.
workspaces-multi-behavior = Többmonitoros viselkedés
.span = A munkaterületek kiterjednek a kijelzőkre
.separate = A kijelzők külön munkaterülettel rendelkeznek
@ -393,7 +395,7 @@ workspaces-orientation = Munkaterületek tájolása
.vertical = Függőleges
.horizontal = Vízszintes
hot-corner = Aktív sarok
.top-left-corner = A munkaterületek bal felső aktív sarkának engedélyezése
.top-left-corner = Bal felső aktív sarok engedélyezése a munkaterületekhez
## Displays
@ -408,16 +410,16 @@ display = Kijelzők
.arrangement-desc = Húzd a kijelzőket az átrendezésükhöz
.enable = Kijelző engedélyezése
.external = { $size } { $output } külső kijelző
.laptop = { $size } laptop kijelző
.options = Kijelző lehetőségek
.laptop = { $size } laptopkijelző
.options = Kijelzőbeállítások
.refresh-rate = Frissítési gyakoriság
.resolution = Felbontás
.scale = Skálázás
.additional-scale-options = További skálázás
mirroring = Tükrözés
.id = Tükrözés { $id }
.dont = Ne tükrözzön
.mirror = Tükrözze a(z) { $display } kijelzőt
.dont = Tükrözés kikapcsolása
.mirror = A(z) { $display } kijelző tükrözése
.project =
Kivetítés { $display ->
[all] az összes kijelzőre
@ -426,16 +428,16 @@ mirroring = Tükrözés
.project-count =
Kivetítés { $count } további { $count ->
[1] kijelzőre
*[other] kijelzőkre
*[other] kijelzőre
}
night-light = Éjszakai fény
.auto = Automatikus (naplementétől napkeltéig)
.desc = Kék fény csökkentése melegebb színekkel
orientation = Tájolás
.standard = Normál
.rotate-90 = 90 fokos elforgatás
.rotate-180 = 180 fokos elforgatás
.rotate-270 = 270 fokos elforgatás
.rotate-90 = Elforgatás 90°-kal
.rotate-180 = Elforgatás 180°-kal
.rotate-270 = Elforgatás 270°-kal
vrr = Változó frissítési gyakoriság
.enabled = Engedélyezve
.force = Mindig
@ -489,11 +491,11 @@ battery = Akkumulátor
[one] nap
*[other] nap
}
.less-than-minute = Kevesebb, mint egy perc
.less-than-minute = Kevesebb mint egy perc
.and = és
.remaining-time =
{ $time } { $action ->
[full] a teljes töltöttségig
[full] a teljes feltöltésig
*[other] a lemerülésig
}
connected-devices = Csatlakoztatott eszközök
@ -546,7 +548,7 @@ keyboard-sources = Bemeneti források
keyboard-special-char = Speciális karakter beírása
.alternate = Alternatív karakterek billentyűje
.compose = Kombináló billentyű
.compose-desc = A kombináló billentyű lehetővé teszi számos karakter bevitelét. Használatához nyomd meg a billentyűt, majd gépeld be a karakterek sorozatát. Például a kombináló billentyű, majd C és o lenyomásával a © jelet kapod, míg a és után á jelenik meg.
.compose-desc = A kombináló billentyű lehetővé teszi számos karakter bevitelét. Használatához nyomd meg a billentyűt, majd gépeld be a karakterek sorozatát. Például a kombináló billentyű, majd „C” és „o” lenyomásával a „©” jelet kapod, míg „a” és „‘” után „á” jelenik meg.
.caps = Caps Lock billentyű
keyboard-typing-assist = Gépelés
.repeat-rate = Ismétlési sebesség
@ -586,7 +588,7 @@ replace = Csere
shortcut-name = Gyorsbillentyű neve
system-controls = Rendszervezérlés
terminate = Befejezés
toggle-stacking = Az ablakok egymásra helyezésének be- és kikapcsolása
toggle-stacking = Ablakok egymásra helyezése be/ki
type-key-combination = Billentyűkombináció
custom-shortcuts = Egyéni gyorsbillentyűk
.add = Gyorsbillentyű hozzáadása
@ -623,7 +625,7 @@ manage-windows = Ablakok kezelése
.minimize = Ablak minimalizálása
.resize-inwards = Ablak átméretezése befelé
.resize-outwards = Ablak átméretezése kifelé
.toggle-sticky = Ragadós ablak be- és kikapcsolása
.toggle-sticky = Ragadós ablak be/ki
move-windows = Ablakok mozgatása
.direction =
Ablakok mozgatása { $direction ->
@ -650,15 +652,15 @@ move-windows = Ablakok mozgatása
.prev-workspace = Ablak áthelyezése az előző munkaterületre
.next-workspace = Ablak áthelyezése a következő munkaterületre
.last-workspace = Ablak áthelyezése az utolsó munkaterületre
.next-display = Ablak áthelyezése a következő monitorra
.prev-display = Ablak áthelyezése az előző monitorra
.next-display = Ablak áthelyezése a következő kijelzőre
.prev-display = Ablak áthelyezése az előző kijelzőre
.send-to-prev-workspace = Ablak áthelyezése az előző munkaterületre
.send-to-next-workspace = Ablak áthelyezése a következő munkaterületre
system-shortcut = Rendszer
.app-library = Alkalmazáskönyvtár megnyitása
.brightness-down = Kijelző fényerejének csökkentése
.brightness-up = Kijelző fényerejének növelése
.display-toggle = Belső kijelző be- vagy kikapcsolása
.display-toggle = Belső kijelző be/ki
.home-folder = Saját mappa megnyitása
.keyboard-brightness-down = Billentyűzet fényerejének csökkentése
.keyboard-brightness-up = Billentyűzet fényerejének növelése
@ -674,20 +676,20 @@ system-shortcut = Rendszer
.screenshot = Képernyőkép készítése
.suspend = Felfüggesztés
.terminal = Terminál megnyitása
.touchpad-toggle = Érintőpárna be- vagy kikapcsolása
.touchpad-toggle = Érintőpárna be/ki
.volume-lower = Hangkimenet hangerejének csökkentése
.volume-raise = Hangkimenet hangerejének növelése
.web-browser = Böngésző megnyitása
.web-browser = Webböngésző megnyitása
.window-switcher = Váltás a nyitott ablakok között
.window-switcher-previous = Váltás a nyitott ablakok között fordított sorrendben
.workspace-overview = Munkaterület áttekintésének megnyitása
window-tiling = Ablakcsempézés
.horizontal = Vízszintes tájolás beállítása
.vertical = Függőleges tájolás beállítása
.swap-window = Ablak felcserélése
.toggle-tiling = Az ablakok csempézésének be- és kikapcsolása
.toggle-stacking = Az ablakok egymásra helyezésének be- és kikapcsolása
.toggle-floating = Az ablakok lebegtetésének be- és kikapcsolása
.swap-window = Ablakok felcserélése
.toggle-tiling = Ablakcsempézés be/ki
.toggle-stacking = Ablakok egymásra helyezése be/ki
.toggle-floating = Ablakok lebegtetése be/ki
.toggle-orientation = Tájolás váltása
replace-shortcut-dialog = Lecseréled a billentyűt?
.desc = A(z) { $shortcut } billentyűt a(z) { $name } használja. Ha lecseréled, a(z) { $name } le lesz tiltva.
@ -697,20 +699,20 @@ zoom-out = Kicsinyítés
## Input: Mouse
mouse = Egér
.speed = Egér sebessége
.speed = Egérsebesség
.acceleration = Egérgyorsítás engedélyezése
## Input: Touchpad
click-behavior = Kattintási viselkedés
.click-finger = Másodlagos kattintás két ujjal és középső kattintás három ujjal
.button-areas = Másodlagos kattintás a jobb alsó sarokban, középső kattintás az alsó középső sarokban
.button-areas = Másodlagos kattintás a jobb alsó sarokban és középső kattintás alul középen
pinch-to-zoom = Csippentéses nagyítás
.desc = Két ujjal belenagyíthatsz a tartalomba azokban az alkalmazásokban, amik támogatják a nagyítást
.desc = Két ujjal belenagyíthatsz a tartalomba azokban az alkalmazásokban, amelyek támogatják a nagyítást
tap-to-click = Koppintás a kattintáshoz
.desc = Engedélyezi az egyujjas koppintást az elsődleges kattintáshoz, a kétujjas koppintást a másodlagos kattintáshoz és a háromujjas érintést a középső kattintáshoz
.desc = Engedélyezi az egyujjas koppintást az elsődleges kattintáshoz, a kétujjas koppintást a másodlagos kattintáshoz és a háromujjas koppintást a középső kattintáshoz
touchpad = Érintőpárna
.acceleration = Az Érintőpárna gyorsításának engedélyezése
.acceleration = Az érintőpárna gyorsításának engedélyezése
.speed = Érintőpárna sebessége
## Input: Gestures
@ -768,7 +770,7 @@ applications = Alkalmazások
## Applications: Default Applications
default-apps = Alapértelmezett alkalmazások
.web-browser = Böngésző
.web-browser = Webböngésző
.file-manager = Fájlkezelő
.mail-client = Levelezőprogram
.music = Zene
@ -786,27 +788,27 @@ startup-apps = Indítási alkalmazások
.add = Alkalmazás hozzáadása
.user = Bejelentkezéskor indított alkalmazások
.none = Nincs indítási alkalmazás hozzáadva
.remove-dialog-title = { $name } eltávolítása?
.remove-dialog-title = Eltávolítod a(z) { $name } alkalmazást?
.remove-dialog-description = Eltávolítod ezt az indítási alkalmazást?
.add-startup-app = Indítási alkalmazás hozzáadása
## Applications: Legacy Applications
legacy-applications = X11 alkalmazások támogatása
legacy-app-global-shortcuts = Globális gyorsbillentyűk X11 alkalmazásokban
.desc = A globális gyorsbillentyűk lehetővé teszik, hogy az alkalmazásokban a billentyűleütéseket és egérkattintásokat más alkalmazások is felismerjék, például a „push-to-talk” vagy a „push-to-mute” funkciókhoz. Alapértelmezés szerint a globális gyorsbillentyűk az X11 alkalmazásokban le van tiltva, hogy más alkalmazások ne figyelhessék a billentyű- és egéreseményeket, amelyek érzékeny információkat tartalmazhatnak.
legacy-applications = X11-alkalmazások támogatása
legacy-app-global-shortcuts = Globális gyorsbillentyűk X11-alkalmazásokban
.desc = A globális gyorsbillentyűk lehetővé teszik, hogy az alkalmazásokban a billentyűleütéseket és egérkattintásokat más alkalmazások is felismerjék, például a „push-to-talk” vagy a „push-to-mute” funkciókhoz. Alapértelmezés szerint a globális gyorsbillentyűk az X11-alkalmazásokban le van tiltva, hogy más alkalmazások ne figyelhessék a billentyű- és egéreseményeket, amelyek érzékeny információkat tartalmazhatnak.
.none = Nincs billentyű
.modifiers = Módosítók (Super, Shift, Control, Alt)
.combination = Minden billentyű, miközben a Super, Control vagy Alt módosítók lenyomva vannak
.all = Minden billentyű
.mouse = Egérgomb-események X11 alkalmazásokban
legacy-app-scaling = X11-ablakrendszer alkalmazásméretezés
.mouse = Egérgomb-események X11-alkalmazásokban
legacy-app-scaling = X11-alkalmazások méretezése
.scaled-gaming = Optimalizálás játékokra és teljes képernyős alkalmazásokra
.gaming-description = Az X11 alkalmazások kicsit nagyobbnak/kisebbnek tűnhetnek a Wayland alkalmazásokhoz képest
.gaming-description = Az X11-alkalmazások kicsit nagyobbnak/kisebbnek tűnhetnek a Wayland-alkalmazásokhoz képest
.scaled-applications = Optimalizálás alkalmazásokra
.applications-description = A játékok és a teljes képernyős X11 alkalmazások felbontása eltérhet a kijelző natív felbontásától
.applications-description = A játékok és a teljes képernyős X11-alkalmazások felbontása eltérhet a kijelző natív felbontásától
.scaled-compatibility = Maximális kompatibilitási mód
.compatibility-description = Az X11 alkalmazások elmosódottan jelenhetnek meg HiDPI képernyőkön
.compatibility-description = Az X11-alkalmazások elmosódottan jelenhetnek meg HiDPI képernyőkön
.preferred-display = Preferált kijelző játékokhoz és teljes képernyős X11-alkalmazásokhoz
.no-display = Nincs
@ -817,7 +819,7 @@ system = Rendszer és fiókok
## System: About
about = Rendszerinformáció
about-device = Eszköz neve
about-device = Eszköznév
.desc = Ez a név más hálózati vagy Bluetooth-eszközök számára látható
about-hardware = Hardver
.model = Hardvermodell
@ -827,8 +829,8 @@ about-hardware = Hardver
.disk-capacity = Tárhely
about-os = Operációs rendszer
.os = Operációs rendszer
.os-architecture = Operációs rendszer architektúra
.kernel = Kernel verzió
.os-architecture = Operációs rendszer architektúrája
.kernel = Kernelverzió
.desktop-environment = Asztali környezet
.windowing-system = Ablakrendszer
about-related = Kapcsolódó beállítások
@ -858,7 +860,7 @@ amplification = Erősítés
qr-code-unavailable = Nem érhető el a QR-kód
network-name = Hálózat neve
share = Hálózat megosztása
scan-to-connect-description = Olvasd be a QR-kódot a hálózathoz való csatlakozáshoz.
scan-to-connect-description = Olvasd be a QR-kódot a hálózathoz való kapcsolódáshoz.
place-here = Helyezd ide a kisalkalmazásokat
sound-device-port-unplugged = Nincs csatlakoztatva
sound-hd-audio = HD-hang
@ -884,7 +886,7 @@ xdg-entry-panel-keywords = COSMIC;Panel;Kisalkalmazás;
xdg-entry-time-language-comment = Rendszerdátum, idő, régió és a nyelv kezelése
xdg-entry-desktop-keywords = COSMIC;Asztal;
xdg-entry-system-keywords = COSMIC;Rendszer;Információk;Fiókok;Firmware;
xdg-entry-about-keywords = COSMIC;Névjegy
xdg-entry-about-keywords = COSMIC;Névjegy;
xdg-entry-panel = Panel
xdg-entry-a11y-keywords = COSMIC;Akadálymentesség;Képernyő;Olvasó;Nagyító;Kontraszt;Szín;
xdg-entry-date-time-keywords = COSMIC;Idő;Zóna;
@ -907,10 +909,16 @@ xdg-entry-x11-applications-keywords = COSMIC;X11;Alkalmazás;Játék;Támogatás
xdg-entry-touchpad-keywords = COSMIC;Érintőpárna;Mozdulat;
xdg-entry-wireless-keywords = COSMIC;WiFi;Wi-Fi;Hálózat;Kapcsolat;
xdg-entry-wireless = Wi-Fi
xdg-entry-workspaces-keywords = COSMIC;Munkaterület;Tájolás;Áttekintés;Monitor;
xdg-entry-workspaces-keywords = COSMIC;Munkaterület;Tájolás;Áttekintés;Monitor;Kijelző;Képernyő;
xdg-entry-wallpaper-keywords = COSMIC;Háttérkép;Háttér;Diavetítés;
xdg-entry-users-keywords = COSMIC;Felhasználó;Fiók;
xdg-entry-vpn-keywords = COSMIC;VPN;Hálózat;Kapcsolat;OpenVPN;OpenConnect;
xdg-entry-comment = Beállításalkalmazás a COSMIC asztalhoz
xdg-entry-window-management-keywords = COSMIC;Ablak;Kezelés;Csempézés;Super;BIllentyű;
xdg-entry-window-management-keywords = COSMIC;Ablak;Kezelés;Csempézés;Super;Billentyű;
no-search-results = Nem található a keresésnek megfelelő hálózat.
workspaces-navigation = Navigáció
.wraparound = Váltás az első és az utolsó munkaterület között gyorsbillentyűkkel és gesztusokkal
bluetooth-display-passkey = Bluetooth-párosítás
.description = Írd be a következő párosítási kulcsot a(z) { $device } eszközön, majd nyomd meg az Enter billentyűt
bluetooth-display-pin = Bluetooth-párosítás
.description = Írd be a következő PIN-kódot a(z) { $device } eszközön, majd nyomd meg az Enter billentyűt

View file

@ -61,6 +61,8 @@ vpn-error = Galat VPN
.connect = Gagal menyambungkan ke VPN
.connection-editor = Penyunting sambungan gagal
.connection-settings = Gagal mendapatkan pengaturan untuk sambungan aktif
.openvpn-config-path = Jalur berkas tidak valid untuk konfig OpenVPN
.openvpn-config-path-desc = Berkas dipilih harus berada di sistem berkas lokal.
.updating-state = Gagal memperbarui status pengelola jaringan
.wireguard-config-path = Jalur berkas tidak valid untuk konfigurasi WireGuard
.wireguard-config-path-desc = Berkas yang dipiih harus di sistem berkas lokal.
@ -818,3 +820,9 @@ xdg-entry-x11-applications = Kompatibilitas Aplikasi X11
xdg-entry-window-management-comment = Tindakan tombol Super, opsi kontrol jendela, dan pengubinan jendela tambahan
xdg-entry-wired-comment = Sambungan berkabel dan profil sambungan
no-search-results = Tidak ada jaringan yang sesuai pencarian anda.
workspaces-navigation = Navigasi
.wraparound = Pindahkan antara ruang kerja pertama dan terakhir menggunakan pintasan papan ketik dan gerakan
bluetooth-display-passkey = Pemasangan Bluetooth
.description = Silakan ketik kunci sandi berikut pada { $device }, kemudian tekan Enter
bluetooth-display-pin = Pemasangan Bluetooth
.description = Silakan ketik PIN berikut pada { $device }, kemudian tekan Enter

View file

@ -818,3 +818,5 @@ xdg-entry-x11-applications = X11 қолданбаларының үйлесімд
xdg-entry-window-management-comment = Super пернесінің әрекеті, терезелерді басқару опциялары және терезелерді қатар қоюдың қосымша опциялары
xdg-entry-wired-comment = Сымды қосылымдар және қосылымдар профильдері
no-search-results = Сіздің іздеуіңізге ешбір желі сәйкес келмейді.
workspaces-navigation = Навигация
.wraparound = Бірінші және соңғы жұмыс орындары арасында пернетақта жарлықтары мен ым қимылдар арқылы ауысу

View file

@ -0,0 +1,828 @@
save = Tomar bike
cancel = Têk bibe
close = Bigire
password = Borînpeyv
forget-dialog = Ev tora Wi-Fi ji bîr bike?
.description = Ji bo ku tu di pêşerojê de vê tora Wi-Fi bikar bînî, divê tu dîsa borînpeyvekê binivîsî.
remove = Rake
import = Têxîne
export = Derxîne
username = Navê bikarhêner
light = Ronî
identity = Nasname
applets = Sepanok
password-confirm = Borînpeyvê biperjîne
dark = Tarî
confirm = Bipejirîne
settings = Sazkarî
replace = Biguhêrîne
add = Tevlî bike
appearance = Xuyang
panel = Destgeh
connect = Girê bide
unknown = Nenas
connected = Hat girêdan
panel-left = Çep
super-key = Çalakiya bişkoka Super
.launcher = Destpêkerê veke
.workspaces = Cîhên xebatê veke
.applications = Sepanan veke
.disable = Neçalak bike
x-minutes =
{ $number } { $number ->
[one] xulek
*[other] xulek
}
vrr = Rêjeya nûkirina guhêrbar
.enabled = Çalak e
.force = Her dem
.auto = Xweber
.disabled = Neçalak e
window-hint-accent = Rengê nîşana çarçoveya çalak
fit-to-screen = Li gorî dîmenderê tije bike
control-tint = Rengkirina hêmanên kontrolê
.desc = Ji bo paşrûyê bişkokên standard, ketanên lêgerînê, ketanên nivîsê û hêmanên wekhev tê bikaranîn
toggle-stacking = Komkirina çarçoveyê biguhêrîne
panel-top = Jor
no-applets-found = Tu sepanok nehatin dîtin...
region = Herêm
zoom-in = Nêzîk bike
place-here = Sepanokan li vir bi cih bike
-requires-restart = Pêdivî bi jinûve despêkirinê heye
workspaces-orientation = Arasteya qadên xebatê
.vertical = Stûnî
.horizontal = Asoyî
acceleration-desc = Hestiyariya şopandinê li gorî lezê xweber diguhêrîne
all-displays = Hemû dîmender
scheduling = Demsazkirin
.manual = Demsazkirina destî
frosted = Bandora cama qeşa li ser navrûya pergalê
.desc = Li ser destgeh, bender, sepanok, destpêker û pirtûkxaneya sepanan bandora mijîkirina paşrûyê disepîne
about-hardware = Reqalav
.model = Modêla reqalavê
.memory = Bîrdank
.processor = Pêvajoker
.graphics = Grafîk
.disk-capacity = Kapasîteya dîskê
short = Kurt
vpn = VPN
.connections = Girêdanên VPNê
.error = Tevlîkirina rêkxistinê VPNê têk çû
.remove = Profîla girêdanê rake
.select-file = Peleke rêkxistinê VPNê hilbijêre
power-mode = Awaya hêzê
.battery = Jiyana betariyê dirêj bike
.battery-desc = Bikaranîna hêzê kêmkirî û performansa bêdeng
.balanced = Hevseng
.balanced-desc = Performansa bêdeng û bikaranîna hêzê navîn
.performance = Performansa bilind
.performance-desc = Performansa bilind û bikaranîna hêzê
.no-backend = Backend nehat dîtin. system76-power an jî power-profiles-daemon saz bike.
wired = Têlkirî
.adapter = Adaptorê têlkirî { $id }
.connections = Girêdanên têlkirî
.devices = Amûrên têlkirî
.remove = Profîla girêdanê rake
bluetooth-available = Amûrên nêzîk
cable-unplugged = Kebl nehatiye girêdan
color-picker = Hilbijêrê rengan
interface-font = Cûrenivîsa pergalê
default = Berdest
zoom-out = Dûr bixe
hot-corner = Quncika germ
.top-left-corner = Ji bo qadên xebatê quncika çepê ya jorîn çalak bike
keyboard-typing-assist = Nivîsandin
.repeat-rate = Rêjeya dûbarekirinê
.repeat-delay = Derengiya dûbarekirinê
edge-gravity = Çarçoveyên herikbar ber bi qeraxên nêzîk ve tê kişandin
amplification = Bilindkirina dengê
.desc = Mafê dide ku deng heya 150% were bilindkirin
recent-colors = Rengên dawîn
about-os = Pergala xebatê
.os = Pergala xebatê
.os-architecture = Avasaziya pergala xebatê
.kernel = Guhertoya kernelê
.desktop-environment = Hawirdora sermaseyê
.windowing-system = Pergala çarçoveyê
panel-behavior-and-position = Tevger û cih
.autohide = Destgehê xweber veşêre
.dock-autohide = Benderê xweber veşêre
.position = Cihê li ser dîmenderê
.display = Li ser dîmenderê nîşan bide
add-language = Zimên tevlî bike
.context = Zimên tevlî bike
password-mismatch = Borînpeyv û piştrastkirin divê li hev bikin
accessibility = Gihîştin
.vision = Dîtin
.on = Vekirî ye
.off = Girtî ye
.unavailable = Ne pêkan e
.screen-reader = Xwînerê dîmenderê
.high-contrast = Awayê berevajiya bilind
.invert-colors = Rengan berevajî bike
.color-filters = Parzûnên rengan
container-background = Paşrûyê konteynirê
.desc-detail = Rengê paşrûyêa konteynirê ji bo gerandina darika kêlekê, kaşoya alî, gotûbêj û alavokên wekhev tê bikaranîn. Bi berdestî, rengê paşrûya konteynirê xweber ji paşrûyê çarçoveyê tê girtin.
.reset = Vegere awayê xweber
.desc = Ji bo gerandina benda kêlekê, kaşoya alî, gotûbêj û alavokên wekhev tê bikaranîn
primary-button = Bişkoka sereke
.desc = Rêza bişkokên fîzîkî diyar dike
.left = Çep
.right = Rast
sound-alerts = Hişyarî
.volume = Asta dengê hişyariyan
.sound = Dengê hişyariyan
icon-theme = Rûkara îkonan
.desc = Komkeke cûda ya îkonan li ser sepanan disepîne
keyboard-shortcuts = Kurteriyên kilîtdankê
.desc = Kurteriyan nîşan bide û kesane bike
switch-between-windows = Di navbera çarçoveyan de biguhêrîne
sound-usb-audio = Dengê USB
text-tint = Rengkirina nivîsa navrûyê
.desc = Ji bo bidestxistina rengên nivîsa navrûyê ku li ser rûyên cûda berevajiyeke têrker pêşkêş dikin tê bikaranîn
online-accounts = Jimarên serhêl
.desc = Jimar, IMAP û SMTP û têketinên pargîdaniyê tevlî bike
open-workspaces-view = Pêşdîtina qadên xebatê veke
all = Hemû
remove-user = Bikarhêner rake
sound-input = Ketan
.volume = Asta dengê ketanê
.device = Amûra ketanê
.level = Asta ketanê
add-another-keybinding = Kurteriyeke din tevlî bike
copy-to-clipboard = Bo jêgirtankê jê bigire
monospace-font = Cûrenivîsa monospace
experimental-settings = Sazkariyên ezmûnkirî
recent-folders = Peldankên dawîn
color-filter = Cûreyê parzûna rengan
.unknown = Parzûna nenas çalak e
.greyscale = Rengên gewr
.deuteranopia = Kesk/Sor (kêmbûna kesk, Deuteranopia)
.protanopia = Sor/Kesk (kêmbûna sor, Protanopia)
.tritanopia = Şîn/Zer (kêmbûna şîn, Tritanopia)
migrate-workspace =
Qada xebatê bibe derana { $direction ->
*[down] jêr
[left] çep
[right] rast
[up] jor
}
battery = Betarî
.minute =
{ $value } { $value ->
[one] xulek
*[other] xulek
}
.hour =
{ $value } { $value ->
[one] demjimêr
*[other] demjimêr
}
.day =
{ $value } { $value ->
[one] roj
*[other] roj
}
.less-than-minute = Kêmtir ji xulekekê
.and = û
.remaining-time =
{ $time } heya { $action ->
[full] tije
*[other] vala
}
search-applets = Sepanokan bigere...
pinch-to-zoom = Ji bo nêzîkbûnê bitikîne
.desc = Ji bo sepanên ku piştgiriya zoomê dikin, du tiliyan bikar bîne da ku nêzî naverokê bibe
wifi = Wi-Fi
.adapter = Adaptorê Wi-Fi { $id }
.forget = Ev tor ji bîr bike
x-hours =
{ $number } { $number ->
[one] demjimêr
*[other] demjimêr
}
added = Hate tevlîkirin
input-source-switch = Çavkaniya ketanê zimanê kilîtdankê biguhêrîne
focus-navigation = Gerandina balkişandinê
.focus-follows-cursor = Balkêşî nîşanderê dişopîne
.focus-follows-cursor-delay = Derengiya balkêşiyê nîşanderê dişopîne di mç de
.cursor-follows-focus = Nîşander balkêşiyê dişopîne
full-name = Navê tije
app-background = Paşrûyê çarçoveyê
panel-right = Rast
sound-device-profiles = Profîlên amûrê
keyboard-sources = Çavkaniyên ketanê
.desc = Çavkaniyên ketanê dikarin bi riya bikaranîna kombînasyona kilîtên Super+Space werin guhertin. Ev dikare di sazkariyên kurteriyên kilîtdankê de were kesanekirin.
.move-up = Bilivîne jor
.move-down = Bilivîne jêr
.settings = Sazkarî
.view-layout = Awayê kilîtdankê nîşan bide
.remove = Rake
.add = Çavkaniya ketanê tevlî bike
nav-shortcuts = Gerandin
.prev-output = Balê bikişîne ser derana paş
.next-output = Balê bikişîne ser derana pêş
.last-workspace = Balê bikişîne ser qada xebatê ya dawî
.prev-workspace = Balê bikişîne ser qada xebatê ya paş
.next-workspace = Balê bikişîne ser qada xebatê ya pêş
.focus =
Balkişandina çarçoveyê { $direction ->
*[down] jêr
[in] hundir
[left] çep
[out] derve
[right] rast
[up] jor
}
.output =
Deranê biguhêrîne { $direction ->
*[down] jêr
[left] çep
[right] rast
[up] jor
}
.workspace = Biguhêrîne bo qada xebatê { $num }
hearing = Guhdarîkirin
.mono = Dengê stereo wekî mono bilîze
preferred-languages = Zimanên bijarte
.desc = Rêza zimanan diyar dike ka kîjan ziman ji bo navrûya bikarhêner tê bikaranîn. Guhertin di têketina din de dikevin bandorê.
sound-hd-audio = Dengê HD
network-and-wireless = Tor & Bêtêlî
panel-style = Şêwaz
.anchor-gap = Valahiya di navbera destgeh û qeraxên dîmenderê de
.dock-anchor-gap = Valahiya di navbera bender û qeraxên dîmenderê de
.extend = Destgehê dirêjî qeraxên dîmenderê bike
.dock-extend = Benderê dirêjî qeraxên dîmenderê bike
.appearance = Xuyang
.size = Mezinahî
.background-opacity = Ronahiya paşrûyê
panel-appearance = Xuyang
.match = Li gorî sermaseyê li hev bîne
.light = Ronî
.dark = Tarî
workspaces-multi-behavior = Tevgera pir-dîmender
.span = Dîmender qadên xebatê yên hevpar parve dikin
.separate = Her dîmender xwedî qadên xebatê yên serbixwe ye
replace-shortcut-dialog = Kurteriyê biguherîne?
.desc = { $shortcut } ji aliyê { $name } ve tê bikaranîn. Ku tu wê biguhêrînî, { $name } wê were neçalakkirin.
rgb = RGB
about-device = Navê amûrê
.desc = Ev nav ji bo amûrên din ên torê an Bluetoothê xuya dike
no-vpn = Tu girêdanên VPNê heyî tune ne.
start-segment = Beşa destpêkê
desktop = Sermase
reset-to-default = Vegere rewşa berdest
debug = Debug
enable-export = Rûkara heyî li ser sepanên GNOME disepîne
.desc = Ne hemû amûr piştgiriya guhertina xweber dikin. Sepanên ne-COSMIC dibe ku piştî guhertina rûkarê pêdivî bi jinûvedestpêkirinê bike.
shortcut-name = Navê kurteriyê
style = Şêwaz
.round = Gilover
.slightly-round = Nîv gilover
.square = Çargoşe
panel-applets = Rêkxistin
.dock-desc = Sepanokên benderê bi rêk bixe
.desc = Sepanokên destgehê bi rêk bixe
qr-code-unavailable = Koda QR ne pêkan e
small = Biçûk
window-tiling = Berhevkirina çarçoveyê
.horizontal = Berhevkirina asoyî saz bike
.vertical = Berhevkirina stûnî saz bike
.swap-window = Berhevkirina biguhêrîne
.toggle-tiling = Berhevkirina çarçoveyê biguhêrîne
.toggle-stacking = Komkirina çarçoveyê biguhêrîne
.toggle-floating = Çarçoveyê herikbar biguhêrîne
.toggle-orientation = Arasteyê biguherîne
change-password = Borînpeyvê biguhêrîne
disable-while-typing = Di dema nivîsandinê de asteng bike
long = Dirêj
copied-to-clipboard = Bo jêgirtankê hate jêgirtin
bluetooth-confirm-pin = PINa Bluetoothê bipejirîne
.description = Tika ye piştrast bike ka PINa jêrîn bi ya ku li ser { $device } xuya dibe re yek e
legacy-app-scaling = Pîvandina sepanên pergala çarçoveyê X11
.scaled-gaming = Ji bo lîstik û sepanên dîmendera tije baştir bike
.gaming-description = Dibe ku sepanên X11 li gorî sepanên Wayland hinekî mezintir/biçûktir xuya bibin
.scaled-applications = Ji bo sepanan baştir bike
.applications-description = Dibe ku lîstik û sepanên X11 ên dîmendera tije bi çareseriya dîmenderê re nelihevhatî bin
.scaled-compatibility = Awaya lihevhatinê ya herî pir
.compatibility-description = Dibe ku sepanên X11 li ser dîmenderên HiDPI mijok xuya bibin
.preferred-display = Dîmenderê bijarte ji bo lîstik û sepanên X11 ên dîmendera tije
.no-display = Ne yek
night-light = Ronahiya şevê
.auto = Xweber (rojavabûn heya hilatina rojê)
.desc = Bi rengên germ ronahiya şîn kêm bike
custom-shortcuts = Kurteriyên kesane
.add = Kurteriyê tevlî bike
.context = Kurteriya kesane tevlî bike
.none = Tu kurteriyên kesane tune ne
visible-networks = Torên dîtbar
terminate = Biqedîne
window-hint-accent-toggle = Rengê sereke yê rûkarê wekî nîşana çarçoveya çalak bi kar bîne
type-to-search = Ji bo lêgerînê binivîse...
workspaces-behavior = Tevgera qada xebatê
.dynamic = Qadên xebatê yên dînamîk
.dynamic-desc = Qadên xebatê yên vala xweber radike.
.fixed = Jimara guhertî ya qadên xebatê
.fixed-desc = Di pêşdîtinê de qadên xebatê tevlî bike yan jî rake.
wireguard-dialog = Amûra WireGuard tevlî bike
.description = Ji bo rêkxistina WireGuard navekî amûrê hilbijêre.
remove-connection-dialog = Profîla girêdanê rake?
.vpn-description = Ji bo ku tu di pêşerojê de vê torê bi kar bînî, divê tu dîsa borînpeyvekê têxînî.
.wired-description = Ji bo ku tu di pêşerojê de vê profîlê bi kar bînî, divê tu dîsa biafirînî.
click-behavior = Tevgera tikandinê
.click-finger = Tikandinê duyemîn bi du tiliyan û tikandinê navîn bi sê tiliyan
.button-areas = Tikandinê duyemîn li quncika rastê ya jêrîn û tikandinê navîn li navenda jêrîn
move-windows = Çarçoveyan bilivîne
.direction =
Çarçoveyê bilivîne { $direction ->
*[down] jêr
[left] çep
[right] rast
[up] jor
} bilivîne
.display =
Çarçoveyê bilivîne dîmendera din { $direction ->
*[down] jêr
[left] çep
[right] rast
[up] jor
}
.workspace =
Çarçoveyê bilivîne qada xebatê ya din { $direction ->
*[below] jêr
[left] çep
[right] rast
[above] jor
}
.workspace-num = Çarçoveyê bilivîne qada xebatê { $num }
.prev-workspace = Çarçoveyê bilivîne qada xebatê ya paş
.next-workspace = Çarçoveyê bilivîne qada xebatê ya pêş
.last-workspace = Çarçoveyê bilivîne qada xebatê ya dawî
.next-display = Çarçoveyê bilivîne dîmenderê pêş
.prev-display = Çarçoveyê bilivîne dîmenderê paş
.send-to-prev-workspace = Çarçoveyê bilivîne qada xebatê ya paş
.send-to-next-workspace = Çarçoveyê bilivîne qada xebatê ya pêş
switch-workspaces = Qadên xebatê biguhêrîne
.horizontal = Bi çar tiliyan ber bi çepê/rastê ve bikişîne
.vertical = Bi çar tiliyan ber bi jor/jêr ve bikişîne
applications = Sepan
shadow-and-corners = Sî û quncikên çarçoveyê
system-controls = Kontrolên pergalê
auto-switch = Di navbera awayên ronî û tarî de xweber biguhêrîne
.sunrise = Di dema hilatina rojê de derbasî awayê ronî dibe
.sunset = Di dema rojavabûnê de derbasî awayê tarî dibe
.next-sunrise = Di hilatina rojê ya bê de derbasî awayê ronî dibe
.next-sunset = Di rojavabûna bê de derbasî awayê tarî dibe
keyboard-numlock-boot = Numlock
.boot-state = Rewşa dema vêxistinê
.last-boot = Vêxistina dawî
.on = Vekirî ye
.off = Girtî ye
.set = Rewşa Numlock a dema vêxistinê saz bike
about-related = Sazkariyên têkildar
.support = Piştgiriyê werbigire
tap-to-click = Ji bo tikandinê pê lê bike
.desc = Ji bo tikandinê sereke bi yek tilî, ji bo tikandinê duyemîn bi du tilî, û ji bo tikandinê navîn bi sê tilî çalak dike
interface-density = Tîrbûna navrûyê
.comfortable = Hedar
.compact = Pêkhatî
.spacious = Fereh
open-application-library = Pirtûkxaneya sepanê veke
slow = Hêdî
system-shortcut = Pergal
.app-library = Pirtûkxaneya sepanan veke
.brightness-down = Ronahiya dîmenderê kêm bike
.brightness-up = Ronahiya dîmenderê bêtir bike
.display-toggle = Dîmenderê navxweyî biguhêrîne
.home-folder = Peldanka rûpela sereke veke
.keyboard-brightness-down = Ronahiya kilîtdankê kêm bike
.keyboard-brightness-up = Ronahiya kilîtdankê bêtir bike
.launcher = Destpêkerê veke
.log-out = Derkeve
.lock-screen = Dîmenderê kilît bike
.mute = Dengê deranê qut bike
.mute-mic = Dengê ketanê mîkrofonê qut bike
.play-pause = Bilizîne/rawestîne
.play-next = Strana pêş
.play-prev = Strana paş
.poweroff = Vemrîne
.screenshot = Wêneyê dîmenderê bigire
.suspend = Rawestîne
.terminal = Termînalê veke
.touchpad-toggle = Touchpadê biguhêrîne
.volume-lower = Asta dengê derana kêm bike
.volume-raise = Asta dengê derana bêtir bike
.web-browser = Gerokeke tevnê veke
.window-switcher = Di navbera çarçoveyên vekirî de biguhêrîne
.window-switcher-previous = Di navbera çarçoveyên vekirî de berevajî biguhêrîne
.workspace-overview = Pêşdîtina qadên xebatê veke
network-name = Navê torê
large = Mezin
scrolling = Şemitandin
.two-finger = Bi du tiliyan bişemtîne
.edge = Bi tilîyekê li ser qeraxê bişemtîne
.speed = Leza şemitandinê
.natural = Şemitandinê xwezayî
.natural-desc = Li şûna nîşandanê, naverokê bişemtîne
auth-dialog = Rastandin pêdivî ye
.vpn-description = Navê bikarhêner û borînpeyva ku ji bo rajekarê VPNê pêdivî ye têxîne.
.wifi-description = Borînpeyv an koda şîfrekirinê têxîne. Tu dikarî her wiha bi riya tikandina bişkoka "WPS" ya li ser routerê jî girê bidî.
add-user = Bikarhêner tevlî bike
enable = Çalak bike
add-image = Wêneyê tevlî bike
migrate-workspace-next = Qada xebatê bibe derana pêş
formatting = Awa
.dates = Dîrok
.time = Dem
.date-and-time = Dîrok & Dem
.numbers = Jimmar
.measurement = Pîvan
.paper = Pelik
disabled = Neçalakkirî
add-applet = Sepanokê tevlî bike
show-extended-input-sources = Çavkaniyên ketanê yên dirêjkirî nîşan bide
mode-and-colors = Awa û reng
center-segment = Beşa navîn
panel-missing = Rêkxistina destgehê wenda ye
.desc = Pelê rêkxistinê destgehê ji ber bikaranîna rêkxistin kesane yan jî xerabûna wê wenda ye.
.fix = Bo rewşa berdest ji nû ve saz bike
invalid-username = Navê bikarhêner nederbasdar e
mirroring = Dûbarekirin
.id = Dûbarekirin { $id }
.dont = Dûbarekirinê neke
.mirror = Dûbarekirin { $display }
.project =
Bişîne bo { $display ->
[all] hemû dîmenderan
*[other] { $display }
}
.project-count =
bo { $count } dîmenderên dinbişîne { $count ->
[1] dîmender
*[other] dîmender
}
orientation = Araste
.standard = Standard
.rotate-90 = 90° bizivirîne
.rotate-180 = 180° bizivirîne
.rotate-270 = 270° bizivirîne
auto = Xweber
end-segment = Beşa dawîn
bluetooth-adapters = Adaptorên Bluetoothê
time-format = Awaya dîrok û demê
.twenty-four = Dema 24-demjimêr
.show-seconds = Çirkeyan nîşan bide
.first = Roja yekem a hefteyê
.show-date = Dîrokê di sepanoka demê de nîşan bide
.friday = Înî
.saturday = Şemî
.sunday = Yekşem
.monday = Duşem
accent-color = Rengê sereke
type-key-combination = Kombînasyona kilîtan binivîse
keyboard-special-char = Têketina tîpên taybet
.alternate = Kilîta tîpên alternatîf
.compose = Kilîta taybet
.compose-desc = Kilîta compose mafê dide ku tîpên cûda werin nivîsandin. Ji bo bikaranînê, pêşî compose û paşê rêzek tîpan bitikîne. Bo mînak, kilîta compose li dû C û o wê © binivîse, dema ku kilîta compose li dû a û wê á binivîse.
.caps = Kilîta Caps Lock
manage-windows = Rêveberê çarçoveyan
.close = Çarçoveyê bigire
.maximize = Çarçoveyê mezin bike
.fullscreen = Çarçoveyê dîmendera tije
.minimize = Çarçoveyê biçûk bike
.resize-inwards = Çarçoveyê ber bi hundir veji nû ve mezinahiyê biguhêrîn
.resize-outwards = Çarçoveyê ber bi derve ve ji nû ve mezinahiyê biguhêrîne
.toggle-sticky = Çarçoveyê daliqandî biguhêrîne
never = Tu car
install-additional-languages = Zimanên vebijêrkî saz bike
sound-output = Derketin
.volume = Asta dengê deranê
.device = Amûra deranê
.level = Asta deranê
.config = Rêkxistin
.balance = Balans
.left = Çep
.right = Rast
window-controls = Kontrolên çarçoveyan
.maximize = Bişkoka mezinkirinê nîşan bide
.minimize = Bişkoka biçûkkirinê nîşan bide
.active-window-hint = Nîşana çarçoveya çalak nîşan bide
shadows-tiling = Çarçoveyên berhevkirî
.clip = Li gorî quncikên pergalê li hev bîne
.shadow = Sî bisepîne
window-management-appearance = Rêveberê çarçoveyê
.active-hint = Mezinahiya nîşana çarçoveyê çalak
.gaps = Valahiyên li derdora çarçoveyên berhevkirî
migrate-workspace-prev = Qada xebatê bibe derana paş
legacy-app-global-shortcuts = Kurteriyên gerdûnî di sepanên X11 de
.desc = Kurteriyên gerdûnî rê didin ku bûyerên kilîtdank û mişkê yên di sepanekê de, ji bo taybetmendiyên wekî push-to-talk an jî push-to-mute ji aliyê sepanên din ve werin naskirin. Bi awayê berdest, kurteriyên gerdûnî di sepanên X11 de hatine girtin da ku sepanên din nikaribin çalakiyên te yên hestiyar bişopînin.
.none = Tu kilît tune ne
.modifiers = Guherîner (Super, Shift, Control, Alt)
.combination = Hemû kilît dema ku guherînerên Super, Control an Alt tên pêlkirin
.all = Hemû kilît
.mouse = Bûyerên bişkoka mişkê di sepanên X11 de
activate = Çalak bike
power-saving = Vebijêrkên parastina hêzê
.turn-off-screen-after = Dîmenderê vemirîne piştî ...
.auto-suspend = Rawestanina xweber
.auto-suspend-ac = Dema bi keblê hêzê ve were girêdan xweber rawestîne
.auto-suspend-battery = Dema betarî kêm be xweber rawestîne
connected-devices = Amûrên girêdayî
.unknown = Amûra nenas
icons-and-toolkit = Rûkara îkon û amûran
sound-applications = Sepan
.desc = Dengê sepanan û sazkarî
time-zone = Herêma demê
.auto = Herêma dema xweber
.auto-info = Pêdiviya wê bi rajekarên cîh û gihîştina înternetê heye
dialog-add = Tevlî bike
fast = Bilez
add-color = Rengê tevlî bike
shadows-floating = Çarçoveyên herikbar
.clip = Quncikên pergalê bişopîne û sî bisepîne
network-device-state =
.activated = Hate girêdan
.config = Tê girêdan
.deactivating = Tê qutkirin
.disconnected = Qut bû
.failed = Girêdan têk çû
.ip-check = Girêdan tê kontrolkirin
.ip-config = IP û zanyariyên routing tê xwestin
.need-auth = Rastandin pêdivî ye
.prepare = Ji bo girêdanê tê amadekirin
.secondaries = Li benda girêdana duyemîn e
.unavailable = Ne pêkan e
.unknown = Rewşa nenas
.unmanaged = Nehatiye birêvebirin
.unplugged = Kebl nehatiye girêdan
command = Ferman
gestures = Tevger
.four-finger-down = Bi çar tiliyan ber bi jêr ve bikişîne
.four-finger-left = Bi çar tiliyan ber bi çepê ve bikişîne
.four-finger-right = Bi çar tiliyan ber bi rastê ve bikişîne
.four-finger-up = Bi çar tiliyan ber bi jor ve bikişîne
.three-finger-any = Bi sê tiliyan her alî bikişîne
system = Pergal & Jimar
no-networks = Tu tor nehatin dîtin.
open-new-folder = Peldanka nû veke
panel-bottom = Jêr
share = Torê parve bike
fill = Tije bike
custom = Kesane
bluetooth-paired = Amûrên berê hatine girêdan
.connect = Girê bide
.battery = { $percentage }% betarî
dialog = Gotûbêj
.title = Van sazkariyên dîmenderê bihêle?
.keep-changes = Guhertinan bihêle
.change-prompt = Guhertinên sazkariyan wê di { $time } çirek de xweber werin têkbirin.
.revert-settings = Sazkariyan têk bibe
sound-device-port-unplugged = Hatiye qutkirin
navigate = Gerandin
workspaces-overview-thumbnails = Wêneyên piçûk ên pêşdîtina qadê xebatê
.show-number = Jimara qadê xebatê nîşan bide
.show-name = Navê qadê xebatê nîşan bide
color = Reng
.depth = Kûrahiya rengê
.profile = Profîla rengê
.sidebar = Profîlên Rengê
.temperature = Germahiya rengê
hex = Hex
magnifier = Mezinker
.controls =
An van kurteriyan bi kar bîne: { $zoom_in ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_in } ji bo nêzîk bibe,
}{ $zoom_out ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_out } ji bo dûr bibe,
}
Super + bi mişka xwe bişemtîne
.scroll_controls = Bi Super + şemitandin, nêzîkbûnê bi mişk an touchpadê çalak bike
.show_overlay = Rûpûşa mezinkerê nîşan bide
.increment = Asta mezinkirinê
.signin = Di dema têketinê de mezinkerê bide destpêkirin
.applet = Mezinkerê li ser destgehê çalak bike/neke
.movement = Dîmena nêzîkirî livandinê nîşan dide
.continuous = Bi mayînde ligel nîşander
.onedge = Dema nîşander digihêje qeraxê
.centered = Ji bo ku nîşanderê li navîn bimîne
colors = Reng
administrator = Rêveber
.desc = Rêveber dikarin sazkariyan ji bo hemû bikarhêneran biguhêrînin, bikarhênerên din tevlî bikin an jî rakin
scan-to-connect-description = Ji bo girêdana bi vê torê ve, koda QRê bixwîne.
modified = { $count } hatiyê guhertin
workspaces-overview = Pêşdîtina qadên xebatê
.action-on-typing = Çalakî di dema nivîsandinê de
.none = Ne yek
.launcher = Destpêkerê veke
.applications = Sepanan veke
xdg-entry-touchpad = Touchpad
time-region = Herêm & Ziman
power = Hêz & Betarî
xdg-entry-wired = Têlkirî
xdg-entry-startup-apps-comment = Sepanên ku di dema têketinê de dixebitin bi rêk bixîne
xdg-entry-region-language = Herêm & Ziman
time-date = Dîrok & Dem
.auto = Xweber saz bike
.auto-ntp = Dîrok û dem wê dema herêma demê were sazkirin, xweber werin rojanekirin
xdg-entry-mouse-keywords = COSMIC;Mişk;Lezbûn;Şemitandin;
xdg-entry-bluetooth-comment = Amûrên Bluetoothê bi rê ve bibe
xdg-entry-notifications-keywords = COSMIC;Agahdarî;Kilîtkirin;
xdg-entry-startup-apps-keywords = COSMIC;Destpêk;Sepan;
xdg-entry-default-apps-comment = Geroka tevnê ya berdest, rajegirê enameyê, geroka pelê, û sepanên din
xdg-entry-keyboard-comment = Çavkaniyên ketanê, guhertin, ketanê tîpên taybet, kurterê
xdg-entry-wired-keywords = COSMIC;Têlkirî;LAN;Tor;Girêdan;
xdg-entry-power = Hêz & Betarî
xdg-entry-appearance-keywords = COSMIC;Reng;Îkon;Cûrenivîs;Rûkar
xdg-entry-mouse = Mişk
xdg-entry-dock-keywords = COSMIC;Bender;Destgeh;Sepanok
bluetooth = Bluetooth
.status = Dema sazkiryên Bluetoothê vekirî bin, ev pergal wekî { $aliases } xuya dibe.
.connected = Hate girêdan
.connecting = Tê girêdan
.disconnecting = Tê qutkirin
.connect = Girê bide
.disconnect = Qut bike
.forget = Ji bîr bike
.dbus-error = Di dema danûstandina bi DBus re şaşetiyek çê bû: { $why }
.disabled = Rajekara Bluetoothê neçalak e
.inactive = Rajekara Bluetoothê ne çalak e
.unknown = Rajekara Bluetoothê nehate çalakkirin. Gelo BlueZ hatiye sazkirin?
xdg-entry-mouse-comment = Leza mişkê, lezbûn û şemitandina xwezayî
xdg-entry-system = Pergal & Jimar
firmware = Firmware
xdg-entry-panel-keywords = COSMIC;Destgeh;Sepanok
xdg-entry-window-management = Rêveberê çarçoveyê
xdg-entry-a11y = Gihîştin
xdg-entry-time-language-comment = Dîroka pergalê, dem, herêm û ziman bi rê ve bibe
xdg-entry-desktop-keywords = COSMIC;Sermase;
default-apps = Sepanên berdest
.web-browser = Geroka tevnê
.file-manager = Rêveberê pelê
.mail-client = Rajigera enameyê
.music = Muzîk
.video = Vîdyo
.photos = Wêne
.calendar = Salname
.terminal = Termînal
.other-associations = Têkiliyên din
.text-editor = Edîtorê nivîsê
.not-installed = Ne sazkirî ye
xdg-entry-x11-applications-keywords = COSMIC;X11;Sepan;Lîstik;Lihevhatin;
xdg-entry-displays-comment = Sazkariyên rêkxistinê yên dîmenderê bi rê ve bibe
xdg-entry-touchpad-keywords = COSMIC;Touchpad;Tevger;
xdg-entry-time-language = Dem & Ziman
xdg-entry-users = Bikarhêner
window-management = Rêveberê çarçoveyê
xdg-entry-system-keywords = COSMIC;Pergal;Zanyarî;Jimar;Firmware;
xdg-entry-default-apps = Sepanên berdest
xdg-entry-wireless-keywords = COSMIC;WiFi;Wi-Fi;Tor;Girêdan;
xdg-entry-applications-comment = Sepanên kesane, sepanên destpêkê, û sazkariyên lihevhatina sepanên X11
mouse = Mişk
.speed = Leza mişkê
.acceleration = Lezgîniya mişkê çalak bike
dock = Bender
xdg-entry-about-keywords = COSMIC;Derbar
xdg-entry-panel = Destgeh
xdg-entry-notifications = Agahdarî
display = Dîmender
.arrangement = Bicihkirina dîmenderê
.arrangement-desc = Ji bo ji nû ve bicihkirina dîmenderan bikşîne
.enable = Dîmenderê çalak bike
.external = { $size } { $output } dîmenderê derveyî
.laptop = { $size } dîmenderê laptopê
.options = Vebijêrkên dîmenderê
.refresh-rate = Rêjeya nûkirinê
.resolution = Çareserî
.scale = Pîvan
.additional-scale-options = Vebijêrkên pîvanê yên din
xdg-entry-a11y-keywords = COSMIC;Gihîştin;A11y;Dîmender;Xwîner;Mezinker;Berevajî;Reng;
xdg-entry-wireless-comment = Girêdanên Wi-Fi û profîlên girêdanê
xdg-entry-desktop = Sermase
xdg-entry-date-time-keywords = COSMIC;Dem;Herêm;
xdg-entry-desktop-comment = Wêneya dîwêr, xuyang, destgeh, bender, rêveberê çarçoveyê û sazkairyên qada xebatê
xdg-entry-wallpaper = Wêneya dîwêr
xdg-entry-keyboard = Kilîtdank
xdg-entry-a11y-comment = Xwînerê dîmenderê, mezinker, berevajiya bilind, û parzûnên rengan
xdg-entry-displays = Dîmender
xdg-entry-network = Tor & Bêtêlî
sound = Deng
xdg-entry-date-time = Dîrok & Dem
notifications = Agahdarî
xdg-entry-users-comment = Rastandin û jimarên bikarhêner
startup-apps = Sepanên destpêkê
.add = Sepan tevlî bike
.user = Sepanên ku dema tu têdikevî pergala dixebitin
.none = Tu sepanên destpêkê nehatin tevlîkirin
.remove-dialog-title = { $name } rake?
.remove-dialog-description = Ev sepana destpêkê rake?
.add-startup-app = Sepana destpêkê tevlî bike
xdg-entry-input-comment = Sazkariyên kilîtdankê û mişkê
xdg-entry-bluetooth-keywords = COSMIC;Bluetooth;
about = Derbar
input-devices = Amûrên ketanê
xdg-entry-dock-comment = Bendeke vebijêrkî ji bo sepan û sepanokan
xdg-entry-network-keywords = COSMIC;Tor;Bêtêlî;WiFi;VPN;
xdg-entry-wallpaper-comment = Wêneyên dîwêr, reng û vebijêrkên pêşandana wêneyan
xdg-entry-dock = Bender
xdg-entry-startup-apps = Sepanên destpêkê
workspaces = Qadên xebatê
xdg-entry-wireless = Wi-Fi
xdg-entry-workspaces-keywords = COSMIC;Qada xebatê;Araste;Pêşdîtin;Dîmender;
xdg-entry-system-comment = Zanyariyên pergalê, jimar û rojanekirinên firmwareyê
xdg-entry-x11-applications-comment = Pîvandina sepanên pergala çarçoveya X11, dîmenderê sereke û kurteriyên gerdûnî
xdg-entry-region-language-comment = Li gorî herêma xwe dîrok, dem û jimaran saz bike
xdg-entry-wallpaper-keywords = COSMIC;Wêneya dîwêr;Paşrû;Pêşandan;
xdg-entry-users-keywords = COSMIC;Bikarhêner;Jimar;
xdg-entry-about-comment = Navê amûrê, zanyariyên reqalavê, sazkariyên kesane yên pergala xebitandinê
xdg-entry-vpn-keywords = COSMIC;VPN;Tor;Girêdan;OpenVPN;OpenConnect;
users = Bikarhêner
.admin = Rêveber
.standard = Standard
.profile-add = Wêneyê profîlê hilbijêre
xdg-entry-appearance-comment = Rengên taybet û rûkar
xdg-entry-time-language-keywords = COSMIC;Pergal;Dem;Dîrok;Herêm;Ziman;
xdg-entry-input-keywords = COSMIC;Ketan;Kilîtdank;Mişk;
xdg-entry-comment = Sepana sazkariyan ji bo sermaseya COSMIC
xdg-entry-notifications-comment = Sazkariyên rawestandina balkêşiyê, agahdariyên dîmendera kilîtê û sazkariyên taybetî yên sepanan
xdg-entry-applications = Sepan
xdg-entry-sound-keywords = COSMIC;Deng;Deng;Hişyarî;Pipewire;
xdg-entry-power-keywords = COSMIC;Hêz;Betarî
legacy-applications = Lihevhatina sepanên X11
xdg-entry-default-apps-keywords = COSMIC;Berdest;Sepan
xdg-entry-keywords = COSMIC;Sazkarî;
xdg-entry-region-language-keywords = COSMIC;Herêm;Ziman;Dîrok;Awa;Dem;Sazkariyên herêmî;Herêmîkirin;
xdg-entry-about = Derbar
xdg-entry-input = Amûrên ketanê
xdg-entry-applications-keywords = COSMIC;Berdest;Sepan;Destpêk;X11;Lihevhatin
xdg-entry-sound-comment = Sazkariyên dengê yên ji bo amûr, hişyarî û sepanan
wallpaper = Wêneya dîwêr
.change = Wêneyê biguhêrîne her
.fit = Lihevhatîna wêneyê
.folder-dialog = Peldanka wêneyên dîwêr hilbijêre
.image-dialog = Wêneya dîwêr hilbijêre
.plural = Wêneyên dîwêr
.same = Li ser hemû dîmenderan heman wêneya dîwêr
.slide = Pêşandana wêneyan
xdg-entry-touchpad-comment = Leza touchpadê, vebijêrkên tikandinê û tevger
xdg-entry-keyboard-keywords = COSMIC;Kilîtdank;Ketan;Çavkanî;Kurterê;
xdg-entry-vpn-comment = Girêdanên VPN û profîlên girêdanê
keyboard = Kilîtdank
xdg-entry-appearance = Xuyang
xdg-entry-window-management-keywords = COSMIC;Çarçove;Rêveberî;Berhevkirin;Super;Kilît;
xdg-entry-workspaces-comment = Arasteya qada xebatê, pêşdîtin û tevgerên pir-dîmender
xdg-entry-network-comment = Girêdanên torê bi rê ve bibe
xdg-entry-panel-comment = Benda pergalê sereke ya ji bo menu û sepanokan
touchpad = Touchpad
.acceleration = Lezgîniya touchpadê çalak bike
.speed = Leza touchpadê
xdg-entry-sound = Deng
xdg-entry-power-comment = Awayên hêzê û vebijêrkên parastina hêzê
xdg-entry-displays-keywords = COSMIC;Dîmender;
time = Dem & ziman
xdg-entry-workspaces = Qadên xebatê
xdg-entry-date-time-comment = Herêma demê, sazkariyên demjimêra xweber û awaya demê
xdg-entry-x11-applications = Lihevhatina sepanên X11
xdg-entry-window-management-comment = Çalakiya bişkoka Super, vebijêrkên kontrolkirina çarçoveyan û vebijêrkên din ên berhevkirina çarçoveyan
xdg-entry-wired-comment = Girêdanên têlkirî û profîlên girêdanê
app = Sazkariyên COSMIC
dbus-connection-error = Girêdana bi DBus re têk çû
known-networks = Torên naskirî
connecting = Tê girêdan…
disconnect = Qut bike
ok = Baş e
number = { $number }
add-vpn = VPNê tevlî bike
airplane-on = Awaya balafirê vekirî ye.
forget = Ji bîr bike
add-network = Torê tevlî bike
.profile = Profîlê tevlî bike
vpn-error = Şaşetiya VPN
.config = Tevlîkirina rêkxistinê VPNê têk çû
.connect = Girêdana bi VPNê re têk çû
.connection-editor = Edîtorê girêdanê têk çû
.connection-settings = Sazkariyên ji bo girêdanên çalak nehatin standin
.openvpn-config-path = Rêgeha pelê OpenVPNê nederbasdar e
.openvpn-config-path-desc = Pela hilbijartî divê li ser pergala pelan a xwecihî be.
.updating-state = Rewşa rêveberiyê torê nehat rojanekirin
.wireguard-config-path = Rêgeha pelê WireGuard nederbasdar e
.wireguard-config-path-desc = Pela hilbijartî divê li ser pergala pelan a xwecihî be.
.wireguard-device = Amûra WireGuard nehate afirandin
.with-password =
VPN nehate sazkirin { $field ->
*[username] navê bikarhêner
[password] borînpeyv
[password-flags] alên-borînpeyvê
} bi nmcli re
bluetooth-display-passkey = Cotkirina Bluetoothê
.description = Tika ye kilîta borandinê ya jêrîn li ser { $device } binivîse, paşê Enterê bitikîne
bluetooth-display-pin = Cotkirina Bluetoothê
.description = Tika ye PINa jêrîn li ser { $device } binivîse, paşê Enterê bitikîne
workspaces-navigation = Gerandin
.wraparound = Di navbera yekem û dawîn qadê xebatê de digere bi riya bikaranîna kurteriyên kilîtdankê û tevgeran
no-search-results = Tu tor li lêgerînên te li hev nakin .

View file

@ -764,3 +764,20 @@ xdg-entry-workspaces = 작업 공간
xdg-entry-date-time-comment = 시간대와 자동 시계 설정 및 시간 형식
xdg-entry-x11-applications = X11 앱 호환성
xdg-entry-wired-comment = 유선 연결 및 연결 프로필
xdg-entry-touchpad = 터치패드
xdg-entry-startup-apps-comment = 로그인 시 실행할 앱 설정
xdg-entry-region-language = 지역 & 언어
xdg-entry-startup-apps-keywords = COSMIC;시작;앱;
xdg-entry-system = 시스템 & 계정
xdg-entry-time-language-comment = 시스템 날짜와 시간, 지역 및 언어 관리
xdg-entry-touchpad-keywords = COSMIC;터치패드;제스처;
xdg-entry-time-language = 시간 & 언어
xdg-entry-users = 사용자
xdg-entry-system-keywords = COSMIC;시스템;정보;계정;펌웨어;
xdg-entry-users-comment = 인증 및 사용자 계정
xdg-entry-startup-apps = 시작 앱
xdg-entry-system-comment = 시스템 정보와 계정 및 펌웨어 업데이트
xdg-entry-region-language-comment = 지역에 기반한 날짜와 시간 및 숫자 형식
xdg-entry-time-language-keywords = COSMIC;시스템;시간;날짜;지역;언어;
xdg-entry-sound-keywords = COSMIC;소리;오디오;알림;Pipewire;
xdg-entry-sound = 소리

0
i18n/lo/cosmic_settings.ftl vendored Normal file
View file

View file

@ -1,9 +1,9 @@
time-region = Regionas ir kalba
panel-left = Kairė
super-key = Super klavišo veiksmas
.launcher = Atidaryti aplikacijų paleidėją
.launcher = Atverti programų leistuką
.workspaces = Atidaryti darbo sritis
.applications = Atidaryti aplikaciją
.applications = Atidaryti programą
.disable = Neaktyvus
x-minutes =
{ $number } { $number ->
@ -28,8 +28,8 @@ save = Išsaugoti
vpn-error = VPN klaida
.config = Nepavyko pridėti VPN konfigūracijos
.connect = Nepavyko prisijungti prie VPN
.connection-editor = Prisijungimo redaguoklės problema
.connection-settings = Nepavyko gauti aktyvaus prisijungimo nustatymų
.connection-editor = Ryšio rengyklės triktis
.connection-settings = Nepavyko gauti veikiančių ryšių nuostatų
.updating-state = Nepavyko atnaujinti tinklo valdytojo būsenos
.wireguard-config-path = Klaidingas WireGuard nustatymų failo kelio vardas
.wireguard-config-path-desc = Pasirinktas failas turi būti lokalioje failų sistemoje.
@ -46,7 +46,7 @@ no-applets-found = Apletų nerasta...
region = Regionas
zoom-in = Priartinti
place-here = Padėti apletą čia
-requires-restart = Reikalingas paleidimas iš naujo
-requires-restart = Reikia paleisti iš naujo
workspaces-orientation = Darbo sričių orientacija
.vertical = Vertikali
.horizontal = Horizontali
@ -55,7 +55,7 @@ all-displays = Visi ekranai
scheduling = Planavimas
.manual = Rankininis planavimas
frosted = Apšalusio stiklo efektas sistemos komponentuose
.desc = Taikomas fono suliejimas panelėje, aplikacijos doke, apletuose, programų paleidyklėje ir aplikacijų bibliotekoje
.desc = Taikomas fono suliejimas skydelyje, doke, apletuose, programų paleidyklėje ir programų bibliotekoje
password = Slaptažodis
about-hardware = Aparatinė įranga
.model = Modelis
@ -66,9 +66,9 @@ about-hardware = Aparatinė įranga
short = Trumpas
vpn = VPN
.connections = VPN prisijungimai
.error = Nepavyko pridėti VPN nustatymų
.error = Nepavyko pridėti VPN nuostatų
.remove = Pašalinti prisijungimo profilį
.select-file = Pasirinkti VPN konfigūraci failą
.select-file = Pasirinkti VPN konfigūracijos failą
power-mode = Maitinimo režimas
.battery = Ilgesnis baterijos veikimo laikas
.battery-desc = Mažesnis energijos suvartojimas ir tylus veikimas
@ -83,7 +83,7 @@ wired = Laidinis
.devices = Laidiniai įrenginiai
.remove = Pašalinti ryšio profilį
bluetooth-available = Įrenginiai netoliese
cable-unplugged = Kabelis neprijungtas
cable-unplugged = Kabelis atjungtas
color-picker = Spalvų pipetė
interface-font = Sistemos šriftas
forget-dialog = Pamirštį šį Wi-Fi tinklą?
@ -96,19 +96,19 @@ hot-corner = Aktyvusis Kampas (Hot Corner)
keyboard-typing-assist = Rašymas
.repeat-rate = Pakartojimo dažnis
.repeat-delay = Pakartojimo uždelsimas
edge-gravity = Slankių langų trauka prie artimiausių kraštų
edge-gravity = Slankūs langai traukiami prie artimiausių kraštų
bluetooth = Bluetooth
.status = Jūsų įrenginys matomas kaip { $aliases }, kol Bluetooth nustatymai bus įjungti.
.status = Jūsų įrenginys matomas kaip { $aliases }, kol Bluetooth nuostatos bus įjungtos.
.connected = Prisijungta
.connecting = Jungiamasi
.disconnecting = Atsijungiama
.connect = Prisijungti
.disconnect = Atsijungti
.forget = Pamiršti
.dbus-error = Įvyko klaida procese su DBus: { $why }
.disabled = Bluetooth funkcionalumas išjungtas
.inactive = Bluetooth functionalumas neaktyvus
.unknown = Bluetooth functionalumas negali būti aktyvuotas. Ar jūsų įrenginys įdiegtas BlueZ paketas?
.dbus-error = Klaida veikiant su DBus: { $why }
.disabled = Bluetooth tarnyba išjungta
.inactive = Bluetooth tarnyba neaktyvi
.unknown = Nepavyksta altyvuoti Bluetooth tarnybos. Ar jūsų įrenginyje įdiegtas BlueZ paketas?
amplification = Stiprinimas
.desc = Leidžia padidinti garsumą iki 150%
recent-colors = Pastarosios spalvos
@ -119,7 +119,7 @@ about-os = Operacinė sistema
.desktop-environment = Darbalaukio aplinka
.windowing-system = Langų valdymo sistema
panel-behavior-and-position = Elgsena ir pozicija
.autohide = Automatiškai slėpti panelę
.autohide = Automatiškai slėpti skydelį
.dock-autohide = Automatiškai slėpti doką
.position = Pozicija ekrane
.display = Rodyti ekrane
@ -137,9 +137,9 @@ accessibility = Prieinamumas
.color-filters = Spalvų filtrai
firmware = Programinė aparatinė įranga
container-background = Įrankių fono spalva
.desc-detail = Įrankių fono spalva naudojama navigavimo šonjuostėse, šoniniame stalčiuje, iššokančiuose languose ir panašiuose komponentuose. Pagal numatytus nustatymus, ši fono spalva automatiškai parenkama pagal lango fono spalvą.
.desc-detail = Įrankių fono spalva naudojama naršymo šonjuostėse, šoniniame meniu, iššokančiuose languose ir panašiuose komponentuose. Pagal numatytus nustatymus, ši fono spalva automatiškai parenkama pagal lango fono spalvą.
.reset = Atstatyti į automatinį
.desc = Naudojama navigavimo šonjuostėse, šoniniame stalčiuje, iššokančiuose languose ir panašiuose komponentuose
.desc = Naudojama naršymo šonjuostėse, šoniniame meniu, iššokančiuose languose ir panašiuose komponentuose
primary-button = Pagrindiniai mygtukai
.desc = Nustato fizinių mygtukų tvarką
.left = Kairė
@ -148,7 +148,7 @@ sound-alerts = Įspėjimai
.volume = Įspėjimo garsumas
.sound = Įspėjimo garsas
icon-theme = Piktogramų tema
.desc = Skirtingų piktogramų rinkinių taikymas aplikacijoms
.desc = Skirtingų piktogramų rinkinių taikymas programoms
import = Importuoti
keyboard-shortcuts = Klaviatūros spartieji klavišai
.desc = Peržiūrėti ir modifikuoti sparčiuosius klavišus
@ -180,7 +180,7 @@ sound-input = Įvestis
add-another-keybinding = Pridėti kitą klavišų kombinacija
copy-to-clipboard = Kopijuoti į iškarpinę
monospace-font = Lygiaplotis šriftas
experimental-settings = Eksperimentiniai nustatymai
experimental-settings = Eksperimentinės nuostatos
recent-folders = Pastarieji aplankai
color-filter = Spalvų filtrų tipai
.unknown = Aktyvus nežinomas filtras
@ -234,10 +234,10 @@ x-hours =
}
added = Pridėta
input-source-switch = Perjungti klaviatūros kalbos įvesties šaltinį
focus-navigation = Navigacija pagal fokusą
.focus-follows-cursor = Fokusas seka žymeklį
.focus-follows-cursor-delay = Fokusas seka žymeklį su atidėjimu (mikrosekundės)
.cursor-follows-focus = Žymeklis seka fokusą
focus-navigation = Aktyvaus lango vairavimas
.focus-follows-cursor = Aktyvuoti langą po žymekliu
.focus-follows-cursor-delay = Delsa ms kol langas po žymekliu bus aktyvuotas
.cursor-follows-focus = Perkelti žymeklį į aktyvų langą
full-name = Vardas ir Pavardė
app-background = Lango fonas
panel-right = Dešinė
@ -283,19 +283,19 @@ preferred-languages = Pageidaujamos kalbos
.desc = Kalbų tvarka nulemia, kuri kalba bus naudojama naudotojo sąsajoje. Pakeitimai įsigalios po kito prisijungimo.
display = Monitoriai
.arrangement = Monitorių išdėstyimas
.arrangement-desc = Pertvarkite monitorius velkant juos
.arrangement-desc = Perdėstykite monitorius juos vilkdami
.enable = Įjungti monitorių
.external = { $size } { $output } išorinis monitorius
.laptop = { $size } nešiojamo kompiuterio monitorius
.options = Monitoriaus parinktys
.refresh-rate = Atnaujinimo dažnis
.resolution = Rezoliucija
.resolution = Raiška
.scale = Mastelis
.additional-scale-options = Papildomos mastelio parinktys
panel-style = Stilius
.anchor-gap = Tarpas tarp panelės ir ekrano krašto
.anchor-gap = Tarpas tarp skydelio ir ekrano krašto
.dock-anchor-gap = Tarpas tarp doko ir ekrano krašto
.extend = Ištęsti panelę iki ekrano kraštų
.extend = Ištęsti skydelį iki ekrano kraštų
.dock-extend = Ištęsti doką iki ekrano kraštų
.appearance = Išvaizda
.size = Dydis
@ -305,35 +305,35 @@ panel-appearance = Išvaizda
.match = Pagal darbalaukį
.light = Šviesi
.dark = Tamsi
workspaces-multi-behavior = Elgsena su keletais monitoriais
workspaces-multi-behavior = Darbas su keliais monitoriais
.span = Darbo sritis tęsiasi per visus monitorius
.separate = Kiekvienas monitorius turi savo atskirą darbo sritį
.separate = Kiekvieno monitoriaus darbo sritis atskira
replace-shortcut-dialog = Pakeisti spartųjį klavišą?
.desc = { $shortcut } naudoja { $name }. Jeigu norite pakeisti, { $name } bus išjungtas.
rgb = RŽM
about-device = Įrenginio vardas
.desc = Šis pavadinimas rodomas kitiems tinklo arba „Bluetooth“ įrenginiams
no-vpn = VPN ryšys nėra prieinamas.
no-vpn = Nėra prieinamų VPN ryšių
start-segment = Pradžios segmentas
desktop = Darbalaukis
identity = Tapatybė
reset-to-default = Atstatyti į numatytas reikšmes
disconnect = Atjungti
ok = Gerai
app = COSMIC Settings
app = COSMIC Nuostatos
debug = Derinti
sound = Garsas
enable-export = Taikyti dabartinę temą GNOME aplikacijose
.desc = Ne visi programiniai rinkiniai palaiko automatinį perjungimą. Po temos pakeitimo , ne COSMIC tipo aplikacijoms gali prireikti paleidimo iš naujo.
.desc = Ne visi programiniai rinkiniai palaiko automatinį perjungimą. Pakeitus temą , gali reikėti iš naujo paleisti ne COSMIC tipo programas..
number = { $number }
shortcut-name = Sparčių klavišų derinio pavadinimas
style = Stilius
.round = Apvalus
.slightly-round = Lengvai apvalus
.slightly-round = Truputį suapvalintas
.square = Kampuotas
panel-applets = Konfigūracija
.dock-desc = Konfigūruoti doko apletus
.desc = Konfigūruoti panelės apletus
.desc = Konfigūruoti skydelio apletus
qr-code-unavailable = QR kodas negalimas
small = Mažas
notifications = Pranešimai
@ -368,8 +368,8 @@ legacy-app-scaling = X11 langų sistemos programų mastelis
.preferred-display = Pageidaujamas žaidimų ir viso ekrano X11 programų ekranas
.no-display = Joks
night-light = Nakties šviesa
.auto = Automati (nuo saulėlydžio iki saulėtekio)
.desc = Sumažinta mėlyna šviesa ir padidinamas spalvų šiltumas
.auto = Automatiškai (nuo saulėlydžio iki saulėtekio)
.desc = Mažiau mėlynos šviesos ir šiltesnės spalvos
about = Apie
input-devices = Įvesties įrenginiai
custom-shortcuts = Pasirinktiniai sparčiųjų klavišų deriniai
@ -383,11 +383,11 @@ window-hint-accent-toggle = Naudoti temos akcento spalvą, kaip aktyvaus lango
type-to-search = Norint ieškoti, pradėkite rašyti…
workspaces-behavior = Darbo sričių elgsena
.dynamic = Dinamiškos darbo sritys
.dynamic-desc = Automatiškai pašalinamos tuščios darbo sritys
.dynamic-desc = Tuščios darbo sritys pašalinamos automatiškai
.fixed = Pastovus darbo sričių skaičius
.fixed-desc = Darbo sričių pridėjimas arba pašalinamas darbo sričių apžvalgos režime.
.fixed-desc = Peržiūrioje pridėkite arba pašalinkite darbo sritis.
wireguard-dialog = Pridėti WireGuard įrenginį
.description = Pasirinkite įrenginio vardą WireGuard nustatymams.
.description = Pasirinkite įrenginio pavadinimą WireGuard nuostatoms.
remove-connection-dialog = Pašalinti prisijungimo profilį
.vpn-description = Jums reikės įvesti slaptažodį iš naujo, kad galėtumėte naudotis šiuo tinklu ateityje.
.wired-description = Jums reikės atkurti šį profilį, kad galėtumėte naudotis šiuo prisijungimu.
@ -433,10 +433,10 @@ applications = Aplikacijos
dbus-connection-error = Nepavyko prisijungti prie DBus
system-controls = Sistemos valdymas
auto-switch = Automatinis šviesaus ir tamsaus režimo valdymas
.sunrise = Įjungiamas šviesus režimas tekant saulei
.sunset = Įjungiamas tamsus režimas saulei leidžiantis
.next-sunrise = Įjungiamas šviesus režimas kitą kartą saulei tekant
.next-sunset = Įjungiamas tamsus režimas kitą kartą saulei leidžiantis
.sunrise = Šviesus režimas įjungiamas tekant saulei
.sunset = Tamsus režimas įjungiamas saulei leidžiantis
.next-sunrise = Šviesus režimas įjungiamas kitą kartą saulei tekant
.next-sunset = Tamsus režimas įjungiamas kitą kartą saulei leidžiantis
add-vpn = Pridėti VPN
cancel = Atšaukti
keyboard-numlock-boot = Numlock
@ -456,9 +456,9 @@ interface-density = Sąsajos tankumas
open-application-library = Atidaryti Aplikacijų Sąrašą
connected = Prisijungta
slow = Lėtas
network-name = Tinklo Pavadinimas
network-name = Tinklo pavadinimas
large = Didelis
panel = Panelė
panel = Skydelis
scrolling = Slinkimas
.two-finger = Slinkimas dviem pirštais
.edge = Vieno piršto slinkimas palei kraštą
@ -492,8 +492,8 @@ users = Naudotojai
forget = Pamiršti
center-segment = Centrinis segmentas
panel-missing = Panelė nesukonfigūruota
.desc = Panelės konfigūraciniai failai nerasti dėl netinkamo jų modifikavimo arba tas failas buvo sugadintas.
.fix = Nustatyti į numatytus nustatymus
.desc = Skydelio konfigūracijos failas nerastas nes buvo netinkamai sukonfigūruotas arba sugadintas.
.fix = Grąžinti numatytas nuostatas
invalid-username = Netinkamas naudotojo vardas
mirroring = Veidrodinis atvaizdavimas
.id = Atvaizduojamas { $id }
@ -539,7 +539,7 @@ manage-windows = Valdyti langus
.toggle-sticky = Įjungti/išjungti lipnų langą
never = Niekada
install-additional-languages = Pridėti papildomas kalbas
settings = Nustatymai
settings = Nuostatos
sound-output = Išvestis
.volume = Išvesties garsumas
.device = Išvesties įrenginys
@ -551,7 +551,7 @@ sound-output = Išvestis
wallpaper = Darbalaukio fonas
.change = Keisti paveiksliuką kas
.fit = Darbalaukio fono talpinimas ekrane
.folder-dialog = Pasirinkite darbalaukio fono aplankalą
.folder-dialog = Pasirinkite darbalaukio fonų aplanką
.image-dialog = Pasirinkite darbalaukio foną
.plural = Darbalaukio fonai
.same = Kiekvienam ekrane toks pat fonas
@ -573,15 +573,15 @@ legacy-app-global-shortcuts = Sparčiųjų klavišų kombinacijos X11 programose
.all = Visi klavišai
.mouse = Peles mygtuko veiksmai X11 programose
activate = Aktyvuoti
replace = Pakeisti
replace = Keisti
power-saving = Energijos taupymo parinktys
.turn-off-screen-after = Išjunkti ekraną po
.turn-off-screen-after = Išjungti ekraną po
.auto-suspend = Automatinis miego režimas
.auto-suspend-ac = Automatinis miego režimas, kai prijungtas maitinimas
.auto-suspend-battery = Automatinis miego režimas, kai naudojama baterija
connected-devices = Prijungti įrenginiai
.unknown = Nežinomas įrenginys
icons-and-toolkit = Ikonų ir įrankių rinkinio temos
icons-and-toolkit = Piktogramų ir įrankių stiliai
sound-applications = Aplikacijos
.desc = Aplikacijų garsumas ir nustatymai
time-zone = Laiko juosta
@ -599,10 +599,10 @@ network-device-state =
.disconnected = Atsijungta
.failed = Nepavyko prisijungti
.ip-check = Tikrinamas ryšys
.ip-config = Vykdoma IP adreso and maršrutizavimo informacijos užklausa
.need-auth = Reikalinga autentifikacija
.ip-config = Užklausiama IP adreso ir maršrutizavimo informacijos
.need-auth = Reikja patvirtinti tapatumą
.prepare = Ruošiamasi prisijungti
.secondaries = Laukiama antrinio ryšio
.secondaries = Laukiama prisijungiant antrąkart
.unavailable = Nepasiekiama
.unknown = Nežinoma būsena
.unmanaged = Nevaldomas
@ -618,10 +618,10 @@ gestures = Gestai
.four-finger-up = Keturių pirštų braukimas aukštyn
.three-finger-any = Trijų pirštų braukimas
system = Sistema ir paskyros
no-networks = Nebuvo rasta tinklų.
open-new-folder = Atidaryti naują aplanką
no-networks = Tinklų nerasta
open-new-folder = Atverti naują aplanką
panel-bottom = Apačia
share = Pasidalinti tinklu
share = Dalintis tinklu
add = Pridėti
time = Laikas ir kalba
fill = Užpildyti
@ -635,7 +635,7 @@ dialog = Dialogas
.change-prompt = Nustatymai bus automatiškai atkurti po { $time } sekundžių.
.revert-settings = Atkurti nustatymus
navigate = Naviguoti
workspaces-overview-thumbnails = Darbo sričių apžvalgos režimo miniatiūros
workspaces-overview-thumbnails = Darbo sričių peržiūros miniatiūros
.show-number = Rodyti darbo srities numerį
.show-name = Rodyti darbo srities vardą
color = Spalvos
@ -646,33 +646,33 @@ color = Spalvos
hex = Šešioliktainis
magnifier = Didinamasis stiklas
.controls =
Arba pasinaudokite sparčiaisiais klavišais: { $zoom_in ->
Arba naudokite sparčiaisius klavišus: { $zoom_in ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_in } priartinimui,
{ $zoom_in } priartinti,
}{ $zoom_out ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_out } atitolinimui,
{ $zoom_out } tolinti,
}
Super + slinkimas su pele
.scroll_controls = Įgalinkite pelės ar jutiklinio pulto vaizdo mastelio keitimą su Super + slinkimas
.show_overlay = Rodyti didinamojo stiklo perdangą
.increment = Artinimo dydis
.signin = Įjungti didinamąjį stiklą prisijungimo ekrane
.applet = Didinamojo stiklo jungiklio apletas panelėje
.movement = Priartinto vaizdo judėjimas
.continuous = Nuolatos su pelės žymekliu
.applet = Didinamojo stiklo jungiklio apletas skydelyje
.movement = Priartinto vaizdo judinimas
.continuous = Nuolat su pelės žymekliu
.onedge = Kai žymeklis pasiekia ekrano ribą
.centered = Laikyti žymeklį centre
colors = Spalvos
administrator = Administratorius
.desc = Administratorius gali keisti nustatymus visiems naudotojams, pridėti ir pašalinti kitus naudotojus
scan-to-connect-description = Norinti prisijungti prie šio tinklo, skenuokite QR kodą.
scan-to-connect-description = Norėdami prisijungti prie šio tinklo, skenuokite QR kodą.
modified = Pakeista: { $count }
known-networks = Žinomi tinklai
known-networks = Įsiminti tinklai
network-and-wireless = Tinklas ir belaidis ryšys
sound-device-port-unplugged = Neprijungta
sound-hd-audio = HD garsas
@ -719,98 +719,98 @@ shadows-floating = Slankieji langai
workspaces-overview = Darbo sričių apžvalga
.action-on-typing = Veiksmas rašant
.none = Jokia
.launcher = Atidaryti Aplikacijų leidyklę
.applications = Atidaryti Aplikacijas
.launcher = Atverti Leistuką
.applications = Atidaryti Programas
connect = Prisijungti
light = Šviesi
dark = Tamsus
xdg-entry-touchpad = Jutiklinis kilimėlis
xdg-entry-wired = Laidinis
xdg-entry-startup-apps-comment = Konfigūruoti aplikacijas paleidžiamas prisijungus
xdg-entry-startup-apps-comment = Konfigūruoti programas paleidžiamas prisijungus
xdg-entry-region-language = Regionas ir Kalba
xdg-entry-mouse-keywords = COSMIC;Pelė;Pagreitis;Slinkimas;
xdg-entry-bluetooth-comment = Valdyti Bluetooth įrenginius
xdg-entry-notifications-keywords = COSMIC;Pranešimai;Užrakinimas;
xdg-entry-startup-apps-keywords = COSMIC;Paleistis;Aplikacija;
xdg-entry-default-apps-comment = Numatytoji naršyklė, pašto programa, failų naršyklė ir kitos aplikacijos
xdg-entry-keyboard-comment = Įvesties šaltiniai, perjungimas, specialių ženklų įvestis, spartieji klavišai
xdg-entry-wired-keywords = COSMIC;Laidinis;LAN;Tinkas;Ryšys;
xdg-entry-notifications-keywords = COSMIC;Pranešimai;Užraktas;
xdg-entry-startup-apps-keywords = COSMIC;Paleistis;Programa;
xdg-entry-default-apps-comment = Numatytoji naršyklė, pašto programa, failų naršyklė ir kitos programos
xdg-entry-keyboard-comment = Įvesties įtaisai, perjungimas, specialių ženklų įvestis, spartieji klavišai
xdg-entry-wired-keywords = COSMIC;Laidinis;LAN;Tinklas;Ryšys;
xdg-entry-power = Energija ir Baterija
xdg-entry-mouse = Pelė
xdg-entry-dock-keywords = COSMIC;Dokas;Panelė;Apletas
xdg-entry-dock-keywords = COSMIC;Dokas;Skydelis;Apletas
xdg-entry-mouse-comment = Pelės greitis, pagreitis ir natūralus slinkimas
xdg-entry-system = Sistema ir Paskyros
xdg-entry-panel-keywords = COSMIC;Panelė;Apletas
xdg-entry-panel-keywords = COSMIC;Skydelis;Apletas
xdg-entry-window-management = Langų Valdymas
xdg-entry-a11y = Prieinamumas
xdg-entry-time-language-comment = Valdyti sistemos datą, laiką, regioną ir kalbą
xdg-entry-time-language-comment = Tvarkyti sistemos datą, laiką, regioną ir kalbą
xdg-entry-desktop-keywords = COSMIC;Darbalaukis;
xdg-entry-x11-applications-keywords = COSMIC;X11;Aplikacijos;Žaidimas;Suderinamumas;
xdg-entry-displays-comment = Valdyti ekrano konfigūracijos nustatymus
xdg-entry-x11-applications-keywords = COSMIC;X11;Programa;Žaidimas;Suderinamumas;
xdg-entry-displays-comment = Tvarkyti ekrano nuostatas
xdg-entry-touchpad-keywords = COSMIC;Jutiklinis kilimėlis; Gestai;
xdg-entry-time-language = Laikas ir Kalba
xdg-entry-users = Naudotojai
xdg-entry-system-keywords = COSMIC;Sistema;Informacija;Paskyros;Programinė aparatinė įranga;
xdg-entry-default-apps = Numatytosios Aplikacijos
xdg-entry-default-apps = Numatytosios programos
xdg-entry-wireless-keywords = COSMIC;WiFi-Wi-Fi;Tinklas;Ryšys;
xdg-entry-applications-comment = Numatytosios aplikacijos, pradžios aplikacijos ir X11 aplikacijų suderinamumo nustatymai
xdg-entry-applications-comment = Numatytosios programos, pradžios programos ir X11 programų suderinamumo nuostatos
xdg-entry-about-keywords = COSMIC;Apie
xdg-entry-panel = Panelė
xdg-entry-panel = Skydelis
xdg-entry-notifications = Pranešimai
xdg-entry-a11y-keywords = COSMIC;Prieinamumas;A11y;Ekranas;Skaitytuvas;Didinamasis stiklas;Kontrastas;Spalva;
xdg-entry-wireless-comment = Wi-Fi ryšiai ir ryšių profiliai
xdg-entry-desktop = Darbalaukis
xdg-entry-date-time-keywords = COSMIC;Laikas;Zona;
xdg-entry-desktop-comment = Darbalaukio fonas, išvaizda, panelė, dokas, langų valdymo ir darbo sričių nustatymai
xdg-entry-desktop-comment = Darbalaukio fonas, išvaizda, skydelis, dokas, langų valdymo ir darbo sričių nuostatos
xdg-entry-wallpaper = Darbalaukio fonas
xdg-entry-keyboard = Klaviatūra
xdg-entry-a11y-comment = Ekrano Skaitytuvas, didinamasis stiklas, didelis kontrastas ir spalvų filtrai
xdg-entry-displays = Ekranai
xdg-entry-network = Tinklas ir Belaidis ryšys
xdg-entry-date-time = Data ir Laikas
xdg-entry-users-comment = Autentifikacija ir naudotojų paskyros
xdg-entry-input-comment = Klaviatūros ir pelės nustatymai
xdg-entry-users-comment = Naudotojų tapatumo patvirtinimas ir paskyros
xdg-entry-input-comment = Klaviatūros ir pelės nuostatos
xdg-entry-bluetooth-keywords = COSMIC;Bluetooth;
xdg-entry-dock-comment = Pasirinktina juosta aplikacijomis ir apletams
xdg-entry-dock-comment = Pasirinktinė juosta programoms ir apletams
xdg-entry-network-keywords = COSMIC;Tinklas;Bevielis ryšys;WiFi;VPN;
xdg-entry-wallpaper-comment = Darbalaukio paveikslėlis, spalvos ir skaidrių parinktys
xdg-entry-dock = Dokas
xdg-entry-startup-apps = Pradžios Aplikacijos
xdg-entry-startup-apps = Paleisties programos
xdg-entry-wireless = Wi-Fi
xdg-entry-workspaces-keywords = COSMIC;Darbo sritis;Orientacija;Apžvalga;Monitorius;
xdg-entry-system-comment = Sistemos informacija, paskyros ir programinės aparatinės įrangos atnaujinimai
xdg-entry-x11-applications-comment = X11 langų sistemos aplikacijų mastelis, pagrindinis ekranas ir sistemos greitieji klavišai
xdg-entry-region-language-comment = Datų, laiko ir skaičių formatas pagal nustatytą regioną
xdg-entry-system-comment = Sistemos informacija, paskyros ir programinės aparatinės įrangos naujiniai
xdg-entry-x11-applications-comment = X11 langų sistemos programų mastelis, pagrindinis ekranas ir sistemos greitieji klavišai
xdg-entry-region-language-comment = Datų, laiko ir skaičių formatas pagal pasirinktą regioną
xdg-entry-wallpaper-keywords = COSMIC;Darbalaukio fonas;Fonas;Skaidrės;
xdg-entry-users-keywords = COSMIC;Naudotojas;Paskyra;
xdg-entry-about-comment = Įrenginio pavadinimas, techninė informacija, operacinės sistemos numatytieji nustatymai
xdg-entry-about-comment = Įrenginio pavadinimas, techninė informacija, operacinės sistemos numatytosios nuostatos
xdg-entry-vpn-keywords = COSMIC;VPN;Ryšys;Prisijungimai;OpenVPN;OpenConnect;
xdg-entry-time-language-keywords = COSMIC;Sistema;Laikas;Data;Regionas;Kalba;
xdg-entry-input-keywords = COSMIC;Įvestis;Klaviatūra;Pelė;Pelytė;
xdg-entry-comment = COSMIC desktop aplikacijų nustatymai
xdg-entry-notifications-comment = Netrukdyti režimas, užrakinto ekrano pranešimai ir programų nustatymai
xdg-entry-applications = Aplikacijos
xdg-entry-notifications-comment = Režimas „Netrukdyti“, užrakinto ekrano pranešimai ir programų nuostatos
xdg-entry-applications = Programos
xdg-entry-sound-keywords = COSMIC;Garsas;Audio;Pranešimai;Pipewire;
xdg-entry-power-keywords = COSMIC;Energija;Baterija
xdg-entry-default-apps-keywords = COSMIC;Numatyta;Aplikacija
xdg-entry-default-apps-keywords = COSMIC;Numatyta;Programa
xdg-entry-keywords = COSMIC;Nustatymai;
xdg-entry-region-language-keywords = COSMIC;Regionas;Kalba;Data;Formatas;Laikas;Vietovė;Lokalizavimas;
xdg-entry-about = Apie
xdg-entry-input = Įvesties Įrenginiai
xdg-entry-applications-keywords = COSMIC;Numatytas;Aplikacija;Paleidimas;X11;Suderinamumas
xdg-entry-sound-comment = Audio nustatymai įrenginiams, pranešimams ir aplikacijoms
xdg-entry-sound-comment = Įrenginių, pranešimų ir programų garso nuostatos
xdg-entry-touchpad-comment = Jutiklinis kilimėlio greitis, valdymo parinktys, gestai
xdg-entry-keyboard-keywords = COSMIC;Klaviatūra;Įvestis;Šaltinis;Spartieji klavišai;
xdg-entry-vpn-comment = VPN ryšiai ir prisijungimų profiliai
xdg-entry-appearance = Išvaizda
xdg-entry-workspaces-comment = Darbo sričių orientacija, apžvalga ir keletos monitorių funkcionavimas
xdg-entry-network-comment = Valdyti tinklo ryšius
xdg-entry-panel-comment = Pagrindinė sistemos juosta apletams ir meniu
xdg-entry-workspaces-comment = Darbo sričių orientacija, peržiūra ir veikimas keliuose monitoriuose
xdg-entry-network-comment = Tvarkyti tinklo ryšius
xdg-entry-panel-comment = Pagrindinė sistemos meniu ir apletų juosta
xdg-entry-sound = Garsas
xdg-entry-power-comment = Energijos režimai ir energijos taupymo parinktys
xdg-entry-displays-keywords = COSMIC;Ekranas;
xdg-entry-workspaces = Darbo sritys
xdg-entry-date-time-comment = Laiko juosta, automatiniai laikrodžio nustatymai ir laiko formatavimas
xdg-entry-x11-applications = X11 Aplikacijų Suderinamumas
xdg-entry-date-time-comment = Laiko juosta, automatinės laikrodžio nuostatos ir laiko formatai
xdg-entry-x11-applications = Suderinamumas su X11 programomis
xdg-entry-wired-comment = Laidiniai ryšiai ir ryšių profiliai
no-search-results = Joks tinklas neatitinka jūsų paieškos.

View file

@ -116,6 +116,8 @@ vpn-error = Błąd VPN
.connect = Nie udało się połączyć z VPN
.connection-editor = Błąd edytora połączenia
.connection-settings = Nie udało się uzyskać ustawień dla aktywnych połączeń
.openvpn-config-path = Nieprawidłowa ścieżka pliku konfiguracji OpenVPN
.openvpn-config-path-desc = Wybrany plik musi być w lokalnym systemie plików.
.updating-state = Nie udało się zaktualizować stanu menedżera sieci
.wireguard-config-path = Nieprawidłowa ścieżka pliku konfiguracji WireGuard
.wireguard-config-path-desc = Wybrany plik musi znajdować się w lokalnym systemie plików system.
@ -928,3 +930,9 @@ xdg-entry-window-management-keywords = COSMIC;Okna;Zarządzanie;Kafelkowanie;Sup
xdg-entry-panel-comment = Podstawowy panel z menu i apletami
xdg-entry-displays-keywords = COSMIC;Wyświetlacz;Ekran;
no-search-results = Żadna sieć nie odpowiada twojemu wyszukiwaniu.
workspaces-navigation = Navigation
.wraparound = Przemieszczaj się między pierwszym a ostatnim obszarem roboczym używając skrótów klawiszowych i gestów
bluetooth-display-passkey = Parowanie Bluetooth
.description = Wpisz ten kod na { $device } i naciśnij Enter
bluetooth-display-pin = Bluetooth Pairing
.description = Wpisz PIN na { $device } i naciśnij Enter

View file

@ -60,8 +60,10 @@ vpn-error = Erro de VPN
.connect = Falha ao conectar na VPN
.connection-editor = Falha de edição da conexão
.connection-settings = Falha ao obter as configurações das conexões ativas
.openvpn-config-path = Caminho de arquivo inválido para a configuração do OpenVPN
.openvpn-config-path-desc = O arquivo escolhido deve estar em um sistema de arquivos local.
.updating-state = Falha ao atualizar o status do gerenciador de rede
.wireguard-config-path = Caminho do arquivo inválido para a configuração do WireGuard
.wireguard-config-path = Caminho de arquivo inválido para a configuração do WireGuard
.wireguard-config-path-desc = O arquivo selecionado deve estar em um sistema de arquivos local.
.wireguard-device = Falha ao criar um dispositivo WireGuard
.with-password =
@ -674,10 +676,10 @@ open-workspaces-view = Abrir a visão geral dos espaços de trabalho
## Time & Language
time = Hora e Idioma
time-date = Data e Hora
time-date = Data e hora
.auto = Definir automaticamente
.auto-ntp = A data e a hora serão atualizadas automaticamente quando o fuso horário for definido
time-zone = Fuso Horário
time-zone = Fuso horário
.auto = Fuso horário automático
.auto-info = Requer um serviço de localização e acesso à Internet
time-format = Formato de data e hora
@ -689,18 +691,18 @@ time-format = Formato de data e hora
.saturday = Sábado
.sunday = Domingo
.monday = Segunda-feira
time-region = Região e Idioma
time-region = Região e idioma
formatting = Formatação
.dates = Datas
.time = Horas
.date-and-time = Data e Hora
.date-and-time = Data e hora
.numbers = Números
.measurement = Medidas
.paper = Papel
preferred-languages = Idiomas preferidos
.desc = A ordem dos idiomas determina qual idioma será usado para a tradução do desktop. As alterações entram em vigor no próximo início de sessão.
add-language = Adicionar idioma
.context = Adicionar Idioma
.context = Adicionar idioma
install-additional-languages = Instalar idiomas adicionais
region = Região
@ -833,7 +835,7 @@ xdg-entry-appearance-keywords = COSMIC;Destaque;Accent;Cor;Ícone;Fonte;Tema
xdg-entry-mouse = Mouse
xdg-entry-dock-keywords = COSMIC;Dock;Painel;Applet;Miniaplicativo
xdg-entry-mouse-comment = Velocidade do mouse, aceleração e rolagem natural
xdg-entry-system = Sistema e Contas
xdg-entry-system = Sistema e contas
xdg-entry-panel-keywords = COSMIC;Painel;Applet;Miniaplicativo
xdg-entry-window-management = Gerenciamento de janelas
xdg-entry-a11y = Acessibilidade
@ -842,7 +844,7 @@ xdg-entry-desktop-keywords = COSMIC;Desktop;Área;Trabalho;
xdg-entry-x11-applications-keywords = COSMIC;X11;Aplicativo;Jogos;Compatibilidade;
xdg-entry-displays-comment = Gerenciar configurações de tela
xdg-entry-touchpad-keywords = COSMIC;Touchpad;Gestos;
xdg-entry-time-language = Hora e Idioma
xdg-entry-time-language = Hora e idioma
xdg-entry-users = Usuários
xdg-entry-system-keywords = COSMIC;Sistema;Info;Contas;Firmware;
xdg-entry-default-apps = Aplicativos padrão
@ -911,3 +913,9 @@ xdg-entry-x11-applications = Compatibilidade com aplicativos X11
xdg-entry-window-management-comment = Ação da tecla Super, opções de controle de janela e opções de janelas lado a lado (tiling)
xdg-entry-wired-comment = Conexões de rede com fio e perfis de conexão
no-search-results = Nenhuma rede corresponde à sua pesquisa.
workspaces-navigation = Navegação
.wraparound = Navegar entre o primeiro e o último espaço de trabalho usando atalhos de teclado e gestos
bluetooth-display-passkey = Emparelhamento Bluetooth
.description = Por favor, digite a seguinte chave em { $device }, depois pressione Enter
bluetooth-display-pin = Emparelhamento Bluetooth
.description = Por favor, digite o seguinte PIN em { $device }, depois pressione Enter

View file

@ -34,7 +34,7 @@ xdg-entry-notifications-comment = "Não Perturbe", notificações na tela de blo
xdg-entry-panel = Panel
xdg-entry-panel-comment = Barra superior com controles e menus
xdg-entry-power = Energia e Bateria
xdg-entry-power-comment = Modos de energia e opções de economia de energia
xdg-entry-power-comment = Modos de energia e opções de poupança de energia
xdg-entry-region-language = Região e Idioma
xdg-entry-region-language-comment = Formatação de datas, horas e números baseados em sua região
xdg-entry-sound = Som
@ -164,7 +164,6 @@ notifications = Notificações
## Desktop: Panel
panel = Painel
.desc = Barra do sistema principal para menus e miniaplicativos.
add = Adicionar
add-applet = Adicionar miniaplicativo
all = Todos
@ -207,7 +206,6 @@ panel-missing = A configuração do painel está em falta
## Desktop: Dock
dock = Doca
.desc = Uma barra opcional para aplicações e miniaplicativos.
## Desktop: Window management
@ -494,7 +492,7 @@ system-shortcut = Sistema
.keyboard-brightness-down = Diminuir o brilho do teclado
.keyboard-brightness-up = Aumentar o brilho do teclado
.launcher = Abrir o lançador
.log-out = Sair
.log-out = Terminar sessão
.lock-screen = Bloquear o ecrã
.mute = Silenciar saída de áudio
.mute-mic = Silenciar entrada do microfone
@ -580,7 +578,6 @@ time-format = Formato de Data e Hora
.sunday = Domingo
.monday = Segunda-feira
time-region = Região e Idioma
.desc = Formatar datas, horas e números baseado na sua região.
## System
@ -859,3 +856,4 @@ workspaces-overview = Visão geral das áreas de trabalho
.none = Nenhuma
.launcher = Abrir lançador
.applications = Abrir aplicações
xdg-entry-power-keywords = COSMIC;Energia;Bateria

View file

@ -112,11 +112,11 @@ notifications = Уведомления
dock = Док
hot-corner = Активные углы
.top-left-corner = Открывать рабочие столы при наведении в левый верхний угол
super-key = Клавиша Super
super-key = Действие клавиши Super
.launcher = Открывает панель запуска
.workspaces = Открывает обзор рабочих столов
.applications = Открывает приложения
.disable = Отключена
.disable = Отсутствует
window-controls = Элементы управления окнами
.minimize = Отображать кнопку «Свернуть»
.maximize = Отображать кнопку «Развернуть»
@ -571,9 +571,11 @@ vpn-error = Ошибка VPN
.connect = Не удалось подключиться к VPN
.connection-editor = Сбой редактора соединений
.connection-settings = Не удалось получить настройки активных подключений
.openvpn-config-path = Некорректный путь к файлу конфигурации OpenVPN
.openvpn-config-path-desc = Выбранный файл должен находиться в локальной файловой системе.
.updating-state = Не удалось обновить состояние сетевого менеджера
.wireguard-config-path = Некорректный путь до файла конфигурации WireGuard
.wireguard-config-path-desc = Выбранный файл должен находиться на локальной файловой системе.
.wireguard-config-path = Некорректный путь к файлу конфигурации WireGuard
.wireguard-config-path-desc = Выбранный файл должен находиться в локальной файловой системе.
.wireguard-device = Не удалось создать устройство WireGuard
.with-password =
Не удалось настроить { $field ->
@ -888,3 +890,9 @@ xdg-entry-x11-applications = Совместимость с приложения
xdg-entry-window-management-comment = Действие кнопки Super, параметры размещения и управления окнами
xdg-entry-wired-comment = Проводные соединения и профили
no-search-results = По запросу не найдено ни одной сети.
workspaces-navigation = Навигация
.wraparound = Переход между крайними рабочими столами при помощи сочетаний клавиш и жестов
bluetooth-display-passkey = Сопряжение по Bluetooth
.description = Введите следующий код на { $device }, затем нажмите Enter
bluetooth-display-pin = Сопряжение по Bluetooth
.description = Введите следующий PIN-код на { $device }, затем нажмите Enter

View file

@ -0,0 +1,822 @@
cancel = Откажи
connected = Повезано
confirm = Потврди
panel-left = Лево
connect = Повежи
super-key = Радња Супер тастера
.launcher = Отвори покретача
.workspaces = Отвори радне просторе
.applications = Отвори програме
.disable = Онемогући
x-minutes =
{ $number } { $number ->
[one] минут
*[other] минута
}
vrr = Променљива стопа освежавања
.enabled = Омогућено
.force = Увек
.auto = Самостално
.disabled = Онемогућено
window-hint-accent = Боја наговештаја за активни прозор
fit-to-screen = Уклопи у екран
control-tint = Боја компоненте за управљање
.desc = Користи се за позадине уобичајених дугмади, поља за претрагу, поља за унос текста и сличних компоненти
close = Затвори
save = Сачувај
vpn-error = Грешка ВПН-а
.config = Неуспешно додавање поставки ВПН-а
.connect = Неуспешно повезивање на ВПН
.connection-editor = Уређивач везе није успео
.connection-settings = Неуспешно преузимање подешавања за активне везе
.updating-state = Неуспешно ажурирање стања руководиоца мреже
.wireguard-config-path = Неисправна путања до датотеке подешавања за Вајергард
.wireguard-config-path-desc = Изабрана датотека мора бити у локалном систему датотека.
.wireguard-device = Неуспешно стварање уређаја за Вајергард
.with-password =
Неуспешно постављање ВПН { $field ->
*[username] username
[password] password
[password-flags] password-flags
} помоћу nmcli
toggle-stacking = Окини слагање прозора
panel-top = Горе
no-applets-found = Није пронађен ниједан програмчић...
region = Област
zoom-in = Увећај
place-here = Поставите програмчиће овде
-requires-restart = Захтева поновно покретање
workspaces-orientation = Смер радних простора
.vertical = Усправно
.horizontal = Водоравно
acceleration-desc = Самостално прилагођава осетљивост праћења на основу брзине
all-displays = Сви екрани
scheduling = Заказивање
.manual = Ручно заказивање
frosted = Ефекат матираног стакла на системском прочељу
.desc = Примењује замућење позадине на окно, док, програмчиће, покретач и библиотеку програма
password = Лозинка
about-hardware = Хардвер
.model = Модел хардвера
.memory = Меморија
.processor = Процесор
.graphics = Графика
.disk-capacity = Капацитет диска
short = Кратко
vpn = ВПН
.connections = ВПН везе
.remove = Уклони профил везе
.select-file = Изабери датотеку са поставкама ВПН-а
.error = Неуспешно додавање ВПН подешавања
power-mode = Режим напајања
.battery = Продужено трајање батерије
.battery-desc = Смањена употреба напајања и утишан рад
.balanced = Уравнотежено
.balanced-desc = Тихи учинак и умерена потрошња енергије
.performance = Висок учинак
.performance-desc = Највећи учинак и потрошња енергије
.no-backend = Позадинац није пронађен. Инсталирај system76-power или power-profiles-daemon.
wired = Жичана
.adapter = Жичани адаптер { $id }
.connections = Жичане везе
.devices = Жичани уређаји
.remove = Уклони профил везе
bluetooth-available = Уређаји у близини
cable-unplugged = Кабел није прикључен
color-picker = Бирач боје
interface-font = Системски фонт
forget-dialog = Заборавити ову бежичну мрежу?
.description = Поново ћете морати да унесете лозинку да бисте у будућности користили ову бежичну мрежу.
remove = Уклони
default = Подразумевано
zoom-out = Умањи
hot-corner = Врући угао
.top-left-corner = Омогући горњи леви врући угао за радне просторе
keyboard-typing-assist = Куцање
.repeat-rate = Брзина понављања
.repeat-delay = Интервал понављања
edge-gravity = Лебдећи прозори теже ка најближим ивицама
amplification = Појачање
.desc = Омогућава повећање јачине звука на 150%
recent-colors = Недавне боје
about-os = Оперативни систем
.os = Оперативни систем
.os-architecture = Архитектура оперативног система
.kernel = Издање језгра
.desktop-environment = Радно окружење
.windowing-system = Прозорски систем
panel-behavior-and-position = Понашање и положаји
.autohide = Самостално сакриј траку
.dock-autohide = Самостално сакриј док
.position = Положај на екрану
.display = Прикажи на екрану
add-language = Додај језик
.context = Додај језик
password-mismatch = Лозинка и потврда се морају поклапати
accessibility = Приступачност
.vision = Вид
.on = Укључено
.off = Искључено
.unavailable = Недоступно
.screen-reader = Читач екрана
.high-contrast = Режим високог контраста
.invert-colors = Изврни боје
.color-filters = Филтери боје
container-background = Позадина контејнера
.desc-detail = Боја позадине контејнера се користи за бочни панел за навигацију, бочну фиоку, прозорчиће и сличне елементе. Подразумевано, боја позадине контејнера се самостално изводи из позадине прозора.
.reset = Врати на самостално
.desc = Користи се за бочни панел за навигацију, бочну фиоку, прозорчиће и сличне елементе
primary-button = Главно дугме
.desc = Подешава поредак физичких дугмади
.left = Лево
.right = Десно
sound-alerts = Упозорења
.volume = Јачина звука упозорења
.sound = Звук упозорења
icon-theme = Тема иконица
.desc = Примењује различит скуп иконица на програме
import = Увези
keyboard-shortcuts = Пречице тастатуре
.desc = Прикажи и прилагоди пречице
switch-between-windows = Пребацујте се између прозора
text-tint = Боја текста прочеља
.desc = Користи се за одређивање боја текста прочеља које имају довољан контраст на различитим површинама
online-accounts = Налози на мрежи
.desc = Додај налоге, ИМАП и СМТП, пословну пријаву
open-workspaces-view = Отвори преглед радних простора
all = Све
remove-user = Уклони корисника
unknown = Непознато
sound-input = Улаз
.volume = Јачина звука на улазу
.device = Улазни уређај
.level = Ниво улаза
add-another-keybinding = Додај још једну комбинацију тастера
copy-to-clipboard = Копирај у оставу
monospace-font = Фонт утврђене ширине
experimental-settings = Експериментална подешавања
recent-folders = Недавне фасцикле
color-filter = Врста филтера боје
.unknown = Непознат филтер је активан
.greyscale = Сива скала
.deuteranopia = Зелена/црвена (слабост зелене боје, деутеранопија)
.protanopia = Црвена/зелена (слабост црвене боје, протанопија)
.tritanopia = Плава/жута (слабост плаве боје, тританопија)
migrate-workspace =
Пресели радни простор на излаз { $direction ->
*[down] доле
[left] лево
[right] десно
[up] горе
}
connecting = Повезивање…
battery = Батерија
.minute =
{ $value } { $value ->
[one] минут
*[other] минута
}
.hour =
{ $value } { $value ->
[one] сат
*[other] сати
}
.day =
{ $value } { $value ->
[one] дан
*[other] дана
}
.less-than-minute = Мање од једног минута
.and = и
.remaining-time =
{ $time } до { $action ->
[full] пуног
*[other] празног
}
search-applets = Претрага програмчића...
export = Извези
pinch-to-zoom = Штипај за увећање
.desc = Користите два прста за увећање садржаја, за програме који подржавају увећање
wifi = Бежична
.adapter = Бежични адаптер { $id }
.forget = Заборави ову мрежу
x-hours =
{ $number } { $number ->
[one] сат
*[other] сати
}
added = Додато
input-source-switch = Промени извор уноса језика тастатуре
focus-navigation = Навигација фокусом
.focus-follows-cursor = Фокус прати показивач
.focus-follows-cursor-delay = Интервал праћења фокуса показивачем у мс
.cursor-follows-focus = Показивач прати фокус
full-name = Име и презиме
app-background = Позадина прозора
panel-right = Десно
keyboard-sources = Извори уноса
.desc = Извори уноса се могу пребацити помоћу комбинације тастера Super+Space. Ово се може прилагодити у подешавањима пречица тастатуре.
.move-up = Помери горе
.move-down = Помери доле
.settings = Подешавања
.view-layout = Прикажи распоред тастатуре
.remove = Уклони
.add = Додај извор уноса
nav-shortcuts = Навигација
.prev-output = Усредсреди претходни излаз
.next-output = Усредсреди следећи излаз
.last-workspace = Усредсреди последњи радни простор
.prev-workspace = Усредсреди претходни радни простор
.next-workspace = Усредсреди следећи радни простор
.focus =
Усредсреди прозор { $direction ->
*[down] доле
[in] унутра
[left] лево
[out] напоље
[right] десно
[up] горе
}
.output =
Пребаци на излаз { $direction ->
*[down] доле
[left] лево
[right] десно
[up] горе
}
.workspace = Пребаци на радни простор { $num }
hearing = Слух
.mono = Пусти стерео звук као моно
preferred-languages = Жељени језици
.desc = Редослед језика одређује који се језик користи за корисничко прочеље. Измене ступају на снагу при следећој пријави.
panel-style = Стил
.anchor-gap = Размак између траке и ивица екрана
.dock-anchor-gap = Размак између дока и ивица екрана
.extend = Прошири траку до ивица екрана
.dock-extend = Прошири док до ивица екрана
.appearance = Изглед
.size = Величина
.background-opacity = Непровидност позадине
username = Корисничко име
light = Светла
panel-appearance = Изглед
.match = Поклопи радну површину
.light = Светла
.dark = Тамна
workspaces-multi-behavior = Понашање са више екрана
.span = Радни простори се протежу преко екрана
.separate = Екрани имају раздвојене радне просторе
replace-shortcut-dialog = Заменити пречицу?
.desc = { $name } користи { $shortcut }. Ако је замените, { $name } ће бити онемогућено.
rgb = РГБ
about-device = Назив уређаја
.desc = Овај назив се појављује другим мрежним или Блутут уређајима
no-vpn = Нема доступних ВПН веза.
start-segment = Почетак сегмента
desktop = Радна површ
identity = Идентитет
reset-to-default = Врати на подразумевано
disconnect = Прекини везу
ok = У реду
app = Космик подешавања
debug = Уклањање грешака
enable-export = Примени тренутну тему на Гномове програме
.desc = Не подржавају сви скупови алата самостално пребацивање. Програме који нису Космик може бити потребно поново покренути након промене теме.
number = { $number }
shortcut-name = Назив пречице
style = Стил
.round = Заобљено
.slightly-round = Благо заобљено
.square = Квадратно
panel-applets = Подешавање
.dock-desc = Подеси програмчиће дока
.desc = Подеси програмчиће траке
qr-code-unavailable = КР код није доступан
small = Мало
window-tiling = Поплочавање прозора
.horizontal = Подеси водоравни смер
.vertical = Подеси усправни смер
.swap-window = Замени прозор
.toggle-tiling = Промени стање поплочавања прозора
.toggle-stacking = Промени стање слагања прозора
.toggle-floating = Промени стање плутања прозора
.toggle-orientation = Промени смер
change-password = Промени лозинку
disable-while-typing = Онемогући током куцања
long = Дуго
copied-to-clipboard = Копирано у оставу
bluetooth-confirm-pin = Потврдите ПИН блутута
.description = Потврдите да се следећи ПИН подудара са оним који је приказан на { $device }
legacy-app-scaling = Скалирање програма у Икс-11 систему прозора
.scaled-gaming = Оптимизуј за игре и програме у пуном екрану
.gaming-description = Икс-11 програми се могу појавити благо већи/мањи у поређењу са Вејленд програмима
.scaled-applications = Оптимизуј за програме
.applications-description = Игре и Икс-11 програми у пуном екрану могу се не поклопити са резолуцијом вашег екрана
.scaled-compatibility = Режим максималне усаглашености
.compatibility-description = Икс-11 програми се могу појавити замућеним на HiDPI екранима
.preferred-display = Жељени екран за игре и Икс-11 програме у пуном екрану
.no-display = Ништа
night-light = Ноћно светло
.auto = Самостално (од заласка до изласка сунца)
.desc = Умањи плаво светло топлијим бојама
custom-shortcuts = Произвољне пречице
.add = Додај пречицу
.context = Додај произвољну пречицу
.none = Нема произвољних пречица
visible-networks = Видљиве мреже
applets = Програмчићи
terminate = Окончај
window-hint-accent-toggle = Употреби акцентну боју теме као наговештај за активни прозор
type-to-search = Куцајте за претрагу...
workspaces-behavior = Понашање радног простора
.dynamic = Динамички радни простори
.dynamic-desc = Самостално уклања празне радне просторе.
.fixed = Неизмењив број радних простора
.fixed-desc = Додај или уклони радне просторе у прегледу.
wireguard-dialog = Додај Вајергард уређај
.description = Изаберите назив уређаја за подешавање Вајергарда.
remove-connection-dialog = Уклонити профил везе?
.vpn-description = Биће потребно да поново унесете лозинку како бисте у будућности користили ову мрежу.
.wired-description = Мораћете поново да креирате овај профил да бисте га у будућности користили.
password-confirm = Потврди лозинку
click-behavior = Понашање клика
.click-finger = Секундарни клик са два прста и средњи клик са три прста
.button-areas = Секундарни клик у доњем десном углу и средњи клик у доњем центру
move-windows = Помери прозоре
.direction =
Помери прозор { $direction ->
*[down] доле
[left] лево
[right] десно
[up] горе
}
.display =
Помери прозор за један екран { $direction ->
*[down] доле
[left] лево
[right] десно
[up] горе
}
.workspace =
Помери прозор за један радни простор { $direction ->
*[below] испод
[left] лево
[right] десно
[above] изнад
}
.workspace-num = Помери прозор у радни простор { $num }
.prev-workspace = Помери прозор у претходни радни простор
.next-workspace = Помери прозор у следећи радни простор
.last-workspace = Премешта прозор на последњи радни простор
.next-display = Премешта прозор на следећи екран
.prev-display = Премешта прозор на претходни екран
.send-to-prev-workspace = Премешта прозор у претходни радни простор
.send-to-next-workspace = Премешта прозор у следећи радни простор
switch-workspaces = Пребаци радне просторе
.horizontal = Превлачење са четири прста лево/десно
.vertical = Превлачење са четири прста горе/доле
applications = Програми
dbus-connection-error = Није успело повезивање са Д-сабирницом
system-controls = Системске контроле
auto-switch = Самостално пребацивање између светлог и тамног режима
.sunrise = Пребацује на светли режим при изласку сунца
.sunset = Пребацује на тамни режим при заласку сунца
.next-sunrise = Пребацује на светли режим при следећем изласку сунца
.next-sunset = Пребацује на тамни режим при следећем заласку сунца
dark = Тамна
add-vpn = Додај ВПН
keyboard-numlock-boot = Num Lock
.boot-state = Стање при покретању
.last-boot = Последње покретање
.on = Укључено
.off = Искључено
.set = Постави стање Num Lock-а при подизању
about-related = Повезана подешавања
.support = Потражите подршку
tap-to-click = Тапни за клик
.desc = Омогућава тап једним прстом за примарни клик, тап два прста за секундарни клик и тап три прста за средњи клик
interface-density = Густина прочеља
.comfortable = Удобно
.compact = Збијено
.spacious = Пространо
open-application-library = Отвори библиотеку програма
slow = Споро
network-name = Назив мреже
large = Велико
scrolling = Помицање
.two-finger = Помицање са два прста
.edge = Помицање дуж ивице једним прстом
.speed = Брзина помицања
.natural = Природно помицање
.natural-desc = Помакни садржај, уместо приказа
auth-dialog = Потребно је потврђивање идентитета
.vpn-description = Унесите корисничко име и лозинку које захтева ВПН услуга.
.wifi-description = Унесите лозинку или кључ за шифровање. Такође се можете повезати притискањем дугмета „WPS“ на рутеру.
add-user = Додај корисника
enable = Омогући
add-image = Додај слику
migrate-workspace-next = Пресели радни простор на следећи излаз
airplane-on = Режим авиона је укључен.
formatting = Форматирање
.dates = Датуми
.time = Време
.date-and-time = Датум и време
.numbers = Бројеви
.measurement = Мерење
.paper = Папир
disabled = Онемогућено
add-applet = Додај програмчић
show-extended-input-sources = Прикажи проширене изворе улаза
mode-and-colors = Режим и боје
forget = Заборави
center-segment = Средишњи сегмент
panel-missing = Подешавање траке недостаје
.desc = Датотека са подешавањима траке недостаје због употребе прилагођеног подешавања или је оштећена.
.fix = Врати на подразумевано
invalid-username = Неисправно корисничко име
mirroring = Пресликавање
.id = Пресликавање { $id }
.dont = Немој пресликати
.mirror = Пресликај { $display }
.project =
Пројектуј на { $display ->
[all] све екране
*[other] { $display }
}
.project-count =
Пројектује на { $count } { $count ->
[1] други екран
*[other] других екрана
}
orientation = Смер
.standard = Обично
.rotate-90 = Заокрени за 90°
.rotate-180 = Заокрени за 180°
.rotate-270 = Заокрени за 270°
auto = Самостално
end-segment = Крајњи сегмент
bluetooth-adapters = Блутут адаптери
time-format = Формат датума и времена
.twenty-four = 24-часовни формат
.show-seconds = Прикажи секунде
.first = Први дан у недељи
.show-date = Прикажи датум у програмчићу за време
.friday = Петак
.saturday = Субота
.sunday = Недеља
.monday = Понедељак
accent-color = Боја за наглашавање
type-key-combination = Укуцај комбинацију тастера
manage-windows = Управљај прозорима
.close = Затвори прозор
.maximize = Увећај прозор
.fullscreen = Прозор преко целог екрана
.minimize = Умањи прозор
.resize-inwards = Промени величину прозора ка унутра
.resize-outwards = Промени величину прозора ка споља
.toggle-sticky = Окини лепљиви прозор
never = Никада
install-additional-languages = Инсталирај додатне језике
settings = Подешавања
sound-output = Излаз
.volume = Јачина излаза
.device = Излазни уређај
.level = Ниво излаза
.config = Поставка
.balance = Равнотежа
.left = Лево
.right = Десно
window-controls = Контроле прозора
.maximize = Прикажи дугме за увећање
.minimize = Прикажи дугме за умањење
.active-window-hint = Прикажи ознаку за активни прозор
window-management-appearance = Управљање прозорима
.active-hint = Величина ознаке за активни прозор
.gaps = Размаци око поређаних прозора
migrate-workspace-prev = Пресели радни простор на претходни излаз
legacy-app-global-shortcuts = Опште пречице у програмима Икс-11
.desc = Опште пречице омогућавају да притисаци тастатура и догађаји дугмића миша у програмима буду препознати од стране других програма за функције као што су „притисни-за-говор“ или „притисни-за-утишавање“. Подразумевано, опште пречице су онемогућене у програмима Икс-11 како би се осигурало да други програми не могу пратити догађаје тастатуре и миша који садрже осетљиве податке.
.none = Нема кључева
.modifiers = Заменски тастери (Super, Shift, Control, Alt)
.combination = Сви тастери док су заменски тастери Super, Control или Alt притиснути
.all = Сви тастери
.mouse = Догађаји дугмади миша у Икс-11 програмима
activate = Покрени
replace = Замени
power-saving = Могућности штедње енергије
.turn-off-screen-after = Искључи екран након
.auto-suspend = Самостално обустављање
.auto-suspend-ac = Самостално обустављање када је прикључен
.auto-suspend-battery = Самостално обустављање при напајању батеријом
connected-devices = Повезани уређаји
.unknown = Непознат уређај
icons-and-toolkit = Иконице и теме скупа алата
sound-applications = Програми
.desc = Јачина звука и подешавања програма
time-zone = Временска зона
.auto = Самостална временска зона
.auto-info = Захтева услуге локације и приступ интернету
dialog-add = Додај
fast = Брзо
add-color = Додај боју
add-network = Додај мрежу
.profile = Додај профил
network-device-state =
.activated = Повезано
.config = Повезује се
.deactivating = Веза се прекида
.disconnected = Веза прекинута
.failed = Неуспешно повезивање
.ip-check = Провера везе
.ip-config = Захтевање ИП и информација о рутирању
.need-auth = Потребно је потврђивање идентитета
.prepare = Припрема за повезивање
.secondaries = Чекање секундарне везе
.unavailable = Недоступно
.unknown = Непознато стање
.unmanaged = Неуправљиво
.unplugged = Кабел није прикључен
command = Наредба
gestures = Гестови
.four-finger-down = Превлачење са четири прста на доле
.four-finger-left = Превлачење са четири прста на лево
.four-finger-right = Превлачење са четири прста на десно
.four-finger-up = Превлачење са четири прста на горе
.three-finger-any = Превлачење са три прста у било ком смеру
system = Систем и налози
no-networks = Ниједна мрежа није пронађена.
open-new-folder = Отвори нову фасциклу
panel-bottom = Доле
share = Дељење мреже
add = Додај
fill = Попуна
custom = Прилагођено
bluetooth-paired = Претходно повезани уређаји
.connect = Повежи
.battery = { $percentage }% батерија
dialog = Прозорче
.title = Задржати подешавања екрана?
.keep-changes = Задржи измене
.change-prompt = Измене подешавања ће се самостално вратити за { $time } секунди.
.revert-settings = Врати подешавања
navigate = Навигирај
workspaces-overview-thumbnails = Сличице прегледа радних простора
.show-number = Прикажи број радног простора
.show-name = Прикажи назив радног простора
color = Боја
.depth = Дубина боје
.profile = Профил боје
.sidebar = Профили боја
.temperature = Температура боје
hex = Хекс
magnifier = Лупа
.controls =
Или употребите ове пречице: { $zoom_in ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_in } за увећање,
}{ $zoom_out ->
[zero] { "" }
*[other]
{ "" }
{ $zoom_out } за умањење,
}
Супер + помакните точком миша
.scroll_controls = Омогући увећање мишем или додирном таблом помоћу Супер + помицање
.show_overlay = Прикажи прекривање лупе
.increment = Корак увећања
.signin = Покрени лупу при пријави
.applet = Окини лупу у програмчићу на траци
.movement = Померање увећаног прегледа
.continuous = Континуирано показивачем
.onedge = Када показивач досегне ивицу
.centered = За задржавање показивача на средини
colors = Боје
administrator = Администратор
.desc = Администратори могу да промене подешавања за све кориснике, као и да додају и уклоне друге кориснике
scan-to-connect-description = Скенирај КР код да би се повезао на ову мрежу.
modified = { $count } измењено
sound-usb-audio = УСБ аудио
sound-device-profiles = Профили уређаја
sound-hd-audio = ХД аудио
shadow-and-corners = Сенка и углови прозора
shadows-tiling = Поплочани прозори
.clip = Поклопи системске углове
.shadow = Примени сенке
shadows-floating = Лебдећи прозори
.clip = Поклопи системске углове и примени сенке
sound-device-port-unplugged = Ископчано
workspaces-overview = Преглед радних простора
.action-on-typing = Радња при куцању
.none = Ништа
.launcher = Отвори покретач
.applications = Отварање програма
xdg-entry-touchpad = Додирна табла
time-region = Регион и језик
power = Напајање и батерија
xdg-entry-wired = Жичана
xdg-entry-startup-apps-comment = Подеси програме који се покрећу при пријави
xdg-entry-region-language = Регион и језик
time-date = Датум и време
.auto = Постави самостално
.auto-ntp = Датум и време ће се самостално ажурирати када се постави временска зона
xdg-entry-mouse-keywords = COSMIC;Mouse;Acceleration;Scrolling;Космик;миш;убрзање;помицање;Kosmik;miš;ubrzanje;pomicanje;
xdg-entry-bluetooth-comment = Управљај Блутут уређајима
xdg-entry-notifications-keywords = COSMIC;Notification;Lock;Космик;обавештење;закључавање;Kosmik;obaveštenje;zaključavanje;
xdg-entry-startup-apps-keywords = COSMIC;Startup;Application;Космик;Покретање;Програм;kosmik;pokretanje;program;
xdg-entry-default-apps-comment = Подразумевани веб-прегледач, програм за е-пошту, прегледач датотека и остали програми
xdg-entry-keyboard-comment = Извори уноса, пребацивање, унос посебних знакова, пречице
xdg-entry-wired-keywords = COSMIC;Wired;LAN;Network;Connection;Космик;Жичана;ЛАН;Мрежа;Веза;kosmik;žičana;lan;mreža;veza;
xdg-entry-power = Напајање и батерија
xdg-entry-appearance-keywords = COSMIC;Accent;Color;Icon;Font;Theme;Космик;Нагласак;Боја;Иконица;Фонт;Тема;kosmik;naglasak;boja;ikonica;font;tema
xdg-entry-mouse = Миш
xdg-entry-dock-keywords = COSMIC;Dock;Panel;Applet;Космик;Док;Окно;Програмчић;kosmik;dok;okno;programčić
bluetooth = Блутут
.status = Овај систем је видљив као { $aliases } док су подешавања за Блутут отворена.
.connected = Повезано
.connecting = Повезује се
.disconnecting = Веза се прекида
.connect = Повежи
.disconnect = Прекини везу
.forget = Заборави
.dbus-error = Дошло је до грешке при интеракцији са Д-сабирницом: { $why }
.disabled = Услуга блутута је онемогућена
.inactive = Услуга блутута није покренута
.unknown = Није могуће покренути услугу блутута. Да ли је BlueZ инсталиран?
xdg-entry-mouse-comment = Брзина миша, убрзање и природно помицање
xdg-entry-system = Систем и налози
firmware = Фирмвер
xdg-entry-panel-keywords = COSMIC;Panel;Applet;Космик;површ;програмчић;Kosmik;površ;programčić
xdg-entry-window-management = Управљање прозорима
xdg-entry-a11y = Приступачност
xdg-entry-time-language-comment = Управљај системским датумом, временом, облашћу и језиком
xdg-entry-desktop-keywords = COSMIC;Desktop;Космик;Радна површина;kosmik;radna površina;
default-apps = Подразумевани програми
.web-browser = Веб прегледач
.file-manager = Управник датотека
.mail-client = Програм за е-пошту
.music = Музика
.video = Видео
.photos = Фотографије
.calendar = Календар
.terminal = Терминал
.other-associations = Остала повезивања
.text-editor = Уређивач текста
.not-installed = Није инсталирано
xdg-entry-x11-applications-keywords = COSMIC;X11;Application;Game;Compatibility;Космик;Икс-11;Програм;Игрица;Усаглашеност;kosmik;iks-11;program;igrica;usaglašenost;
xdg-entry-displays-comment = Управљај подешавањима поставки екрана
xdg-entry-touchpad-keywords = COSMIC;Touchpad;Gesture;Космик;додирна табла;гест;Kosmik;dodirna tabla;gest;
xdg-entry-time-language = Време и језик
xdg-entry-users = Корисници
window-management = Управљање прозорима
xdg-entry-system-keywords = COSMIC;System;Info;Accounts;Firmware;Космик;Систем;Подаци;Налози;Фирмвер;kosmik;sistem;podaci;nalozi;firmver;
xdg-entry-default-apps = Подразумевани програми
xdg-entry-wireless-keywords = Космик;Бежична;Вај-фај;Мрежа;Веза;
xdg-entry-applications-comment = Подразумевани програми, почетни програми и подешавања усаглашености програма за Икс-11
appearance = Изглед
mouse = Миш
.speed = Брзина миша
.acceleration = Омогући убрзање миша
dock = Док
xdg-entry-about-keywords = COSMIC;About;Космик;О програму;Kosmik;O programu
xdg-entry-panel = Површ
xdg-entry-notifications = Обавештења
display = Екрани
.arrangement = Распоред екрана
.arrangement-desc = Превуците екране да бисте их преуредили
.enable = Омогући екран
.external = { $size } { $output } спољни екран
.laptop = { $size } екран преносиног рачунара
.options = Подешавања приказа
.refresh-rate = Учесталост освежавања
.resolution = Резолуција
.scale = Размера
.additional-scale-options = Додатне могућности размере
xdg-entry-a11y-keywords = COSMIC;Accessibility;A11y;Screen;Reader;Magnifier;Contrast;Color;Космик;Приступачност;Екран;Читач;Лупа;Контраст;Боја;kosmik;pristupačnost;ekran;čitač;lupa;kontrast;boja;
xdg-entry-wireless-comment = Бежичне везе и профили везе
xdg-entry-desktop = Радна површина
xdg-entry-date-time-keywords = COSMIC;Time;Zone;Космик;Време;Зона;kosmik;vreme;zona;
xdg-entry-desktop-comment = Позадина, изглед, окно, док, управљање прозорима и подешавања радног простора
xdg-entry-wallpaper = Позадина
xdg-entry-keyboard = Тастатура
xdg-entry-a11y-comment = Читач екрана, лупа, велики контраст и филтери боја
xdg-entry-displays = Екрани
xdg-entry-network = Мрежа и бежична веза
sound = Звук
xdg-entry-date-time = Датум и време
notifications = Обавештења
xdg-entry-users-comment = Потврђивање идентитета и кориснички налози
startup-apps = Почетни програми
.add = Додај програм
.user = Програми који се покрећу када се пријавите
.none = Није додат ниједан програм за покретање
.remove-dialog-title = Уклонити { $name }?
.remove-dialog-description = Уклонити овај програм за покретање?
.add-startup-app = Додај програм за покретање
xdg-entry-input-comment = Подешавања тастатуре и миша
xdg-entry-bluetooth-keywords = COSMIC;Bluetooth;Космик;Блутут;kosmik;blutut;
about = О систему
input-devices = Улазни уређаји
xdg-entry-dock-comment = Необавезна трака за апликације и програмчиће
xdg-entry-network-keywords = COSMIC;Network;Wireless;WiFi;VPN;Космик;мрежа;бежична веза;бежична;ВПН;Kosmik;mreža;bežična veza;bežična;vpn;
xdg-entry-wallpaper-comment = Слике позадине, боје и могућности покретног приказа
xdg-entry-dock = Док
xdg-entry-startup-apps = Почетни програми
workspaces = Радни простори
xdg-entry-wireless = Бежична
xdg-entry-workspaces-keywords = COSMIC;WiFi;Wi-Fi;Network;Connection;Космик;Радни простор;Смер;Преглед;Монитор;kosmik;radni prostor;smer;pregled;monitor;
xdg-entry-system-comment = Подаци о систему, налози и ажурирања фирмвера
xdg-entry-x11-applications-comment = Размера програма прозорног система Икс-11, главни екран и опште пречице
xdg-entry-region-language-comment = Форматирајте датуме, време и бројеве на основу вашег региона
panel = Трака
xdg-entry-wallpaper-keywords = COSMIC;Wallpaper;Background;Slideshow;Космик;позадина;позадина;покретни приказ;Kosmik;pozadina;pozadina;pokretni prikaz;
xdg-entry-users-keywords = COSMIC;User;Account;Космик;корисник;налог;Kosmik;korisnik;nalog;
xdg-entry-about-comment = Назив уређаја, подаци о хардверу, подразумевана подешавања оперативног система
xdg-entry-vpn-keywords = COSMIC;VPN;Network;Connection;OpenVPN;OpenConnect;Космик;ВПН;мрежа;веза;ОпенВПН;ОпенКонект;kosmik;vpn;mreža;veza;openvpn;openkonekt;
users = Корисници
.admin = Админ
.standard = Обичан
.profile-add = Изаберите слику профила
xdg-entry-appearance-comment = Боје нагласка и теме
xdg-entry-time-language-keywords = COSMIC;System;Time;Date;Region;Language;Космик;систем;време;датум;област;језик;Kosmik;sistem;vreme;datum;oblast;jezik;
xdg-entry-input-keywords = COSMIC;Input;Keyboard;Mouse;Mice;Космик;Унос;Тастатура;Миш;Мишеви;kosmik;unos;tastatura;miš;miševi;
xdg-entry-comment = Програм Подешавања за Космик радну површину
xdg-entry-notifications-comment = Не узнемиравај, обавештења на закључаном екрану и подешавања по програмима
xdg-entry-applications = Програми
xdg-entry-sound-keywords = COSMIC;Sound;Audio;Alert;Pipewire;Космик;Звук;Аудио;Упозорење;Пајпвајер;kosmik;zvuk;audio;upozorenje;pajpvajer;
xdg-entry-power-keywords = COSMIC;Power;Battery;Космик;Напајање;Батерија;kosmik;napajanje;baterija
legacy-applications = Усаглашеност програма Икс-11
xdg-entry-default-apps-keywords = COSMIC;Default;Application;Космик;Подразумевано;Програм;kosmik;podrazumevano;program
xdg-entry-keywords = COSMIC;Settings;Космик;Подешавања;kosmik;podešavanja;
xdg-entry-region-language-keywords = COSMIC;Region;Language;Date;Format;Time;Locale;Localization;Космик;Регион;Језик;Датум;Формат;Време;Локалитет;Локализација;kosmik;region;jezik;datum;format;vreme;lokalitet;lokalizacija;
xdg-entry-about = О програму
xdg-entry-input = Улазни уређаји
xdg-entry-applications-keywords = COSMIC;Default;Application;Startup;X11;Compatibility;Космик;Подразумевано;Програм;Покретање;Икс-11;Усаглашеност;kosmik;podrazumevano;program;pokretanje;iks-11;usaglašenost
xdg-entry-sound-comment = Подешавања звука за уређаје, упозорења и програме
wallpaper = Позадина
.change = Промени слику сваких
.fit = Уклапање позадине
.folder-dialog = Изаберите фасциклу позадине
.image-dialog = Изаберите слику позадине
.plural = Позадине
.same = Иста позадина на свим екранима
.slide = Покретни приказ
xdg-entry-touchpad-comment = Брзина додирне табле, могућности клика и гестова
xdg-entry-keyboard-keywords = COSMIC;Keyboard;Input;Source;Shortcuts;Космик;тастатура;унос;извор;пречице;Kosmik;tastatura;unos;izvor;prečice;
xdg-entry-vpn-comment = ВПН везе и профили везе
keyboard = Тастатура
xdg-entry-appearance = Изглед
xdg-entry-window-management-keywords = COSMIC;Window;Management;Tiling;Super;Key;Космик;Прозор;Управљање;Поплочавање;Супер;Тастер;kosmik;prozor;upravljanje;popločavanje;super;taster;
xdg-entry-workspaces-comment = Смер радних простора, преглед и понашање са више екрана
xdg-entry-network-comment = Управљај мрежним везама
xdg-entry-panel-comment = Примарна системска трака за изборнике и програмчиће
touchpad = Додирна табла
.acceleration = Омогући убрзање додирне табле
.speed = Брзина додирне табле
xdg-entry-sound = Звук
xdg-entry-power-comment = Режими напајања и могућности уштеде енергије
xdg-entry-displays-keywords = COSMIC;Display;Космик;Екран;kosmik;ekran;
time = Време и језик
xdg-entry-workspaces = Радни простори
xdg-entry-date-time-comment = Временска зона, самостална подешавања сата и форматирање времена
xdg-entry-x11-applications = Усаглашеност програма Икс-11
xdg-entry-window-management-comment = Радња Супер тастера, могућности управљања прозором и додатне могућности поплочавања прозора
xdg-entry-wired-comment = Жичане везе и профили везе
known-networks = Познате мреже
network-and-wireless = Мрежа и бежична веза
keyboard-special-char = Унос посебних знакова
.alternate = Тастер заменских знакова
.compose = Саставни тастер
.compose-desc = Саставни тастер омогућава унос великого броја различитих знакова. Да бисте га користили, притисните саставни тастер, а затим низ знакова. На пример, саставни тастер праћено словима C и o унеће ©, док саставни тастер праћено словима a и унеће á.
.caps = Тастер Caps Lock
system-shortcut = Систем
.app-library = Отвори библиотеку програма
.brightness-down = Смањи осветљење екрана
.brightness-up = Повећај осветљење екрана
.display-toggle = Промени стање унутрашњег екрана
.home-folder = Отвори личну фасциклу
.keyboard-brightness-down = Смањи осветљење тастатуре
.keyboard-brightness-up = Повећај осветљење тастатуре
.launcher = Отвори покретач
.log-out = Одјави се
.lock-screen = Закључај екран
.mute = Утишај аудио излаз
.mute-mic = Утишај микрофонски улаз
.play-pause = Пусти/паузирај
.play-next = Следећа нумера
.play-prev = Претходна нумера
.poweroff = Искључи
.screenshot = Направи снимак екрана
.suspend = Обустави
.terminal = Отвори терминал
.touchpad-toggle = Промени стање додирне табле
.volume-lower = Смањи јачину звука на излазу
.volume-raise = Повећај јачину звука на излазу
.web-browser = Отвори веб прегледач
.window-switcher = Пребаци између отворених прозора
.window-switcher-previous = Пребаци између отворених прозора у обратном смеру
.workspace-overview = Отвори преглед радног простора
workspaces-navigation = Навигација
.wraparound = Померите се између првог и последњег радног простора помоћу пречица на тастатури и гестова
no-search-results = Ниједна мрежа се не поклопи са вашом претрагом.

View file

@ -926,3 +926,9 @@ xdg-entry-region-language-keywords = COSMIC;Region;Språk;Datum;Format;Tid;Lokal
xdg-entry-keyboard-keywords = COSMIC;Tangentbord;Inmatning;Källa;Genvägar;
xdg-entry-window-management-keywords = COSMIC;Fönster;Hantering;Kaklande;Super;Tangent;
no-search-results = Inga nätverk matchar din sökning.
workspaces-navigation = Navigering
.wraparound = Flytta mellan första och sista arbetsytan med hjälp av tangentbordsgenvägar och gester
bluetooth-display-passkey = Bluetooth-koppling
.description = Mata in följande säkerhetskod på { $device }, tryck sedan Enter
bluetooth-display-pin = Bluetooth-koppling
.description = Ange följande PIN på { $device }, tryck sedan Enter

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