├── .envrc
├── .github
├── FUNDING.yml
└── workflows
│ ├── pr.yml
│ └── push.yml
├── .gitignore
├── .vscode
└── launch.json
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── aux
└── fcg.py
├── default.nix
├── dev.edfloreshz.CosmicTweaks.json
├── docs
├── CNAME
├── assets
│ └── img
│ │ └── preview.png
├── index.html
└── style.css
├── flake.lock
├── flake.nix
├── i18n.toml
├── i18n
├── en
│ └── cosmic_ext_tweaks.ftl
├── es-419
│ └── cosmic_ext_tweaks.ftl
├── es-MX
│ └── cosmic_ext_tweaks.ftl
├── es
│ └── cosmic_ext_tweaks.ftl
├── fr
│ └── cosmic_ext_tweaks.ftl
├── ja
│ └── cosmic_ext_tweaks.ftl
├── nl
│ └── cosmic_ext_tweaks.ftl
├── pt-BR
│ └── cosmic_ext_tweaks.ftl
├── ro
│ └── cosmic_ext_tweaks.ftl
├── sr-Cyrl
│ └── cosmic_ext_tweaks.ftl
├── sr-Latn
│ └── cosmic_ext_tweaks.ftl
└── sv
│ └── cosmic_ext_tweaks.ftl
├── justfile
├── res
├── app.desktop
├── icons
│ ├── bundled
│ │ ├── arrow-circular-bottom-right-symbolic.svg
│ │ ├── arrow-into-box-symbolic.svg
│ │ ├── checkmark-symbolic.svg
│ │ ├── cross-small-square-filled-symbolic.svg
│ │ ├── dark-mode-symbolic.svg
│ │ ├── dock-bottom-symbolic.svg
│ │ ├── dock-top-symbolic.svg
│ │ ├── document-save-symbolic.svg
│ │ ├── edit-symbolic.svg
│ │ ├── face-smile-big-symbolic.svg
│ │ ├── folder-download-symbolic.svg
│ │ ├── info-outline-symbolic.svg
│ │ ├── keyboard-symbolic.svg
│ │ ├── list-add-symbolic.svg
│ │ ├── plus-square-filled-symbolic.svg
│ │ ├── recycling-bin-symbolic.svg
│ │ ├── resize-mode-symbolic.svg
│ │ ├── search-global-symbolic.svg
│ │ ├── selection-mode-symbolic.svg
│ │ ├── settings-symbolic.svg
│ │ ├── size-horizontally-symbolic.svg
│ │ ├── snapshots-symbolic.svg
│ │ ├── step-in-symbolic.svg
│ │ ├── symbolic-link-symbolic.svg
│ │ ├── tabs-stack-symbolic.svg
│ │ ├── user-trash-symbolic.svg
│ │ └── view-coverflow-symbolic.svg
│ └── hicolor
│ │ └── scalable
│ │ └── apps
│ │ └── icon.svg
├── layouts
│ ├── cosmic.ron
│ ├── mac.ron
│ ├── ubuntu.ron
│ └── windows.ron
├── metainfo.xml
├── screenshots
│ ├── color-schemes-dark.png
│ ├── color-schemes-light.png
│ ├── dock-dark.png
│ ├── dock-light.png
│ ├── layouts-dark.png
│ ├── layouts-light.png
│ ├── panel-dark.png
│ ├── panel-light.png
│ ├── snapshots-dark.png
│ └── snapshots-light.png
└── shortcuts
│ └── windows.ron
├── shell.nix
└── src
├── app.rs
├── app
├── action.rs
├── context.rs
├── context_drawer.rs
├── core
│ ├── config.rs
│ ├── error.rs
│ ├── grid.rs
│ ├── icons.rs
│ ├── key_bindings.rs
│ ├── localize.rs
│ ├── mod.rs
│ ├── settings.rs
│ └── style.rs
├── dialog.rs
├── flags.rs
├── footer.rs
├── header.rs
├── init.rs
├── message.rs
├── nav.rs
├── page.rs
├── pages
│ ├── color_schemes
│ │ ├── config.rs
│ │ ├── cosmic_theme.rs
│ │ ├── mod.rs
│ │ └── preview.rs
│ ├── dock.rs
│ ├── layouts
│ │ ├── config.rs
│ │ ├── dialog.rs
│ │ ├── mod.rs
│ │ └── preview.rs
│ ├── mod.rs
│ ├── panel
│ │ ├── config.rs
│ │ └── mod.rs
│ ├── shortcuts.rs
│ └── snapshots
│ │ ├── config.rs
│ │ └── mod.rs
├── subscription.rs
├── update.rs
└── view.rs
└── main.rs
/.envrc:
--------------------------------------------------------------------------------
1 | if has nix_direnv_version; then
2 | use flake
3 | fi
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: edfloreshz
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: edfloreshz
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: edfloreshz
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | name: Pull request
2 |
3 | on:
4 | pull_request:
5 | branches: [ "main" ]
6 |
7 | env:
8 | CARGO_TERM_COLOR: always
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | checks: write
15 | pull-requests: write
16 | steps:
17 | - name: Install depencencies
18 | run: sudo apt-get install -y libxkbcommon-dev
19 | - uses: actions/checkout@v4
20 | - name: Build
21 | run: cargo build --verbose
22 | - name: clippy
23 | uses: auguwu/clippy-action@1.4.0
24 | with:
25 | token: ${{ secrets.GITHUB_TOKEN }}
26 |
27 |
--------------------------------------------------------------------------------
/.github/workflows/push.yml:
--------------------------------------------------------------------------------
1 | name: Push
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 |
7 | env:
8 | CARGO_TERM_COLOR: always
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | checks: write
15 | pull-requests: write
16 | steps:
17 | - name: Install depencencies
18 | run: sudo apt-get install -y libxkbcommon-dev
19 | - uses: actions/checkout@v4
20 | - name: Build
21 | run: cargo build --verbose
22 | - name: clippy
23 | uses: auguwu/clippy-action@1.4.0
24 | with:
25 | token: ${{ secrets.GITHUB_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # These are backup files generated by rustfmt
7 | **/*.rs.bk
8 |
9 | # MSVC Windows builds of rustc generate these, which store debugging information
10 | *.pdb
11 |
12 | /flatpak-builder-tools
13 | /.flatpak-builder
14 | /repo
15 | /flatpak-out
16 |
17 | vendor/
18 |
19 | .cargo/
20 | .direnv/
21 | result*
22 |
23 | .idea
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "lldb",
9 | "request": "launch",
10 | "name": "Debug executable 'cosmic-ext-tweaks'",
11 | "cargo": {
12 | "args": [
13 | "build",
14 | "--bin=cosmic-ext-tweaks",
15 | "--package=cosmic-ext-tweaks"
16 | ],
17 | "filter": {
18 | "name": "cosmic-ext-tweaks",
19 | "kind": "bin"
20 | }
21 | },
22 | "args": [],
23 | "cwd": "${workspaceFolder}"
24 | },
25 | {
26 | "type": "lldb",
27 | "request": "launch",
28 | "name": "Debug unit tests in executable 'cosmic-ext-tweaks'",
29 | "cargo": {
30 | "args": [
31 | "test",
32 | "--no-run",
33 | "--bin=cosmic-ext-tweaks",
34 | "--package=cosmic-ext-tweaks"
35 | ],
36 | "filter": {
37 | "name": "cosmic-ext-tweaks",
38 | "kind": "bin"
39 | }
40 | },
41 | "args": [],
42 | "cwd": "${workspaceFolder}"
43 | }
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cosmic-ext-tweaks"
3 | version = "0.1.3"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | dirs = "5.0.1"
8 | env_logger = "0.11.5"
9 | i18n-embed-fl = "0.8"
10 | log = "0.4.22"
11 | open = "5.1.2"
12 | pretty_env_logger = "0.5.0"
13 | reqwest = { version = "0.12", features = ["json"] }
14 | ron = "0.8.1"
15 | rust-embed = "8.3.0"
16 | chrono = { version = "0.4.38", features = ["serde"] }
17 | thiserror = "2.0.12"
18 | uuid = { version = "1.17.0", features = ["serde", "v4"] }
19 |
20 | [dependencies.ashpd]
21 | version = "0.8.1"
22 | default-features = false
23 | features = ["async-std"]
24 |
25 | [dependencies.libcosmic]
26 | git = "https://github.com/pop-os/libcosmic.git"
27 | default-features = false
28 | features = ["tokio", "winit", "wgpu", "about"]
29 |
30 | [dependencies.cosmic-ext-config-templates]
31 | git = "https://github.com/ryanabx/cosmic-ext-config-templates"
32 |
33 | [dependencies.i18n-embed]
34 | version = "0.14"
35 | features = ["fluent-system", "desktop-requester"]
36 |
37 | [dependencies.cosmic-panel-config]
38 | git = "https://github.com/pop-os/cosmic-panel"
39 |
40 | [dependencies.serde]
41 | version = "1.0.196"
42 | features = ["derive"]
43 |
44 | [dependencies.tokio]
45 | version = "1.35.1"
46 | features = ["macros", "fs", "rt"]
47 |
48 | [patch."https://github.com/smithay/client-toolkit.git"]
49 | sctk = { package = "smithay-client-toolkit", version = "=0.19.2" }
50 |
51 | #[patch."https://github.com/pop-os/libcosmic.git"]
52 | #libcosmic = { path = "../../edfloreshz-ext/libcosmic" }
53 | #cosmic-config = { path = "../../edfloreshz-ext/libcosmic/cosmic-config" }
54 | #cosmic-config-derive = { path = "../../edfloreshz-ext/libcosmic/cosmic-config-derive" }
55 | #cosmic-theme = { path = "../../edfloreshz-ext/libcosmic/cosmic-theme" }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Tweaks
4 |
Beyond the limits of your desktop
5 |
A tweaking tool offering access to advanced settings and features for COSMIC™
6 |
7 |
8 |
9 |
10 | 
11 | 
12 |
13 | ## Getting Started
14 | Clone this repository to your local machine and open it in your code editor.
15 |
16 | Run `cargo run` in the terminal to build and run the application.
17 |
18 | ## Dependencies
19 | - `cargo`
20 | - `just`
21 | - `libxkbcommon-dev`
22 | - [`libcosmic`](https://github.com/pop-os/libcosmic?tab=readme-ov-file#building)
23 |
24 | ## Installation
25 | Clone this repository to your local machine and run:
26 |
27 | ```bash
28 | just build-release
29 | sudo just install
30 | ```
31 |
32 | ## License
33 | This project is licensed under the GPL-3.0 License - see the [LICENSE](LICENSE) file for details.
34 |
--------------------------------------------------------------------------------
/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs ? import { },
3 | ...
4 | }:
5 | pkgs.callPackage (
6 | {
7 | lib,
8 | rustPlatform,
9 | just,
10 | openssl,
11 | libxkbcommon,
12 | libGL,
13 | xorg,
14 | vulkan-loader,
15 | wayland,
16 | pkg-config,
17 | makeWrapper,
18 | stdenv,
19 | cosmic-comp,
20 | cosmic-icons,
21 | }:
22 | let
23 | pname = "cosmic-ext-tweaks";
24 | version = "0.1.3";
25 |
26 | buildInputs = [
27 | openssl
28 | libGL
29 | libxkbcommon
30 | vulkan-loader
31 | wayland
32 | xorg.libX11
33 | xorg.libXcursor
34 | xorg.libXi
35 | xorg.libXrandr
36 | ];
37 | in
38 | rustPlatform.buildRustPackage {
39 | inherit pname version buildInputs;
40 |
41 | src = builtins.path {
42 | name = "${pname}-source";
43 | path = ./.;
44 | };
45 |
46 | cargoLock = {
47 | lockFile = ./Cargo.lock;
48 | outputHashes = {
49 | "accesskit-0.16.0" = "sha256-yeBzocXxuvHmuPGMRebbsYSKSvN+8sUsmaSKlQDpW4w=";
50 | "atomicwrites-0.4.2" = "sha256-QZSuGPrJXh+svMeFWqAXoqZQxLq/WfIiamqvjJNVhxA=";
51 | "clipboard_macos-0.1.0" = "sha256-tovB4fjPVVRY8LKn5albMzskFQ+1W5ul4jT5RXx9gKE=";
52 | "cosmic-config-0.1.0" = "sha256-VVxiIJanb9gs/7sYpXtsoDdsd+QDUg0QBpBpBWVTSqo=";
53 | "cosmic-ext-config-templates-2.0.2" = "sha256-MkccHdaB4qUOELQdWRMPyLbBM6jMg37sC+TfVHUV9Ew=";
54 | "cosmic-panel-config-0.1.0" = "sha256-/mAffg2OuL5atwYpW64JlFsKY0s/PYR7hdPyLhhQbKQ=";
55 | "cosmic-text-0.12.1" = "sha256-nCw3RNIHINXH4+m9wKB+0CeoXSVKKxP+ylaZhfp8u+o=";
56 | "dpi-0.1.1" = "sha256-whi05/2vc3s5eAJTZ9TzVfGQ/EnfPr0S4PZZmbiYio0=";
57 | "iced_glyphon-0.6.0" = "sha256-u1vnsOjP8npQ57NNSikotuHxpi4Mp/rV9038vAgCsfQ=";
58 | "smithay-clipboard-0.8.0" = "sha256-4InFXm0ahrqFrtNLeqIuE3yeOpxKZJZx+Bc0yQDtv34=";
59 | "softbuffer-0.4.1" = "sha256-a0bUFz6O8CWRweNt/OxTvflnPYwO5nm6vsyc/WcXyNg=";
60 | "taffy-0.3.11" = "sha256-SCx9GEIJjWdoNVyq+RZAGn0N71qraKZxf9ZWhvyzLaI=";
61 | };
62 | };
63 |
64 | nativeBuildInputs = [
65 | just
66 | pkg-config
67 | makeWrapper
68 | ];
69 |
70 | dontUseJustBuild = true;
71 | dontUseJustCheck = true;
72 |
73 | justFlags = [
74 | "--set"
75 | "prefix"
76 | (placeholder "out")
77 | "--set"
78 | "bin-src"
79 | "target/${stdenv.hostPlatform.rust.cargoShortTarget}/release/cosmic-ext-tweaks"
80 | ];
81 |
82 | postInstall = ''
83 | wrapProgram $out/bin/cosmic-ext-tweaks \
84 | --suffix XDG_DATA_DIRS : "${cosmic-icons}/share" \
85 | --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath buildInputs}
86 | '';
87 |
88 | meta = {
89 | changelog = "https://github.com/cosmic-utils/tweaks/releases/tag/${version}";
90 | description = "Tweaking tool for the COSMIC Desktop Environment";
91 | homepage = "https://github.com/cosmic-utils/tweaks";
92 | license = lib.licenses.gpl3Only;
93 | maintainers = with lib.maintainers; [ HeitorAugustoLN ];
94 | mainProgram = "cosmic-ext-tweaks";
95 | inherit (cosmic-comp.meta) platforms;
96 | };
97 | }
98 | ) { }
99 |
--------------------------------------------------------------------------------
/dev.edfloreshz.CosmicTweaks.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "dev.edfloreshz.CosmicTweaks",
3 | "runtime": "org.freedesktop.Platform",
4 | "runtime-version": "24.08",
5 | "sdk": "org.freedesktop.Sdk",
6 | "sdk-extensions": [
7 | "org.freedesktop.Sdk.Extension.rust-stable",
8 | "org.freedesktop.Sdk.Extension.llvm18"
9 | ],
10 | "command": "cosmic-ext-tweaks",
11 | "finish-args": [
12 | "--share=ipc",
13 | "--share=network",
14 | "--socket=fallback-x11",
15 | "--socket=wayland",
16 | "--device=dri",
17 | "--talk-name=com.system76.CosmicSettingsDaemon",
18 | "--filesystem=xdg-config/cosmic:rw"
19 | ],
20 | "build-options": {
21 | "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin",
22 | "env": {
23 | "CARGO_HOME": "/run/build/tweaks/cargo",
24 | "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER": "clang",
25 | "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold",
26 | "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER": "clang",
27 | "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold"
28 | }
29 | },
30 | "modules": [
31 | {
32 | "name": "tweaks",
33 | "buildsystem": "simple",
34 | "build-commands": [
35 | "cargo --offline build --release --verbose",
36 | "install -Dm0755 ./target/release/cosmic-ext-tweaks -t /app/bin/",
37 | "install -Dm644 ./res/app_icon.svg /app/share/icons/hicolor/scalable/apps/dev.edfloreshz.CosmicTweaks.svg",
38 | "install -Dm644 ./res/metainfo.xml /app/share/metainfo/dev.edfloreshz.CosmicTweaks.metainfo.xml",
39 | "install -Dm644 ./res/desktop_entry.desktop /app/share/applications/dev.edfloreshz.CosmicTweaks.desktop"
40 | ],
41 | "sources": [
42 | {
43 | "type": "dir",
44 | "path": "./"
45 | },
46 | "./cargo-sources.json"
47 | ]
48 | }
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | tweaks.edfloreshz.dev
--------------------------------------------------------------------------------
/docs/assets/img/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/docs/assets/img/preview.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Tweaks
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
53 |
54 |
55 |
67 |
68 |
69 |
70 |
71 |
72 |
75 |
76 |
77 |
78 |
82 |
83 | Beyond the limits of your desktop
84 |
85 |
86 | Personalize your COSMIC™ desktop beyond infinity
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | @media (prefers-color-scheme: dark) {
2 | #screenshot {
3 | content: url("https://raw.githubusercontent.com/cosmic-utils/tweaks/refs/heads/main/res/screenshots/color-schemes-dark.png?raw=true");
4 | }
5 |
6 | html {
7 | background: linear-gradient(to right bottom, #005a41ff, #059f7aff);
8 | }
9 | }
10 |
11 | @media (prefers-color-scheme: light) {
12 | #screenshot {
13 | content: url("https://raw.githubusercontent.com/cosmic-utils/tweaks/refs/heads/main/res/screenshots/color-schemes-light.png?raw=true");
14 | }
15 |
16 | body {
17 | background: linear-gradient(to right bottom, #88ffe1ff, #c9fff1ff);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-parts": {
4 | "inputs": {
5 | "nixpkgs-lib": [
6 | "nixpkgs"
7 | ]
8 | },
9 | "locked": {
10 | "lastModified": 1733312601,
11 | "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
12 | "owner": "hercules-ci",
13 | "repo": "flake-parts",
14 | "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
15 | "type": "github"
16 | },
17 | "original": {
18 | "owner": "hercules-ci",
19 | "repo": "flake-parts",
20 | "type": "github"
21 | }
22 | },
23 | "nixpkgs": {
24 | "locked": {
25 | "lastModified": 1733581040,
26 | "narHash": "sha256-Qn3nPMSopRQJgmvHzVqPcE3I03zJyl8cSbgnnltfFDY=",
27 | "owner": "NixOS",
28 | "repo": "nixpkgs",
29 | "rev": "22c3f2cf41a0e70184334a958e6b124fb0ce3e01",
30 | "type": "github"
31 | },
32 | "original": {
33 | "owner": "NixOS",
34 | "ref": "nixos-unstable",
35 | "repo": "nixpkgs",
36 | "type": "github"
37 | }
38 | },
39 | "root": {
40 | "inputs": {
41 | "flake-parts": "flake-parts",
42 | "nixpkgs": "nixpkgs",
43 | "treefmt-nix": "treefmt-nix"
44 | }
45 | },
46 | "treefmt-nix": {
47 | "inputs": {
48 | "nixpkgs": [
49 | "nixpkgs"
50 | ]
51 | },
52 | "locked": {
53 | "lastModified": 1733761991,
54 | "narHash": "sha256-s4DalCDepD22jtKL5Nw6f4LP5UwoMcPzPZgHWjAfqbQ=",
55 | "owner": "numtide",
56 | "repo": "treefmt-nix",
57 | "rev": "0ce9d149d99bc383d1f2d85f31f6ebd146e46085",
58 | "type": "github"
59 | },
60 | "original": {
61 | "owner": "numtide",
62 | "repo": "treefmt-nix",
63 | "type": "github"
64 | }
65 | }
66 | },
67 | "root": "root",
68 | "version": 7
69 | }
70 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "A tweaking tool for the COSMIC desktop";
3 |
4 | inputs = {
5 | flake-parts = {
6 | url = "github:hercules-ci/flake-parts";
7 | inputs.nixpkgs-lib.follows = "nixpkgs";
8 | };
9 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
10 | treefmt-nix = {
11 | url = "github:numtide/treefmt-nix";
12 | inputs.nixpkgs.follows = "nixpkgs";
13 | };
14 | };
15 |
16 | outputs =
17 | inputs:
18 | let
19 | inherit (inputs.nixpkgs) lib;
20 | in
21 | inputs.flake-parts.lib.mkFlake { inherit inputs; } {
22 | systems = [
23 | "aarch64-linux"
24 | "x86_64-linux"
25 | ];
26 |
27 | imports = lib.optionals (inputs.treefmt-nix ? flakeModule) [ inputs.treefmt-nix.flakeModule ];
28 |
29 | perSystem =
30 | { pkgs, self', ... }:
31 | {
32 | devShells.default = import ./shell.nix { inherit pkgs; };
33 |
34 | packages = {
35 | default = self'.packages.cosmic-ext-tweaks;
36 | cosmic-ext-tweaks = import ./. { inherit pkgs; };
37 | };
38 | }
39 | // lib.optionalAttrs (inputs.treefmt-nix ? flakeModule) {
40 | treefmt.config = {
41 | flakeCheck = true;
42 | projectRootFile = "flake.nix";
43 |
44 | programs = {
45 | nixfmt = {
46 | enable = true;
47 | package = pkgs.nixfmt-rfc-style;
48 | };
49 | rustfmt = {
50 | enable = true;
51 | package = pkgs.rustfmt;
52 | };
53 | };
54 | };
55 | };
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/i18n.toml:
--------------------------------------------------------------------------------
1 | fallback_language = "en"
2 |
3 | [fluent]
4 | assets_dir = "i18n"
--------------------------------------------------------------------------------
/i18n/en/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks for COSMIC™
2 | app-description = A tool to customize your COSMIC™ desktop experience.
3 |
4 | # Pages
5 | home = Home
6 | dock = Dock
7 | panel = Panel
8 | color-schemes = Color schemes
9 | layouts = Layouts
10 | shortcuts = Shortcuts
11 | snapshots = Snapshots
12 |
13 | create-layout = Create layout
14 | apply-layout = Apply layout
15 | delete-layout = Delete layout
16 | layout-name = Layout name
17 | layout-name-empty = Layout name cannot be empty.
18 | dock-icons = Dock icons
19 | show-window = Show window
20 | preview = Preview
21 | layout-exists-error = A layout with this name already exists.
22 | layout-name-required = Name is required.
23 | panel-properties = Panel properties
24 | panel-position = Panel position
25 | panel-size = Panel size
26 | dock-properties = Dock properties
27 | position = Position
28 | extend = Extend
29 | size = Size
30 | top = Top
31 | bottom = Bottom
32 | left = Left
33 | right = Right
34 | enable = Enable
35 | show = Show
36 |
37 | color-schemes-error = Error loading color schemes
38 | import-color-scheme = Import color scheme
39 | delete-color-scheme = Delete color scheme
40 | available-color-schemes-body = Find and install color schemes
41 | install-color-scheme = Install color scheme
42 | set-color-scheme = Set color scheme
43 | find-color-schemes = Find color schemes
44 | open-containing-folder = Open containing folder
45 | open-link = Open link
46 | installed = Installed
47 | available = Available
48 | loading = Loading...
49 | show-more = Show more
50 |
51 | show-panel = Show panel
52 | show-dock = Show dock
53 | force-icon-buttons-in-panel = Force icon buttons in panel
54 |
55 | padding = Padding
56 | padding-description = Padding is the space between the contents and the borders of the dock or panel.
57 |
58 | spacing = Spacing
59 | spacing-description = Spacing is the space between the icons in the dock or panel.
60 |
61 | save = Save
62 | cancel = Cancel
63 | close = Close
64 | create = Create
65 |
66 | navigation = Navigation
67 |
68 |
69 | save-current-layout = Save current layout
70 | save-current-layout-description = Save the current desktop layout for future use.
71 | save-current-color-scheme = Save current color scheme
72 | color-scheme-name = Color scheme name
73 | create-snapshot = Create snapshot
74 | create-snapshot-description = You are about to create a snapshot, this will save the current state of your desktop and make it possible to restore it later on.
75 | restore-snapshot = Restore snapshot
76 | delete-snapshot = Delete snapshot
77 | no-snapshots = No snapshots available
78 | snapshot-name = Snapshot name
79 | name = Name
80 | type = Type
81 | created = Created
82 | actions = Actions
83 | system = System
84 | user = User
85 |
86 | ## Snapshots
87 | application-opened = Application opened
88 |
89 | ## About
90 | about = About
91 | repository = Repository
92 | support = Support
93 | website = Website
94 |
95 | ## Settings
96 | settings = Settings
97 |
98 | ### Appearance
99 | appearance = Appearance
100 | theme = Theme
101 | match-desktop = Match desktop
102 | dark = Dark
103 | light = Light
104 |
105 | # Menu
106 | view = View
107 |
108 |
109 | ## Shortcuts
110 |
111 | warning = Warning: this will remove your existing custom shortcuts
112 | windows-desc = Super+Arrows to move windows. Ctrl+Alt+Arrows to navigate workspaces.
113 |
--------------------------------------------------------------------------------
/i18n/es-419/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks para COSMIC
2 | app-description = Una herramienta para personalizar la apariencia de COSMIC.
3 |
4 | # Pages
5 | home = Inicio
6 | dock = Dock
7 | panel = Panel
8 | layouts = Acomodos de escritorio
9 | color-schemes = Esquemas de color
10 | snapshots = Copia de seguridad
11 |
12 | color-schemes-error = Error al cargar los esquemas de color
13 | import-color-scheme = Importar esquema de color
14 | delete-color-scheme = Eliminar esquema de color
15 | available-color-schemes-body = Encuentra e instala esquemas de color
16 | install-color-scheme = Instalar esquema de color
17 | find-color-schemes = Buscar esquemas de color
18 | open-containing-folder = Abrir carpeta contenedora
19 | open-link = Abrir enlace
20 | installed = Instalado
21 | available = Disponible
22 | loading = Cargando...
23 | show-more = Mostrar mas
24 |
25 | show-panel = Mostrar panel
26 | force-icon-buttons-in-panel = Forzar iconos en el panel
27 |
28 | padding = Relleno
29 | padding-description = El relleno es el espacio entre los iconos y el borde del dock o panel.
30 |
31 | spacing = Espaciado
32 | spacing-description = El espacio es la distancia entre los iconos en el dock o panel.
33 |
34 | save = Guardar
35 | cancel = Cancelar
36 | close = Cerrar
37 | create = Crear
38 |
39 | navigation = Navegar
40 |
41 | save-current-color-scheme = Guardar esquema de color actual
42 | color-scheme-name = Nombre del esquema de color
43 | create-snapshot = Crear copia de seguridad
44 | create-snapshot-description = Está a punto de crear una copia de seguridad; esto guardará el estado actual de su escritorio y permitirá restaurarlo más adelante
45 | restore-snapshot = Restaurar copia de seguridad
46 | delete-snapshot = Eliminar copia de seguridad
47 | no-snapshots = No hay copias de seguridad disponibles
48 | snapshot-name = Nombre de la copia de seguridad
49 | name = Nombre
50 | type = Tipo
51 | created = Creado
52 | actions = Acciones
53 | system = Sistema
54 | user = Usuario
55 |
56 | ## Snapshots
57 | application-opened = Aplicación abierta
58 |
59 | ## About
60 | about = Acerca de
61 | repository = Repositorio
62 | support = Soporte
63 | website = Sitio web
64 |
65 | ## Settings
66 | settings = Ajustes
67 |
68 | ### Appearance
69 | appearance = Apariencia
70 | theme = Tema
71 | match-desktop = Igual al escritorio
72 | dark = Oscruro
73 | light = Claro
74 |
75 | # Menu
76 | view = Ver
77 |
--------------------------------------------------------------------------------
/i18n/es-MX/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks para COSMIC
2 | app-description = Una herramienta para personalizar la apariencia de COSMIC.
3 |
4 | # Pages
5 | home = Inicio
6 | dock = Dock
7 | panel = Panel
8 | layouts = Acomodos de escritorio
9 | color-schemes = Esquemas de color
10 | snapshots = Copia de seguridad
11 |
12 | color-schemes-error = Error al cargar los esquemas de color
13 | import-color-scheme = Importar esquema de color
14 | delete-color-scheme = Eliminar esquema de color
15 | available-color-schemes-body = Encuentra e instala esquemas de color
16 | install-color-scheme = Instalar esquema de color
17 | find-color-schemes = Buscar esquemas de color
18 | open-containing-folder = Abrir carpeta contenedora
19 | open-link = Abrir enlace
20 | installed = Instalado
21 | available = Disponible
22 | loading = Cargando...
23 | show-more = Mostrar mas
24 |
25 | show-panel = Mostrar panel
26 | force-icon-buttons-in-panel = Forzar iconos en el panel
27 |
28 | padding = Relleno
29 | padding-description = El relleno es el espacio entre los iconos y el borde del dock o panel.
30 |
31 | spacing = Espaciado
32 | spacing-description = El espacio es la distancia entre los iconos en el dock o panel.
33 |
34 | save = Guardar
35 | cancel = Cancelar
36 | close = Cerrar
37 | create = Crear
38 |
39 | navigation = Navegar
40 |
41 | save-current-color-scheme = Guardar esquema de color actual
42 | color-scheme-name = Nombre del esquema de color
43 | create-snapshot = Crear copia de seguridad
44 | create-snapshot-description = Está a punto de crear una copia de seguridad; esto guardará el estado actual de su escritorio y permitirá restaurarlo más adelante
45 | restore-snapshot = Restaurar copia de seguridad
46 | delete-snapshot = Eliminar copia de seguridad
47 | no-snapshots = No hay copias de seguridad disponibles
48 | snapshot-name = Nombre de la copia de seguridad
49 | name = Nombre
50 | type = Tipo
51 | created = Creado
52 | actions = Acciones
53 | system = Sistema
54 | user = Usuario
55 |
56 | ## Snapshots
57 | application-opened = Aplicación abierta
58 |
59 | ## About
60 | about = Acerca de
61 | repository = Repositorio
62 | support = Soporte
63 | website = Sitio web
64 |
65 | ## Settings
66 | settings = Ajustes
67 |
68 | ### Appearance
69 | appearance = Apariencia
70 | theme = Tema
71 | match-desktop = Igual al escritorio
72 | dark = Oscruro
73 | light = Claro
74 |
75 | # Menu
76 | view = Ver
77 |
--------------------------------------------------------------------------------
/i18n/es/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks para COSMIC
2 | app-description = Una herramienta para personalizar la apariencia de COSMIC.
3 |
4 | # Pages
5 | home = Inicio
6 | dock = Dock
7 | panel = Panel
8 | layouts = Acomodos de escritorio
9 | color-schemes = Esquemas de color
10 | snapshots = Copia de seguridad
11 |
12 | create-layout = Crear acomodo
13 | layout-name = Nombre del acomodo
14 | dock-icons = Iconos del dock
15 | show-window = Mostrar ventana
16 | preview = Vista previa
17 | layout-exists-error = Ya existe un acomodo con este nombre.
18 | layout-name-required = El nombre es obligatorio.
19 | panel-properties = Propiedades del panel
20 | dock-properties = Propiedades del dock
21 | position = Posición
22 | extend = Extender
23 | size = Tamaño
24 | top = Arriba
25 | bottom = Abajo
26 | left = Izquierda
27 | right = Derecha
28 | enable = Habilitar
29 |
30 | color-schemes-error = Error al cargar los esquemas de color
31 | import-color-scheme = Importar esquema de color
32 | delete-color-scheme = Eliminar esquema de color
33 | available-color-schemes-body = Encuentra e instala esquemas de color
34 | install-color-scheme = Instalar esquema de color
35 | find-color-schemes = Buscar esquemas de color
36 | open-containing-folder = Abrir carpeta contenedora
37 | open-link = Abrir enlace
38 | installed = Instalado
39 | available = Disponible
40 | loading = Cargando...
41 | show-more = Mostrar mas
42 |
43 | show-panel = Mostrar panel
44 | force-icon-buttons-in-panel = Forzar iconos en el panel
45 |
46 | padding = Relleno
47 | padding-description = El relleno es el espacio entre los iconos y el borde del dock o panel.
48 |
49 | spacing = Espaciado
50 | spacing-description = El espacio es la distancia entre los iconos en el dock o panel.
51 |
52 | save = Guardar
53 | cancel = Cancelar
54 | close = Cerrar
55 | create = Crear
56 |
57 | navigation = Navegar
58 |
59 | save-current-color-scheme = Guardar esquema de color actual
60 | color-scheme-name = Nombre del esquema de color
61 | create-snapshot = Crear copia de seguridad
62 | create-snapshot-description = Está a punto de crear una copia de seguridad; esto guardará el estado actual de su escritorio y permitirá restaurarlo más adelante
63 | restore-snapshot = Restaurar copia de seguridad
64 | delete-snapshot = Eliminar copia de seguridad
65 | no-snapshots = No hay copias de seguridad disponibles
66 | snapshot-name = Nombre de la copia de seguridad
67 | name = Nombre
68 | type = Tipo
69 | created = Creado
70 | actions = Acciones
71 | system = Sistema
72 | user = Usuario
73 |
74 | ## Snapshots
75 | application-opened = Aplicación abierta
76 |
77 | ## About
78 | about = Acerca de
79 | repository = Repositorio
80 | support = Soporte
81 | website = Sitio web
82 |
83 | ## Settings
84 | settings = Ajustes
85 |
86 | ### Appearance
87 | appearance = Apariencia
88 | theme = Tema
89 | match-desktop = Igual al escritorio
90 | dark = Oscruro
91 | light = Claro
92 |
93 | # Menu
94 | view = Ver
95 |
--------------------------------------------------------------------------------
/i18n/fr/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks pour COSMIC
2 | app-description = Un outil pour customizer votre COSMIC expérience.
3 |
4 | # Pages
5 | home = Acceuil
6 | dock = Dock
7 | panel = Panneau
8 | color-schemes = Thèmes
9 | layouts = Agencements
10 | shortcuts = Raccourcis clavier
11 | snapshots = Sauvegarde
12 |
13 | color-schemes-error = Erreur de chargement des thèmes
14 | import-color-scheme = Importer un thème
15 | delete-color-scheme = Supprimer un thème
16 | available-color-schemes-body = Trouver et installer des thèmes
17 | set-color-scheme = Activer le thème
18 | install-color-scheme = Installer un thème
19 | find-color-schemes = Trouver des thèmes
20 | open-containing-folder = Ouvrir le dossier
21 | open-link = Ouvrir le lien
22 | installed = Installés
23 | available = Disponibles
24 | loading = Chargement...
25 | show-more = Voir plus
26 |
27 | show-panel = Afficher le panneau
28 | force-icon-buttons-in-panel = Forcer l'affichage des boutons avec des icones dans le panneau
29 |
30 | padding = Écarts
31 | padding-description = L'écart entre le contenu et les bordures du dock et du panneau.
32 |
33 | spacing = Espacement
34 | spacing-description = L'espacement est l'espace entre les icônes dans le dock ou le panneau.
35 |
36 | save = Sauvegarder
37 | cancel = Annuler
38 | close = Fermer
39 | create = Créer
40 |
41 | navigation = Navigation
42 |
43 | save-current-color-scheme = Sauvegarder le thème actuel
44 | color-scheme-name = Thème
45 | create-snapshot = Créer une sauvegarde
46 | create-snapshot-description = Vous vous apprétez à créer une sauvegarde de l'état actuel de votre environnement pour vous permettre de le restaurer plus tard.
47 | restore-snapshot = Restaurer la sauvegarde
48 | delete-snapshot = Supprimer la savegarde
49 | no-snapshots = Pas de sauvegarde disponible
50 | snapshot-name = Nom de la sauvegarde
51 | name = Nom
52 | type = Type
53 | created = Créé
54 | actions = Actions
55 | system = Système
56 | user = Utilisateur
57 |
58 | ## Snapshots
59 | application-opened = Application ouvert
60 |
61 | ## About
62 | about = À propos
63 | repository = Dépôt de code
64 | support = Support
65 | website = Site Web
66 |
67 | ## Settings
68 | settings = Préférences
69 |
70 | ### Appearance
71 | appearance = Apparence
72 | theme = Thème
73 | match-desktop = Thème par défaut du système
74 | dark = Sombre
75 | light = Clair
76 |
77 | # Menu
78 | view = Vue
79 |
80 |
81 | ## Shortcuts
82 |
83 | warning = Attention: ceci supprimera vos raccourcis actuels
84 | windows-desc = Super+Flèche directionnelle pour bouger les fenêtres. Ctrl+Alt+Flèche directionnelle pour naviguer parmis les bureaux virtuels.
85 |
--------------------------------------------------------------------------------
/i18n/ja/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks for COSMIC
2 | app-description = COSMICデスクトップ体験のパーソナライゼーションツール
3 |
4 | # Pages
5 | home = ホーム
6 | dock = ドック
7 | panel = パネル
8 | color-schemes = カラースキーム
9 | layouts = レイアウト
10 | shortcuts = ショートカット
11 | snapshots = スナップショット
12 |
13 | color-schemes-error = カラースキームが読み込みませんでした
14 | import-color-scheme = カラースキームをインポート
15 | delete-color-scheme = カラースキームをエクスポート
16 | available-color-schemes-body = カラースキームを探してインストール…
17 | install-color-scheme = カラースキームをインストール
18 | find-color-schemes = カラースキームを探す
19 | open-containing-folder = 保存先
20 | open-link = リンクを開く
21 | installed = インストール済み
22 | available = Available
23 | loading = 読み込み中…
24 | show-more = さらに表示
25 |
26 | show-panel = パネルを表示
27 | force-icon-buttons-in-panel = 常にパネルでボタンを表示
28 |
29 | padding = 余白
30 | padding-description = 内容と枠線の間の空間です。
31 |
32 | spacing = 間隔
33 | spacing-description = アイコンの間の空間です。
34 |
35 | save = 保存
36 | cancel = キャンセル
37 | close = 閉じる
38 | create = 作成
39 |
40 | navigation = ナビゲーション
41 |
42 | save-current-color-scheme = 現代のカラースキームを保存
43 | color-scheme-name = カラースキームの名前
44 | create-snapshot = スナップショットを作成
45 | create-snapshot-description = スナップショットを作成しそうです。これは後でデスクトップの現代の状態に戻れるためのです。
46 | restore-snapshot = スナップショットに戻る
47 | delete-snapshot = スナップショットを削除
48 | no-snapshots = スナップショットはありません
49 | snapshot-name = スナップショットの名前
50 | name = 名前
51 | type = 種類
52 | created = 作成日
53 | actions = 行動
54 | system = システム
55 | user = ユーザー
56 |
57 | ## Snapshots
58 | application-opened = アプリケーションを開きました
59 |
60 | ## About
61 | about = Tweaks for COSMICについて
62 | repository = レポジトリー
63 | support = サポート
64 | website = ウェブサイト
65 |
66 | ## Settings
67 | settings = 設定
68 |
69 | ### Appearance
70 | appearance = 外観
71 | theme = テーマ
72 | match-desktop = システムに従う
73 | dark = ダーク
74 | light = ライト
75 |
76 | # Menu
77 | view = 表示
78 |
79 |
80 | ## Shortcuts
81 |
82 | warning = ご注意:これは現代のカスタムなショートカットを削除します。
83 | windows-desc = スーパーキー+矢印キーでウィンドウを移動します。コントロール+Alt+矢印キーでワークスペースを切り替えます。
84 |
--------------------------------------------------------------------------------
/i18n/nl/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks voor COSMIC™
2 | app-description = Een tool om uw COSMIC™ desktop te personaliseren.
3 |
4 | # Pages
5 | home = Startpagina
6 | dock = Dock
7 | panel = Paneel
8 | color-schemes = Kleurpaletten
9 | layouts = Layouts
10 | shortcuts = Sneltoetsen
11 | snapshots = Momentopnames
12 |
13 | color-schemes-error = Kon geen kleurpaletten laden
14 | import-color-scheme = Kleurpalet importeren
15 | delete-color-scheme = Kleurpalet verwijderen
16 | available-color-schemes-body = Naar beschikbare kleurpaletten zoeken
17 | install-color-scheme = Kleurpalet installeren
18 | set-color-scheme = Kleurpalet gebruiken
19 | find-color-schemes = Kleurpaletten zoeken
20 | open-containing-folder = Map openen
21 | open-link = Link openen
22 | installed = Geïnstalleerd
23 | available = Beschikbaar
24 | loading = Laden...
25 | show-more = Meer tonen
26 |
27 | show-panel = Paneel tonen
28 | force-icon-buttons-in-panel = Pictogramknoppen ipv tekstknoppen in het paneel forceren
29 |
30 | padding = Opvulling
31 | padding-description = De ruimte tussen de inhoud en de randen van de dock of het paneel aanpassen.
32 |
33 | spacing = Spatiëring
34 | spacing-description = De ruimte tussen de pictogrammen in de dock of het paneel aanpassen.
35 |
36 | save = Opslaan
37 | cancel = Annuleren
38 | close = Sluiten
39 | create = Aanmaken
40 |
41 | navigation = Navigatie
42 |
43 | save-current-color-scheme = Huidig kleurpalet opslaan
44 | color-scheme-name = Naam kleurpalet
45 | create-snapshot = Momentopname maken
46 | create-snapshot-description = U staat op het punt een momentopname te maken, hiermee wordt de huidige staat van uw COSMIC™ desktop opgeslagen zodat u die later kunt herstellen.
47 | restore-snapshot = Momentopname herstellen
48 | delete-snapshot = Momentopname verwijderen
49 | no-snapshots = Geen momentopnamen beschikbaar
50 | snapshot-name = Naam momentopname
51 | name = Naam
52 | type = Type
53 | created = Aangemaakt op
54 | actions = Acties
55 | system = Systeem
56 | user = Gebruiker
57 |
58 | ## Snapshots
59 | application-opened = Toepassing geopend
60 |
61 | ## About
62 | about = Over
63 | repository = Repository
64 | support = Ondersteuning
65 | website = Website
66 |
67 | ## Settings
68 | settings = Instellingen
69 |
70 | ### Appearance
71 | appearance = Weergave
72 | theme = Thema
73 | match-desktop = Systeemstandaard
74 | dark = Donker
75 | light = Licht
76 |
77 | # Menu
78 | view = Beeld
79 |
80 |
81 | ## Shortcuts
82 |
83 | warning = Waarschuwing: hierdoor worden al uw bestaande aangepaste sneltoetsen verwijderd
84 | windows-desc = Gebruik Super+Pijltjes om vensters te verplaatsen, en gebruik Ctrl+Alt+Pijltjes om tussen werkbladen te wisselen.
85 |
--------------------------------------------------------------------------------
/i18n/pt-BR/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Ajustes do COSMIC™
2 | app-description = Uma ferramenta para personalizar a sua experiência com o ambiente COSMIC™.
3 |
4 | # Pages
5 | home = Início
6 | dock = Dock
7 | panel = Painel
8 | color-schemes = Esquemas de cores
9 | layouts = Layouts
10 | shortcuts = Atalhos
11 | snapshots = Snapshots
12 |
13 | color-schemes-error = Erro ao carregar esquemas de cores
14 | import-color-scheme = Importar esquema de cores
15 | delete-color-scheme = Excluir esquema de cores
16 | available-color-schemes-body = Procurar e instalar esquemas de cores
17 | install-color-scheme = Instalar esquema de cores
18 | set-color-scheme = Definir esquema de cores
19 | find-color-schemes = Procurar esquemas de cores
20 | open-containing-folder = Abrir pasta
21 | open-link = Abrir link
22 | installed = Instalado
23 | available = Disponível
24 | loading = Carregando...
25 | show-more = Mostrar mais
26 |
27 | show-panel = Mostrar painel
28 | force-icon-buttons-in-panel = Forçar botões com ícones no painel
29 |
30 | padding = Preenchimento
31 | padding-description = O preenchimento é o espaço entre o conteúdo e as bordas da dock ou do painel.
32 |
33 | spacing = Espaçamento
34 | spacing-description = O espaçamento é o espaço entre os ícones na dock ou no painel.
35 |
36 | save = Salvar
37 | cancel = Cancelar
38 | close = Fechar
39 | create = Criar
40 |
41 | navigation = Navegação
42 |
43 | save-current-color-scheme = Salvar esquema de cores atual
44 | color-scheme-name = Nome do esquema de cores
45 | create-snapshot = Criar snapshot
46 | create-snapshot-description = Você está prestes a criar um snapshot, isso irá salvar o estado atual da sua área de trabalho e tornará possível restaurá-lo mais tarde.
47 | restore-snapshot = Restaurar snapshot
48 | delete-snapshot = Excluir snapshot
49 | no-snapshots = Nenhum snapshot disponível
50 | snapshot-name = Nome do snapshot
51 | name = Nome
52 | type = Tipo
53 | created = Criado em
54 | actions = Ações
55 | system = Sistema
56 | user = Usuário
57 |
58 | ## Snapshots
59 | application-opened = Aplicativo aberto
60 |
61 | ## About
62 | about = Sobre
63 | repository = Repositório
64 | support = Suporte
65 | website = Website
66 |
67 | ## Settings
68 | settings = Configurações
69 |
70 | ### Appearance
71 | appearance = Aparência
72 | theme = Tema
73 | match-desktop = Estilo do sistema
74 | dark = Estilo escuro
75 | light = Estilo claro
76 |
77 | # Menu
78 | view = Exibir
79 |
80 |
81 | ## Shortcuts
82 |
83 | warning = Aviso: isso removerá seus atalhos personalizados existentes
84 | windows-desc = Super+Setas para mover as janelas. Ctrl+Alt+Setas para navegar pelas áreas de trabalho.
85 |
--------------------------------------------------------------------------------
/i18n/ro/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Ajustări pentru COSMIC
2 | app-description = O unealtă pentru personalizarea experienței desktop COSMIC.
3 |
4 | # Pages
5 | home = Acasă
6 | dock = Dock
7 | panel = Panou
8 | color-schemes = Scheme de culori
9 | layouts = Aranjamente
10 | shortcuts = Scurtături
11 | snapshots = Copii de rezervă
12 |
13 | color-schemes-error = Eroare la încărcarea schemelor de culori
14 | import-color-scheme = Importă schemă de culori
15 | delete-color-scheme = Șterge schemă de culori
16 | available-color-schemes-body = Caută și instalează scheme de culori
17 | install-color-scheme = Instalează schemă de culori
18 | set-color-scheme = Setează schemă de culori
19 | find-color-schemes = Găsește scheme de culori
20 | open-containing-folder = Deschide folderul conținător
21 | open-link = Deschide link
22 | installed = Instalate
23 | available = Disponibile
24 | loading = Se încarcă...
25 | show-more = Afișează mai mult
26 |
27 | show-panel = Afișează panoul
28 | force-icon-buttons-in-panel = Forțează butoane cu pictogramă în panou
29 |
30 | padding = Margine interioară
31 | padding-description = Marginea interioară este spațiul dintre conținut și marginile dock-ului sau panoului.
32 |
33 | spacing = Spațiere
34 | spacing-description = Spațierea este distanța dintre pictogramele din dock sau panou.
35 |
36 | save = Salvează
37 | cancel = Anulează
38 | close = Închide
39 | create = Creează
40 |
41 | navigation = Navigare
42 |
43 | save-current-color-scheme = Salvează schema de culori curentă
44 | color-scheme-name = Nume schemă de culori
45 | create-snapshot = Creează copie de rezervă
46 | create-snapshot-description = Urmează să creezi o copie de rezervă. Acesta va salva starea actuală a desktopului și va permite restaurarea ulterioară.
47 | restore-snapshot = Restaurează copie de rezervă
48 | delete-snapshot = Șterge copie de rezervă
49 | no-snapshots = Nicio copie de rezervă disponibilă
50 | snapshot-name = Nume copie de rezervă
51 | name = Nume
52 | type = Tip
53 | created = Creat
54 | actions = Acțiuni
55 | system = Sistem
56 | user = Utilizator
57 |
58 | ## Snapshots
59 | application-opened = Aplicație deschisă
60 |
61 | ## About
62 | about = Despre
63 | repository = Repozitoriu
64 | support = Suport
65 | website = Site web
66 |
67 | ## Settings
68 | settings = Setări
69 |
70 | ### Appearance
71 | appearance = Aspect
72 | theme = Temă
73 | match-desktop = Potrivește cu desktopul
74 | dark = Întunecat
75 | light = Luminos
76 |
77 | # Menu
78 | view = Vizualizare
79 |
80 |
81 | ## Shortcuts
82 |
83 | warning = Atenție: aceasta va elimina scurtăturile personalizate existente
84 | windows-desc = Super+Săgeți pentru a muta ferestre. Ctrl+Alt+Săgeți pentru a naviga între spațiile de lucru.
85 |
--------------------------------------------------------------------------------
/i18n/sr-Cyrl/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Прилагођавања за COSMIC
2 | app-description = Алат за прилагођавање Вашег COSMIC искуства.
3 |
4 | # Pages
5 | home = Почетна
6 | dock = Док
7 | panel = Панел
8 | layouts = Распореди
9 | color-schemes = Палете
10 | snapshots = Снимци
11 |
12 | color-schemes-error = Грешка при учитавању палета
13 | import-color-scheme = Увези палету
14 | delete-color-scheme = Избриши палету
15 | delete-layout = Избриши распоред
16 | available-color-schemes-body = Пронађи и инсталирај палете.
17 | install-color-scheme = Инсталирај палету
18 | find-color-schemes = Пронађи палете
19 | open-containing-folder = Отвори фасциклу
20 | open-link = Отвори линк
21 | installed = Инсталирано
22 | available = Доступно
23 | loading = Учитавање...
24 | show-more = Прикажи још
25 | about = О програму
26 | find-themes = Пронађи теме
27 |
28 | # Panel only
29 | show-panel = Приказ панела
30 | force-icon-buttons-in-panel = Натерај приказ дугмади са иконицама на панелу
31 |
32 | padding = Пуњење
33 | padding-description = Пуњење је простор између садржаја и ивица дока или панела.
34 |
35 | spacing = Размак
36 | spacing-description = Размак је простор између иконица на доку или панелу.
37 |
38 | save = Сачувај
39 | cancel = Откажи
40 | close = Затвори
41 | create = Направи
42 |
43 | save-current-color-scheme = Сачувај тренутну палету
44 |
45 | save-current-layout = Сачувај тренутни распоред
46 |
47 | create-snapshot = Направи снимак
48 | create-snapshot-description = Управо ћете направити снимак. Ово ће сачувати тренутно стање Ваше радне површине и омогућити да га касније вратите.
49 | restore-snapshot = Врати снимак
50 | delete-snapshot = Обриши снимак
51 | no-snapshots = Нема доступних снимака
52 | snapshot-name = Име снимка
53 | name = Име
54 | type = Тип
55 | created = Направљен
56 | actions = Акције
57 | system = Систем
58 | user = Корисник
59 |
60 | color-scheme-name = Име палете
61 |
62 | ## About
63 | repository = Репозиторијум
64 | support = Подршка
65 | website = Сајт
66 |
67 | ## Settings
68 | settings = Подешавања
69 |
70 | ### Appearance
71 | appearance = Изглед
72 | theme = Тема
73 | match-desktop = Као систем
74 | dark = Тамна
75 | light = Светла
76 |
77 | # Menu
78 | view = Приказ
79 |
--------------------------------------------------------------------------------
/i18n/sr-Latn/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Prilagođavanja za COSMIC
2 | app-description = Alat za prilagođavanje Vašeg COSMIC iskustva.
3 |
4 | # Pages
5 | home = Početna
6 | dock = Dok
7 | panel = Panel
8 | layouts = Rasporedi
9 | color-schemes = Palete
10 | snapshots = Snimci
11 |
12 | color-schemes-error = Greška pri učitavanju paleta
13 | import-color-scheme = Uvezi paletu
14 | delete-color-scheme = Izbriši paletu
15 | delete-layout = Izbriši raspored
16 | available-color-schemes-body = Pronađi i instaliraj palete.
17 | install-color-scheme = Instaliraj paletu
18 | find-color-schemes = Pronađi palete
19 | open-containing-folder = Otvori fasciklu
20 | open-link = Otvori link
21 | installed = Instalirano
22 | available = Dostupno
23 | loading = Učitavanje...
24 | show-more = Prikaži još
25 | about = O programu
26 | find-themes = Pronađi teme
27 |
28 | # Panel only
29 | show-panel = Prikaz panela
30 | force-icon-buttons-in-panel = Nateraj prikaz dugmadi sa ikonicama na panelu
31 |
32 | padding = Punjenje
33 | padding-description = Punjenje je prostor između sadržaja i ivica doka ili panela.
34 |
35 | spacing = Razmak
36 | spacing-description = Razmak je prostor između ikonica na doku ili panelu.
37 |
38 | save = Sačuvaj
39 | cancel = Poništi
40 | close = Zatvori
41 | create = Napravi
42 |
43 | save-current-color-scheme = Sačuvaj trenutnu paletu
44 |
45 | save-current-layout = Sačuvaj trenutni raspored
46 |
47 | create-snapshot = Napravi snimak
48 | create-snapshot-description = Upravo ćete napraviti snimak. Ovo će sačuvati trenutno stanje Vaše radne površine i omogućiti da ga kasnije vratite.
49 | restore-snapshot = Vrati snimak
50 | delete-snapshot = Obriši snimak
51 | no-snapshots = Nema dostupnih snimaka
52 | snapshot-name = Ime snimka
53 | name = Ime
54 | type = Tip
55 | created = Napravljen
56 | actions = Akcije
57 | system = Sistem
58 | user = Korisnik
59 |
60 | color-scheme-name = Ime palete
61 |
62 | ## About
63 | repository = Repozitorijum
64 | support = Podrška
65 | website = Sajt
66 |
67 | ## Settings
68 | settings = Podešavanja
69 |
70 | ### Appearance
71 | appearance = Izgled
72 | theme = Tema
73 | match-desktop = Kao sistem
74 | dark = Tamna
75 | light = Svetla
76 |
77 | # Menu
78 | view = Prikaz
79 |
--------------------------------------------------------------------------------
/i18n/sv/cosmic_ext_tweaks.ftl:
--------------------------------------------------------------------------------
1 | app-title = Tweaks för COSMIC™
2 | app-description = Ett verktyg för att anpassa din COSMIC™ skrivbordsupplevelse.
3 |
4 | # Pages
5 | home = Hem
6 | dock = Docka
7 | panel = Panel
8 | color-schemes = Färgscheman
9 | layouts = Layouter
10 | shortcuts = Genvägar
11 | snapshots = Ögonblicksavbilder
12 |
13 | color-schemes-error = Ett fel uppstod när färgscheman skulle läsas in
14 | import-color-scheme = Importera färgschema
15 | delete-color-scheme = Ta bort färgschema
16 | available-color-schemes-body = Find and install color schemes
17 | install-color-scheme = Installera färgschema
18 | find-color-schemes = Hitta färgschema
19 | open-containing-folder = Öppna innehållande mapp
20 | open-link = Öppna länk
21 | installed = Installerat
22 | available = Tillgängligt
23 | loading = Laddar...
24 | show-more = Visa mer
25 |
26 | show-panel = Visa panel
27 | force-icon-buttons-in-panel = Forcera ikonknappar i panelen
28 |
29 | padding = Utfyllnad
30 | padding-description = Utfyllnad är utrymmet mellan innehållet och kanterna på dockan eller panelen.
31 |
32 | spacing = Utfyllnad
33 | spacing-description = Avståndet är utrymmet mellan ikonerna i dockan eller panelen.
34 |
35 | save = Spara
36 | cancel = Avbryt
37 | close = Stäng
38 | create = Skapa
39 |
40 | navigation = Navigering
41 |
42 | save-current-color-scheme = Spara nuvarande färgschema
43 | color-scheme-name = Färgschema namn
44 | create-snapshot = Skapa ögonblicksavbild
45 | create-snapshot-description = Du är på väg att skapa en ögonblicksavbild, detta kommer att spara det aktuella tillståndet av ditt skrivbord och göra det möjligt att återställa det senare.
46 | restore-snapshot = Återställ ögonblicksavbild
47 | delete-snapshot = Ta bort ögonblicksavbild
48 | no-snapshots = Inga ögonblicksavbilder tillgängliga
49 | snapshot-name = Namn på ögonblicksavbild
50 | name = Namn
51 | type = Typ
52 | created = Skapad
53 | actions = Åtgärder
54 | system = System
55 | user = Användare
56 |
57 | ## Ögonblicksavbilder
58 | application-opened = Program öppnat
59 |
60 | ## Om
61 | about = Om
62 | repository = Förråd
63 | support = Support
64 | website = Hemsida
65 |
66 | ## Inställningar
67 | settings = Inställningar
68 |
69 | ### Utseende
70 | appearance = Utseende
71 | theme = Tema
72 | match-desktop = Matcha skrivbord
73 | dark = Mörkt
74 | light = Ljust
75 |
76 | # Meny
77 | view = Visa
78 |
79 | ## Genvägar
80 |
81 | warning = Varning: detta kommer att ta bort dina existerande anpassade genvägar
82 | windows-desc = Super+Piltangenterna för att flytta fönster. Ctrl+Alt+Piltangenterna för att navigera bland dina arbetsytor.
83 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | name := 'cosmic-ext-tweaks'
2 | appid := 'dev.edfloreshz.CosmicTweaks'
3 |
4 | rootdir := ''
5 | prefix := '/usr'
6 |
7 | base-dir := absolute_path(clean(rootdir / prefix))
8 |
9 | bin-src := 'target' / 'release' / name
10 | bin-dst := base-dir / 'bin' / name
11 |
12 | desktop := appid + '.desktop'
13 | desktop-src := 'res' / desktop
14 | desktop-dst := clean(rootdir / prefix) / 'share' / 'applications' / desktop
15 |
16 | icons-src := 'res' / 'icons' / 'hicolor'
17 | icons-dst := clean(rootdir / prefix) / 'share' / 'icons' / 'hicolor'
18 |
19 | icon-svg-src := icons-src / 'scalable' / 'apps' / 'icon.svg'
20 | icon-svg-dst := icons-dst / 'scalable' / 'apps' / appid + '.svg'
21 |
22 | # Default recipe which runs `just build-release`
23 | default: build-release
24 |
25 | # Runs `cargo clean`
26 | clean:
27 | cargo clean
28 |
29 | # Removes vendored dependencies
30 | clean-vendor:
31 | rm -rf .cargo vendor vendor.tar
32 |
33 | # `cargo clean` and removes vendored dependencies
34 | clean-dist: clean clean-vendor
35 |
36 | # Compiles with debug profile
37 | build-debug *args:
38 | cargo build {{args}}
39 |
40 | # Compiles with release profile
41 | build-release *args: (build-debug '--release' args)
42 |
43 | # Compiles release profile with vendored dependencies
44 | build-vendored *args: vendor-extract (build-release '--frozen --offline' args)
45 |
46 | # Runs a clippy check
47 | check *args:
48 | cargo clippy --all-features {{args}} -- -W clippy::pedantic
49 |
50 | # Runs a clippy check with JSON message format
51 | check-json: (check '--message-format=json')
52 |
53 | # Run the application for testing purposes
54 | run *args:
55 | env RUST_BACKTRACE=full cargo run --release {{args}}
56 |
57 | # Installs files
58 | install:
59 | install -Dm0755 {{bin-src}} {{bin-dst}}
60 | install -Dm0644 res/app.desktop {{desktop-dst}}
61 | install -Dm0644 {{icon-svg-src}} {{icon-svg-dst}}
62 |
63 | # Uninstalls installed files
64 | uninstall:
65 | rm {{bin-dst}} {{desktop-dst}} {{icon-svg-dst}}
66 |
67 | # Vendor dependencies locally
68 | vendor:
69 | #!/usr/bin/env bash
70 | mkdir -p .cargo
71 | cargo vendor --sync Cargo.toml | head -n -1 > .cargo/config.toml
72 | echo 'directory = "vendor"' >> .cargo/config.toml
73 | echo >> .cargo/config.toml
74 | echo '[env]' >> .cargo/config.toml
75 | if [ -n "${SOURCE_DATE_EPOCH}" ]
76 | then
77 | source_date="$(date -d "@${SOURCE_DATE_EPOCH}" "+%Y-%m-%d")"
78 | echo "VERGEN_GIT_COMMIT_DATE = \"${source_date}\"" >> .cargo/config.toml
79 | fi
80 | if [ -n "${SOURCE_GIT_HASH}" ]
81 | then
82 | echo "VERGEN_GIT_SHA = \"${SOURCE_GIT_HASH}\"" >> .cargo/config.toml
83 | fi
84 | tar pcf vendor.tar .cargo vendor
85 | rm -rf .cargo vendor
86 |
87 | # Extracts vendored dependencies
88 | vendor-extract:
89 | rm -rf vendor
90 | tar pxf vendor.tar
91 |
--------------------------------------------------------------------------------
/res/app.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Tweaks for COSMIC
3 | Name[fr]=Tweaks pour COSMIC
4 | Name[pt]=Ajustes do COSMIC
5 | GenericName = COSMIC tweaks
6 | Comment=Tweaks your COSMIC desktop environment
7 | Comment[fr]=Améliorez votre environnement de bureau COSMIC
8 | Comment[pt]=Ajustes adicionais para seu ambiente de trabalho COSMIC
9 | Keywords=Folder;Manager
10 | Keywords[fr]=ajustement
11 | Keywords[pt]=layout;tweaks;melhorias;configurações
12 |
13 | Exec=cosmic-ext-tweaks
14 | Terminal=false
15 | Type=Application
16 | StartupNotify=true
17 | Icon=dev.edfloreshz.CosmicTweaks
18 | Categories=Utility;Settings;DesktopSettings
19 | OnlyShowIn=COSMIC
20 |
--------------------------------------------------------------------------------
/res/icons/bundled/arrow-circular-bottom-right-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/arrow-into-box-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/checkmark-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/cross-small-square-filled-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/dark-mode-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/dock-bottom-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/dock-top-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/document-save-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/icons/bundled/edit-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/face-smile-big-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/folder-download-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/res/icons/bundled/info-outline-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/keyboard-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/icons/bundled/list-add-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/res/icons/bundled/plus-square-filled-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/recycling-bin-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/resize-mode-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/search-global-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/selection-mode-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/icons/bundled/settings-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/size-horizontally-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/snapshots-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/step-in-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/symbolic-link-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/tabs-stack-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/icons/bundled/user-trash-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/res/icons/bundled/view-coverflow-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/icons/hicolor/scalable/apps/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
266 |
--------------------------------------------------------------------------------
/res/layouts/cosmic.ron:
--------------------------------------------------------------------------------
1 | (
2 | id: "8d286ae7-5e7c-443d-b5ca-ddb791922e67",
3 | name: "Cosmic",
4 | custom: false,
5 | schema: Panel((
6 | panel_config: (
7 | config_list: [
8 | (
9 | name: "Panel",
10 | anchor: Top,
11 | anchor_gap: false,
12 | layer: Top,
13 | keyboard_interactivity: OnDemand,
14 | size: XS,
15 | output: All,
16 | background: ThemeDefault,
17 | plugins_wings: Some(([
18 | "com.system76.CosmicPanelWorkspacesButton",
19 | "com.system76.CosmicPanelAppButton",
20 | ], [
21 | "com.system76.CosmicAppletInputSources",
22 | "com.system76.CosmicAppletStatusArea",
23 | "com.system76.CosmicAppletTiling",
24 | "com.system76.CosmicAppletAudio",
25 | "com.system76.CosmicAppletNetwork",
26 | "com.system76.CosmicAppletBattery",
27 | "com.system76.CosmicAppletNotifications",
28 | "com.system76.CosmicAppletBluetooth",
29 | "com.system76.CosmicAppletPower",
30 | ])),
31 | plugins_center: Some([
32 | "com.system76.CosmicAppletTime",
33 | ]),
34 | size_wings: None,
35 | size_center: None,
36 | expand_to_edges: true,
37 | padding: 0,
38 | spacing: 2,
39 | border_radius: 0,
40 | exclusive_zone: true,
41 | autohide: None,
42 | margin: 0,
43 | opacity: 1.0,
44 | ),
45 | (
46 | name: "Dock",
47 | anchor: Bottom,
48 | anchor_gap: false,
49 | layer: Top,
50 | keyboard_interactivity: OnDemand,
51 | size: L,
52 | output: All,
53 | background: ThemeDefault,
54 | plugins_wings: None,
55 | plugins_center: Some([
56 | "com.system76.CosmicPanelLauncherButton",
57 | "com.system76.CosmicPanelWorkspacesButton",
58 | "com.system76.CosmicPanelAppButton",
59 | "com.system76.CosmicAppList",
60 | "com.system76.CosmicAppletMinimize",
61 | ]),
62 | size_wings: None,
63 | size_center: None,
64 | expand_to_edges: true,
65 | padding: 0,
66 | spacing: 4,
67 | border_radius: 0,
68 | exclusive_zone: true,
69 | autohide: None,
70 | margin: 0,
71 | opacity: 1.0,
72 | ),
73 | ],
74 | ),
75 | panel_config_version: 1,
76 | panel_button_config: (
77 | configs: {
78 | "Dock": (
79 | force_presentation: Some(Icon),
80 | ),
81 | "Panel": (
82 | force_presentation: None,
83 | ),
84 | },
85 | ),
86 | panel_button_config_version: 1,
87 | )),
88 | preview: (
89 | panel: (
90 | position: Top,
91 | extend: true,
92 | hidden: false,
93 | size: 10,
94 | ),
95 | dock: (
96 | position: Bottom,
97 | extend: true,
98 | hidden: false,
99 | size: 20,
100 | ),
101 | dock_icons: 6,
102 | show_window: true,
103 | )
104 | )
105 |
--------------------------------------------------------------------------------
/res/layouts/mac.ron:
--------------------------------------------------------------------------------
1 | (
2 | id: "9727f9f3-240a-4a9d-8459-3b007056cfeb",
3 | name: "Mac",
4 | custom: false,
5 | schema: Panel((
6 | panel_config: (
7 | config_list: [
8 | (
9 | name: "Panel",
10 | anchor: Top,
11 | anchor_gap: false,
12 | layer: Top,
13 | keyboard_interactivity: OnDemand,
14 | size: XS,
15 | output: All,
16 | background: ThemeDefault,
17 | plugins_wings: Some(([
18 | "com.system76.CosmicAppletPower",
19 | ], [
20 | "com.system76.CosmicAppletStatusArea",
21 | "com.system76.CosmicAppletBattery",
22 | "com.system76.CosmicAppletNetwork",
23 | "com.system76.CosmicPanelLauncherButton",
24 | "com.system76.CosmicAppletNotifications",
25 | "com.system76.CosmicAppletTime",
26 | ])),
27 | plugins_center: Some([]),
28 | size_wings: None,
29 | size_center: None,
30 | expand_to_edges: true,
31 | padding: 0,
32 | spacing: 4,
33 | border_radius: 0,
34 | exclusive_zone: true,
35 | autohide: None,
36 | margin: 0,
37 | opacity: 0.9,
38 | ),
39 | (
40 | name: "Dock",
41 | anchor: Bottom,
42 | anchor_gap: true,
43 | layer: Top,
44 | keyboard_interactivity: OnDemand,
45 | size: S,
46 | output: All,
47 | background: ThemeDefault,
48 | plugins_wings: Some(([], [])),
49 | plugins_center: Some([
50 | "com.system76.CosmicAppList",
51 | "com.system76.CosmicPanelAppButton",
52 | "com.system76.CosmicAppletMinimize",
53 | ]),
54 | size_wings: None,
55 | size_center: None,
56 | expand_to_edges: false,
57 | padding: 0,
58 | spacing: 0,
59 | border_radius: 8,
60 | exclusive_zone: true,
61 | autohide: None,
62 | margin: 4,
63 | opacity: 0.9,
64 | ),
65 | ],
66 | ),
67 | panel_config_version: 1,
68 | panel_button_config: (
69 | configs: {
70 | "Dock": (
71 | force_presentation: Some(Icon),
72 | ),
73 | "Panel": (
74 | force_presentation: Some(Icon),
75 | ),
76 | },
77 | ),
78 | panel_button_config_version: 1,
79 | )),
80 | preview: (
81 | panel: (
82 | position: Top,
83 | extend: true,
84 | hidden: false,
85 | size: 10,
86 | ),
87 | dock: (
88 | position: Bottom,
89 | extend: false,
90 | hidden: false,
91 | size: 20,
92 | ),
93 | dock_icons: 6,
94 | show_window: true,
95 | )
96 | )
97 |
--------------------------------------------------------------------------------
/res/layouts/ubuntu.ron:
--------------------------------------------------------------------------------
1 | (
2 | id: "e4af4098-aad2-4b07-afd0-9ac540874558",
3 | name: "Ubuntu",
4 | custom: false,
5 | schema: Panel((
6 | panel_config: (
7 | config_list: [
8 | (
9 | name: "Panel",
10 | anchor: Top,
11 | anchor_gap: false,
12 | layer: Top,
13 | keyboard_interactivity: OnDemand,
14 | size: XS,
15 | output: All,
16 | background: ThemeDefault,
17 | plugins_wings: Some(([
18 | "com.system76.CosmicPanelWorkspacesButton",
19 | "com.system76.CosmicPanelAppButton",
20 | ], [
21 | "com.system76.CosmicAppletInputSources",
22 | "com.system76.CosmicAppletStatusArea",
23 | "com.system76.CosmicAppletTiling",
24 | "com.system76.CosmicAppletAudio",
25 | "com.system76.CosmicAppletNetwork",
26 | "com.system76.CosmicAppletBattery",
27 | "com.system76.CosmicAppletNotifications",
28 | "com.system76.CosmicAppletBluetooth",
29 | "com.system76.CosmicAppletPower",
30 | ])),
31 | plugins_center: Some([
32 | "com.system76.CosmicAppletTime",
33 | ]),
34 | size_wings: None,
35 | size_center: None,
36 | expand_to_edges: true,
37 | padding: 0,
38 | spacing: 2,
39 | border_radius: 0,
40 | exclusive_zone: true,
41 | autohide: None,
42 | margin: 0,
43 | opacity: 1.0,
44 | ),
45 | (
46 | name: "Dock",
47 | anchor: Left,
48 | anchor_gap: false,
49 | layer: Top,
50 | keyboard_interactivity: OnDemand,
51 | size: M,
52 | output: All,
53 | background: ThemeDefault,
54 | plugins_wings: Some(([
55 | "com.system76.CosmicPanelLauncherButton",
56 | "com.system76.CosmicPanelWorkspacesButton",
57 | "com.system76.CosmicPanelAppButton",
58 | "com.system76.CosmicAppList",
59 | "com.system76.CosmicAppletMinimize",
60 | ], [])),
61 | plugins_center: Some([]),
62 | size_wings: None,
63 | size_center: None,
64 | expand_to_edges: true,
65 | padding: 0,
66 | spacing: 4,
67 | border_radius: 0,
68 | exclusive_zone: true,
69 | autohide: None,
70 | margin: 0,
71 | opacity: 1.0,
72 | ),
73 | ],
74 | ),
75 | panel_config_version: 1,
76 | panel_button_config: (
77 | configs: {
78 | "Dock": (
79 | force_presentation: Some(Icon),
80 | ),
81 | "Panel": (
82 | force_presentation: None,
83 | ),
84 | },
85 | ),
86 | panel_button_config_version: 1,
87 | )),
88 | preview: (
89 | panel: (
90 | position: Top,
91 | extend: true,
92 | hidden: false,
93 | size: 10,
94 | ),
95 | dock: (
96 | position: Left,
97 | extend: true,
98 | hidden: false,
99 | size: 20,
100 | ),
101 | dock_icons: 3,
102 | show_window: true,
103 | )
104 | )
105 |
--------------------------------------------------------------------------------
/res/layouts/windows.ron:
--------------------------------------------------------------------------------
1 | (
2 | id: "24162674-cbeb-442b-9e7a-4f31b9a89d7a",
3 | name: "Windows",
4 | custom: false,
5 | schema: Panel((
6 | panel_config: (
7 | config_list: [
8 | (
9 | name: "Panel",
10 | anchor: Bottom,
11 | anchor_gap: false,
12 | layer: Top,
13 | keyboard_interactivity: OnDemand,
14 | size: S,
15 | output: All,
16 | background: ThemeDefault,
17 | plugins_wings: Some(([
18 | "com.system76.CosmicAppletPower",
19 | ], [
20 | "com.system76.CosmicAppletStatusArea",
21 | "com.system76.CosmicAppletNetwork",
22 | "com.system76.CosmicAppletAudio",
23 | "com.system76.CosmicAppletBattery",
24 | "com.system76.CosmicAppletTime",
25 | "com.system76.CosmicAppletNotifications",
26 | ])),
27 | plugins_center: Some([
28 | "com.system76.CosmicPanelAppButton",
29 | "com.system76.CosmicPanelLauncherButton",
30 | "com.system76.CosmicAppList",
31 | ]),
32 | size_wings: None,
33 | size_center: None,
34 | expand_to_edges: true,
35 | padding: 0,
36 | spacing: 4,
37 | border_radius: 0,
38 | exclusive_zone: true,
39 | autohide: None,
40 | margin: 0,
41 | opacity: 1.0,
42 | ),
43 | ],
44 | ),
45 | panel_config_version: 1,
46 | panel_button_config: (
47 | configs: {
48 | "Panel": (
49 | force_presentation: Some(Icon),
50 | ),
51 | "Dock": (
52 | force_presentation: Some(Icon),
53 | ),
54 | },
55 | ),
56 | panel_button_config_version: 1,
57 | )),
58 | preview: (
59 | panel: (
60 | position: Top,
61 | extend: true,
62 | hidden: true,
63 | size: 15,
64 | ),
65 | dock: (
66 | position: Bottom,
67 | extend: true,
68 | hidden: false,
69 | size: 15,
70 | ),
71 | dock_icons: 6,
72 | show_window: true,
73 | )
74 | )
75 |
--------------------------------------------------------------------------------
/res/metainfo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | dev.edfloreshz.CosmicTweaks
4 | Tweaks
5 | Beyond the limits of your desktop
6 | CC0-1.0
7 | GPL-3.0-only
8 | COSMIC
9 |
10 | edfloreshz
11 |
12 | edfloreshz@proton.me
13 | https://github.com/cosmic-utils/tweaks
14 | https://github.com/cosmic-utils/tweaks/issues
15 | https://github.com/cosmic-utils/tweaks
16 | https://ko-fi.com/edfloreshz
17 | https://edfloreshz.dev
18 | https://github.com/cosmic-utils/tweaks
19 | https://github.com/cosmic-utils/tweaks/tree/main/i18n
20 |
21 |
22 | Personalize your COSMIC desktop beyond infinity
23 |
24 | Save, import and download color schemes
25 | Customize the panel and dock
26 | Replicate other desktop's layouts
27 | Fearless tweaking with snapshots
28 |
29 |
30 |
31 | #8affe1
32 | #007d5c
33 |
34 | dev.edfloreshz.CosmicTweaks.desktop
35 | https://raw.githubusercontent.com/cosmic-utils/tweaks/master/res/icons/hicolor/scalable/apps/icon.svg
36 |
37 |
38 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/color-schemes-light.png
39 | Color schemes page with light mode
40 |
41 |
42 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/color-schemes-dark.png
43 | Color schemes page with dark mode
44 |
45 |
46 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/dock-light.png
47 | Dock page with light mode
48 |
49 |
50 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/dock-dark.png
51 | Dock page with dark mode
52 |
53 |
54 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/panel-light.png
55 | Panel page with light mode
56 |
57 |
58 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/panel-dark.png
59 | Panel page with dark mode
60 |
61 |
62 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/layouts-light.png
63 | Layouts page with light mode
64 |
65 |
66 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/layouts-dark.png
67 | Layouts page with dark mode
68 |
69 |
70 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/snapshots-light.png
71 | Snapshots page with light mode
72 |
73 |
74 | https://raw.githubusercontent.com/cosmic-utils/tweaks/main/res/screenshots/snapshots-dark.png
75 | Snapshots page with dark mode
76 |
77 |
78 |
79 | 360
80 |
81 |
82 | always
83 |
84 |
85 | keyboard
86 | pointing
87 |
88 |
89 | Utility
90 |
91 |
92 | com.system76.CosmicApplication
93 |
94 |
95 |
96 |
97 | Snapshots and toolkit improvements 🚀
98 | Tweak without worries with snapshots, a simple way to save the state of your desktop and restore it later on, everytime the app opens a snapshot is created in case you need to go back to a previous state.
99 |
100 | Create snapshots of your own
101 | Includes a revamped about page
102 | Borders have been added to the window
103 |
104 |
105 |
106 |
107 |
108 | Layouts are here!
109 |
110 | Apply pre-installed layouts to your desktop
111 | Save your own desktop layouts
112 | Available themes are now displayed in a grid
113 |
114 |
115 |
116 |
117 |
118 | Improved user experience
119 |
120 | Available themes are now shown on the side
121 | Added settings and about page
122 |
123 |
124 |
125 |
126 |
127 | Initial release! 🎉
128 |
129 | Save current color scheme
130 | Import existing color schemes
131 | Download color schemes from cosmic-themes.org
132 | Customize the panel and dock
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/res/screenshots/color-schemes-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/color-schemes-dark.png
--------------------------------------------------------------------------------
/res/screenshots/color-schemes-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/color-schemes-light.png
--------------------------------------------------------------------------------
/res/screenshots/dock-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/dock-dark.png
--------------------------------------------------------------------------------
/res/screenshots/dock-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/dock-light.png
--------------------------------------------------------------------------------
/res/screenshots/layouts-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/layouts-dark.png
--------------------------------------------------------------------------------
/res/screenshots/layouts-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/layouts-light.png
--------------------------------------------------------------------------------
/res/screenshots/panel-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/panel-dark.png
--------------------------------------------------------------------------------
/res/screenshots/panel-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/panel-light.png
--------------------------------------------------------------------------------
/res/screenshots/snapshots-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/snapshots-dark.png
--------------------------------------------------------------------------------
/res/screenshots/snapshots-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmic-utils/tweaks/7d056810df046e73afe1f938c3e50318adf66120/res/screenshots/snapshots-light.png
--------------------------------------------------------------------------------
/res/shortcuts/windows.ron:
--------------------------------------------------------------------------------
1 | {
2 | (
3 | modifiers: [
4 | Super,
5 | ],
6 | key: "Left",
7 | ): Move(Left),
8 | (
9 | modifiers: [
10 | Super,
11 | Shift,
12 | ],
13 | key: "Down",
14 | ): Focus(Down),
15 | (
16 | modifiers: [
17 | Ctrl,
18 | Alt,
19 | Shift,
20 | ],
21 | key: "Right",
22 | ): MoveToNextWorkspace,
23 | (
24 | modifiers: [
25 | Super,
26 | ],
27 | key: "Up",
28 | ): Move(Up),
29 | (
30 | modifiers: [
31 | Super,
32 | Shift,
33 | ],
34 | key: "Up",
35 | ): Focus(Up),
36 | (
37 | modifiers: [
38 | Ctrl,
39 | Alt,
40 | Shift,
41 | ],
42 | key: "Left",
43 | ): MoveToPreviousWorkspace,
44 | (
45 | modifiers: [
46 | Super,
47 | ],
48 | key: "Right",
49 | ): Move(Right),
50 | (
51 | modifiers: [
52 | Ctrl,
53 | Alt,
54 | ],
55 | key: "Left",
56 | ): PreviousWorkspace,
57 | (
58 | modifiers: [
59 | Super,
60 | ],
61 | key: "Down",
62 | ): Move(Down),
63 | (
64 | modifiers: [
65 | Ctrl,
66 | Alt,
67 | ],
68 | key: "Right",
69 | ): NextWorkspace,
70 | (
71 | modifiers: [
72 | Super,
73 | Shift,
74 | ],
75 | key: "Left",
76 | ): Focus(Left),
77 | (
78 | modifiers: [
79 | Super,
80 | Shift,
81 | ],
82 | key: "Right",
83 | ): Focus(Right),
84 | }
--------------------------------------------------------------------------------
/shell.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs ? import { },
3 | ...
4 | }:
5 | pkgs.mkShell {
6 | strictDeps = true;
7 |
8 | nativeBuildInputs = with pkgs; [
9 | cargo
10 | clippy
11 | just
12 | rustc
13 | rustfmt
14 | ];
15 | }
16 |
--------------------------------------------------------------------------------
/src/app.rs:
--------------------------------------------------------------------------------
1 | use action::TweaksAction;
2 | use context::ContextPage;
3 | use cosmic::{
4 | app::{self, context_drawer::ContextDrawer, Task},
5 | iced, widget, Application, Core, Element,
6 | };
7 | use std::collections::{HashMap, VecDeque};
8 |
9 | use dialog::DialogPage;
10 | use flags::Flags;
11 | use message::Message;
12 | use crate::app::pages::color_schemes::config::ColorScheme;
13 |
14 | pub mod action;
15 | pub mod context;
16 | pub mod context_drawer;
17 | pub mod dialog;
18 | pub mod flags;
19 | pub mod footer;
20 | pub mod header;
21 | pub mod init;
22 | pub mod message;
23 | pub mod nav;
24 | pub mod page;
25 | pub mod subscription;
26 | pub mod update;
27 | pub mod view;
28 | pub mod pages;
29 | pub mod core;
30 |
31 | pub struct App {
32 | cosmic: Cosmic,
33 | handler: cosmic::cosmic_config::Config,
34 | config: core::config::TweaksConfig,
35 | color_schemes: pages::ColorSchemes,
36 | dock: pages::Dock,
37 | panel: pages::Panel,
38 | layouts: pages::Layouts,
39 | snapshots: pages::Snapshots,
40 | shortcuts: pages::Shortcuts,
41 | }
42 |
43 | pub struct Cosmic {
44 | core: Core,
45 | nav_model: widget::segmented_button::SingleSelectModel,
46 | about: widget::about::About,
47 | dialog_pages: VecDeque,
48 | dialog_text_input: widget::Id,
49 | key_binds: HashMap,
50 | modifiers: iced::keyboard::Modifiers,
51 | context_page: ContextPage,
52 | app_themes: Vec,
53 | }
54 |
55 | impl Application for App {
56 | type Executor = cosmic::executor::Default;
57 |
58 | type Flags = Flags;
59 |
60 | type Message = Message;
61 |
62 | const APP_ID: &'static str = "dev.edfloreshz.CosmicTweaks";
63 |
64 | fn core(&self) -> &Core {
65 | &self.cosmic.core
66 | }
67 |
68 | fn core_mut(&mut self) -> &mut Core {
69 | &mut self.cosmic.core
70 | }
71 |
72 | fn init(core: Core, flags: Self::Flags) -> (Self, app::Task) {
73 | Cosmic::init(core, flags)
74 | }
75 |
76 | fn header_start(&self) -> Vec> {
77 | Cosmic::header_start(self)
78 | }
79 |
80 | fn nav_model(&self) -> Option<&widget::nav_bar::Model> {
81 | Some(&self.cosmic.nav_model)
82 | }
83 |
84 | fn on_nav_select(&mut self, id: widget::nav_bar::Id) -> app::Task {
85 | Cosmic::on_nav_select(self, id)
86 | }
87 |
88 | fn context_drawer(&self) -> Option> {
89 | Cosmic::context_drawer(self)
90 | }
91 |
92 | fn dialog(&self) -> Option> {
93 | Cosmic::dialog(self)
94 | }
95 |
96 | fn view(&self) -> Element {
97 | Cosmic::view(self)
98 | }
99 |
100 | fn footer(&self) -> Option> {
101 | Cosmic::footer(self)
102 | }
103 |
104 | fn update(&mut self, message: Self::Message) -> app::Task {
105 | Cosmic::update(self, message)
106 | }
107 |
108 | fn subscription(&self) -> cosmic::iced::Subscription {
109 | Cosmic::subscription()
110 | }
111 | }
112 |
113 | impl App {
114 | fn update_config(&mut self) -> Task {
115 | self.color_schemes.theme_builder = ColorScheme::current_theme();
116 | Task::batch(vec![cosmic::command::set_theme(
117 | self.config.app_theme.theme(),
118 | )])
119 | }
120 |
121 | fn settings(&self) -> Element {
122 | let app_theme_selected = match self.config.app_theme {
123 | core::config::AppTheme::Dark => 1,
124 | core::config::AppTheme::Light => 2,
125 | core::config::AppTheme::System => 0,
126 | };
127 | widget::settings::view_column(vec![widget::settings::section()
128 | .title(crate::fl!("appearance"))
129 | .add(
130 | widget::settings::item::builder(crate::fl!("theme")).control(widget::dropdown(
131 | &self.cosmic.app_themes,
132 | Some(app_theme_selected),
133 | Message::AppTheme,
134 | )),
135 | )
136 | .into()])
137 | .into()
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/app/action.rs:
--------------------------------------------------------------------------------
1 | use super::{context::ContextPage, Message};
2 |
3 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
4 | pub enum TweaksAction {
5 | About,
6 | Settings,
7 | }
8 |
9 | impl cosmic::widget::menu::Action for TweaksAction {
10 | type Message = Message;
11 | fn message(&self) -> Self::Message {
12 | match self {
13 | TweaksAction::About => Message::ToggleContextPage(ContextPage::About),
14 | TweaksAction::Settings => Message::ToggleContextPage(ContextPage::Settings),
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/context.rs:
--------------------------------------------------------------------------------
1 | use crate::fl;
2 |
3 | #[derive(Debug, Clone, Eq, PartialEq)]
4 | pub enum ContextPage {
5 | Settings,
6 | About,
7 | }
8 |
9 | impl ContextPage {
10 | pub fn title(&self) -> String {
11 | match self {
12 | Self::About => fl!("about"),
13 | Self::Settings => fl!("settings"),
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/context_drawer.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | app::{self, context_drawer::ContextDrawer},
3 | Application,
4 | };
5 |
6 | use crate::app::context::ContextPage;
7 | use crate::app::message::Message;
8 | use crate::app::App;
9 |
10 | use super::Cosmic;
11 |
12 | impl Cosmic {
13 | pub fn context_drawer(app: &App) -> Option> {
14 | if !app.core().window.show_context {
15 | return None;
16 | }
17 |
18 | Some(match app.cosmic.context_page {
19 | ContextPage::About => app::context_drawer::about(
20 | &app.cosmic.about,
21 | Message::Open,
22 | Message::ToggleContextDrawer,
23 | ),
24 | ContextPage::Settings => {
25 | app::context_drawer::context_drawer(app.settings(), Message::ToggleContextDrawer)
26 | .title(app.cosmic.context_page.title())
27 | }
28 | })
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/core/config.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, Config, CosmicConfigEntry},
3 | theme, Application,
4 | };
5 | use serde::{Deserialize, Serialize};
6 |
7 | use crate::app::App;
8 |
9 | pub const CONFIG_VERSION: u64 = 1;
10 |
11 | #[derive(Clone, Default, Debug, Eq, PartialEq, Deserialize, Serialize, CosmicConfigEntry)]
12 | pub struct TweaksConfig {
13 | pub app_theme: AppTheme,
14 | }
15 |
16 | impl TweaksConfig {
17 | pub fn config() -> Config {
18 | match Config::new(App::APP_ID, CONFIG_VERSION) {
19 | Ok(config) => config,
20 | Err(err) => panic!("Failed to fetch config for application: {err}"),
21 | }
22 | }
23 |
24 | pub fn new() -> TweaksConfig {
25 | TweaksConfig::get_entry(&Self::config()).unwrap_or_else(|(errs, config)| {
26 | log::info!("errors loading config: {:?}", errs);
27 | config
28 | })
29 | }
30 | }
31 |
32 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
33 | pub enum AppTheme {
34 | Dark,
35 | Light,
36 | #[default]
37 | System,
38 | }
39 |
40 | impl AppTheme {
41 | pub fn theme(&self) -> theme::Theme {
42 | match self {
43 | Self::Dark => {
44 | let mut t = theme::system_dark();
45 | t.theme_type.prefer_dark(Some(true));
46 | t
47 | }
48 | Self::Light => {
49 | let mut t = theme::system_light();
50 | t.theme_type.prefer_dark(Some(false));
51 | t
52 | }
53 | Self::System => theme::system_preference(),
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/app/core/error.rs:
--------------------------------------------------------------------------------
1 | use thiserror::Error;
2 |
3 | #[derive(Debug, Error)]
4 | pub enum Error {
5 | #[error("IO error: {0}")]
6 | Io(#[from] std::io::Error),
7 | #[error("RON error: {0}")]
8 | Ron(#[from] ron::error::SpannedError),
9 | #[error("Iced error: {0}")]
10 | Iced(#[from] cosmic::iced::Error),
11 | #[error("Theme path not found")]
12 | ThemePathNotFound,
13 | #[error("Layout path not found")]
14 | LayoutPathNotFound,
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/core/grid.rs:
--------------------------------------------------------------------------------
1 | pub struct GridMetrics {
2 | pub cols: usize,
3 | pub item_width: usize,
4 | pub column_spacing: u16,
5 | }
6 |
7 | impl GridMetrics {
8 | pub fn new(width: usize, min_width: usize, column_spacing: u16) -> Self {
9 | let width_m1 = width.checked_sub(min_width).unwrap_or(0);
10 | let cols_m1 = width_m1 / (min_width + column_spacing as usize);
11 | let cols = cols_m1 + 1;
12 | let item_width = width
13 | .checked_sub(cols_m1 * column_spacing as usize)
14 | .unwrap_or(0)
15 | .checked_div(cols)
16 | .unwrap_or(0);
17 | Self {
18 | cols,
19 | item_width,
20 | column_spacing,
21 | }
22 | }
23 |
24 | pub fn custom(spacing: &cosmic::cosmic_theme::Spacing, width: usize) -> Self {
25 | Self::new(width, 240 + 2 * spacing.space_s as usize, spacing.space_xxs)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/core/icons.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-only
2 |
3 | use cosmic::widget::icon;
4 | use std::collections::HashMap;
5 | use std::sync::{Mutex, OnceLock};
6 |
7 | pub(crate) static ICON_CACHE: OnceLock> = OnceLock::new();
8 |
9 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
10 | pub struct IconCacheKey {
11 | name: &'static str,
12 | size: u16,
13 | }
14 |
15 | pub struct IconCache {
16 | cache: HashMap,
17 | }
18 |
19 | impl IconCache {
20 | pub fn new() -> Self {
21 | let mut cache = HashMap::new();
22 |
23 | macro_rules! bundle {
24 | ($name:expr, $size:expr) => {
25 | let data: &'static [u8] =
26 | include_bytes!(concat!("../../../res/icons/bundled/", $name, ".svg"));
27 | cache.insert(
28 | IconCacheKey {
29 | name: $name,
30 | size: $size,
31 | },
32 | icon::from_svg_bytes(data).symbolic(true),
33 | );
34 | };
35 | }
36 |
37 | // Menu items
38 | bundle!("cross-small-square-filled-symbolic", 14);
39 | bundle!("edit-symbolic", 14);
40 | bundle!("face-smile-big-symbolic", 14);
41 | bundle!("plus-square-filled-symbolic", 14);
42 | bundle!("settings-symbolic", 14);
43 | bundle!("tabs-stack-symbolic", 14);
44 | bundle!("info-outline-symbolic", 14);
45 | bundle!("keyboard-symbolic", 18);
46 |
47 | bundle!("size-horizontally-symbolic", 18);
48 | bundle!("dock-bottom-symbolic", 18);
49 | bundle!("dock-top-symbolic", 18);
50 | bundle!("dark-mode-symbolic", 18);
51 | bundle!("resize-mode-symbolic", 18);
52 | bundle!("view-coverflow-symbolic", 18);
53 | bundle!("snapshots-symbolic", 18);
54 | bundle!("checkmark-symbolic", 16);
55 | bundle!("recycling-bin-symbolic", 16);
56 | bundle!("arrow-into-box-symbolic", 16);
57 | bundle!("document-save-symbolic", 16);
58 | bundle!("search-global-symbolic", 16);
59 | bundle!("list-add-symbolic", 16);
60 | bundle!("symbolic-link-symbolic", 14);
61 | bundle!("user-trash-symbolic", 14);
62 | bundle!("selection-mode-symbolic", 14);
63 | bundle!("folder-download-symbolic", 14);
64 | bundle!("arrow-circular-bottom-right-symbolic", 14);
65 |
66 | Self { cache }
67 | }
68 |
69 | pub fn get(&mut self, name: &'static str, size: u16) -> icon::Icon {
70 | let handle = self
71 | .cache
72 | .entry(IconCacheKey { name, size })
73 | .or_insert_with(|| icon::from_name(name).size(size).handle())
74 | .clone();
75 | icon::icon(handle).size(size)
76 | }
77 |
78 | pub fn get_handle(&mut self, name: &'static str, size: u16) -> icon::Handle {
79 | let handle = self
80 | .cache
81 | .entry(IconCacheKey { name, size })
82 | .or_insert_with(|| icon::from_name(name).size(size).handle())
83 | .clone();
84 | handle
85 | }
86 | }
87 |
88 | pub fn get_icon(name: &'static str, size: u16) -> icon::Icon {
89 | let mut icon_cache = ICON_CACHE.get().unwrap().lock().unwrap();
90 | icon_cache.get(name, size)
91 | }
92 |
93 | pub fn get_handle(name: &'static str, size: u16) -> icon::Handle {
94 | let mut icon_cache = ICON_CACHE.get().unwrap().lock().unwrap();
95 | icon_cache.get_handle(name, size)
96 | }
97 |
--------------------------------------------------------------------------------
/src/app/core/key_bindings.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use cosmic::iced::keyboard::Key;
4 | use cosmic::widget::menu::key_bind::KeyBind;
5 | use cosmic::widget::menu::key_bind::Modifier;
6 |
7 | use crate::app::action::TweaksAction;
8 |
9 | pub struct KeyBindings;
10 |
11 | impl KeyBindings {
12 | pub fn new() -> HashMap {
13 | let mut key_binds = HashMap::new();
14 |
15 | macro_rules! bind {
16 | ([$($modifier:ident),* $(,)?], $key:expr, $action:ident) => {{
17 | key_binds.insert(
18 | KeyBind {
19 | modifiers: vec![$(Modifier::$modifier),*],
20 | key: $key,
21 | },
22 | TweaksAction::$action,
23 | );
24 | }};
25 | }
26 |
27 | bind!([Ctrl], Key::Character(",".into()), Settings);
28 | bind!([Ctrl], Key::Character("i".into()), About);
29 |
30 | key_binds
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/core/localize.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-only
2 |
3 | use std::sync::LazyLock;
4 |
5 | use i18n_embed::{
6 | fluent::{fluent_language_loader, FluentLanguageLoader},
7 | DefaultLocalizer, LanguageLoader, Localizer,
8 | };
9 | use rust_embed::RustEmbed;
10 |
11 | #[derive(RustEmbed)]
12 | #[folder = "i18n/"]
13 | struct Localizations;
14 |
15 | pub static LANGUAGE_LOADER: LazyLock = LazyLock::new(|| {
16 | let loader: FluentLanguageLoader = fluent_language_loader!();
17 |
18 | loader
19 | .load_fallback_language(&Localizations)
20 | .expect("Error while loading fallback language");
21 |
22 | loader
23 | });
24 |
25 | #[macro_export]
26 | macro_rules! fl {
27 | ($message_id:literal) => {{
28 | i18n_embed_fl::fl!($crate::app::core::localize::LANGUAGE_LOADER, $message_id)
29 | }};
30 |
31 | ($message_id:literal, $($args:expr),*) => {{
32 | i18n_embed_fl::fl!($crate::app::core::localize::LANGUAGE_LOADER, $message_id, $($args), *)
33 | }};
34 | }
35 |
36 | // Get the `Localizer` to be used for localizing this library.
37 | pub fn localizer() -> Box {
38 | Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations))
39 | }
40 |
41 | pub fn localize() {
42 | let localizer = localizer();
43 | let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages();
44 |
45 | if let Err(error) = localizer.select(&requested_languages) {
46 | eprintln!("Error while loading language for App List {}", error);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/core/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod config;
2 | pub mod error;
3 | pub mod grid;
4 | pub mod icons;
5 | pub mod key_bindings;
6 | pub mod localize;
7 | pub mod settings;
8 | pub mod style;
9 |
--------------------------------------------------------------------------------
/src/app/core/settings.rs:
--------------------------------------------------------------------------------
1 | use crate::app::flags::Flags;
2 |
3 | use super::icons::{IconCache, ICON_CACHE};
4 | use std::sync::Mutex;
5 | use crate::app::core::localize;
6 |
7 | pub fn settings() -> cosmic::app::Settings {
8 | cosmic::app::Settings::default().size_limits(
9 | cosmic::iced::Limits::NONE
10 | .min_width(360.0)
11 | .min_height(180.0),
12 | )
13 | }
14 |
15 | pub fn flags() -> Flags {
16 | Flags {
17 | handler: crate::app::core::config::TweaksConfig::config(),
18 | config: crate::app::core::config::TweaksConfig::new(),
19 | }
20 | }
21 |
22 | pub fn init() -> Result<(), crate::Error> {
23 | ICON_CACHE.get_or_init(|| Mutex::new(IconCache::new()));
24 | localize::localize();
25 | std::env::set_var("RUST_LOG", "cosmic_ext_tweaks=info");
26 | pretty_env_logger::init();
27 | crate::app::pages::layouts::Layouts::init()
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/dialog.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{widget, Element};
2 |
3 | use crate::app::message::Message;
4 | use crate::app::pages::layouts::dialog::{CreateLayoutDialog, PanelType};
5 | use crate::app::App;
6 |
7 | use super::Cosmic;
8 | use crate::fl;
9 |
10 | #[derive(Clone, Debug)]
11 | pub enum DialogPage {
12 | SaveCurrentColorScheme(String),
13 | CreateSnapshot(String),
14 | CreateLayout(CreateLayoutDialog),
15 | }
16 |
17 | impl Cosmic {
18 | pub fn dialog(app: &App) -> Option> {
19 | let spacing = cosmic::theme::spacing();
20 | let dialog_page = match app.cosmic.dialog_pages.front() {
21 | Some(some) => some,
22 | None => return None,
23 | };
24 |
25 | let dialog = match dialog_page {
26 | DialogPage::SaveCurrentColorScheme(name) => widget::dialog()
27 | .title(fl!("save-current-color-scheme"))
28 | .primary_action(
29 | widget::button::suggested(fl!("save"))
30 | .on_press_maybe(Some(Message::DialogComplete)),
31 | )
32 | .secondary_action(
33 | widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
34 | )
35 | .control(
36 | widget::column()
37 | .push(widget::text::body(fl!("color-scheme-name")))
38 | .push(
39 | widget::text_input("", name.as_str())
40 | .id(app.cosmic.dialog_text_input.clone())
41 | .on_input(move |name| {
42 | Message::DialogUpdate(DialogPage::SaveCurrentColorScheme(name))
43 | })
44 | .on_submit(|_| Message::DialogComplete),
45 | )
46 | .spacing(spacing.space_xxs),
47 | ),
48 | DialogPage::CreateSnapshot(name) => widget::dialog()
49 | .title(fl!("create-snapshot"))
50 | .body(fl!("create-snapshot-description"))
51 | .primary_action(
52 | widget::button::standard(fl!("create")).on_press(Message::DialogComplete),
53 | )
54 | .secondary_action(
55 | widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
56 | )
57 | .control(
58 | widget::text_input(fl!("snapshot-name"), name.as_str())
59 | .id(app.cosmic.dialog_text_input.clone())
60 | .on_input(move |name| {
61 | Message::DialogUpdate(DialogPage::CreateSnapshot(name))
62 | })
63 | .on_submit(|_| Message::DialogComplete),
64 | ),
65 | DialogPage::CreateLayout(dialog) => {
66 | let CreateLayoutDialog {
67 | name,
68 | preview,
69 | error,
70 | } = dialog;
71 | let preview_view = preview.view::(&spacing, 130);
72 |
73 | let name_input = widget::text_input(fl!("layout-name"), name)
74 | .id(app.cosmic.dialog_text_input.clone())
75 | .on_input(move |name| {
76 | Message::DialogUpdate(DialogPage::CreateLayout(CreateLayoutDialog::new(
77 | name.clone(),
78 | preview.clone(),
79 | error.clone(),
80 | )))
81 | })
82 | .on_submit(|_| Message::DialogComplete);
83 |
84 | widget::dialog()
85 | .width(700)
86 | .title(fl!("save-current-layout"))
87 | .body(fl!("save-current-layout-description"))
88 | .primary_action(widget::button::suggested(fl!("create")).on_press(
89 | if name.is_empty() {
90 | Message::DialogUpdate(DialogPage::CreateLayout(
91 | CreateLayoutDialog::new(
92 | name.clone(),
93 | preview.clone(),
94 | Some(fl!("layout-name-empty")),
95 | ),
96 | ))
97 | } else {
98 | Message::DialogComplete
99 | },
100 | ))
101 | .secondary_action(
102 | widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
103 | )
104 | .control(
105 | widget::column()
106 | .push(preview_view)
107 | .push(
108 | widget::column()
109 | .push(name_input)
110 | .push_maybe(if let Some(error) = error {
111 | Some(
112 | widget::text::caption(error.to_string())
113 | .class(cosmic::style::Text::Accent),
114 | )
115 | } else {
116 | None
117 | })
118 | .push(
119 | widget::scrollable(
120 | widget::column()
121 | .push(dialog.section(
122 | PanelType::Panel,
123 | &app.layouts.panel_model,
124 | ))
125 | .push(dialog.section(
126 | PanelType::Dock,
127 | &app.layouts.dock_model,
128 | )),
129 | )
130 | .height(300),
131 | )
132 | .padding(spacing.space_s)
133 | .spacing(spacing.space_m),
134 | )
135 | .spacing(spacing.space_m),
136 | )
137 | }
138 | };
139 |
140 | Some(dialog.into())
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/app/flags.rs:
--------------------------------------------------------------------------------
1 | use cosmic::cosmic_config::Config;
2 |
3 | use crate::app::core::config::TweaksConfig;
4 |
5 | #[derive(Clone, Debug)]
6 | pub struct Flags {
7 | pub handler: Config,
8 | pub config: TweaksConfig,
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/footer.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | widget::{self},
3 | Apply, Element,
4 | };
5 |
6 | use crate::app::{dialog::DialogPage, App};
7 | use crate::app::{message::Message, pages::layouts::dialog::CreateLayoutDialog};
8 | use crate::app::{page::Page, pages};
9 |
10 | use super::Cosmic;
11 | use crate::app::core::icons;
12 | use crate::app::pages::{
13 | color_schemes::{self, Status, Tab},
14 | layouts::preview::LayoutPreview,
15 | };
16 | use crate::fl;
17 |
18 | impl Cosmic {
19 | pub fn footer(app: &App) -> Option> {
20 | let spacing = cosmic::theme::spacing();
21 |
22 | match app.cosmic.nav_model.active_data::() {
23 | Some(Page::ColorSchemes) => match app.color_schemes.model.active_data::() {
24 | Some(Tab::Installed) => Some(
25 | widget::row()
26 | .push(widget::horizontal_space())
27 | .push(
28 | widget::button::standard(fl!("save-current-color-scheme"))
29 | .trailing_icon(icons::get_handle("arrow-into-box-symbolic", 16))
30 | .spacing(spacing.space_xs)
31 | .on_press(Message::ColorSchemes(Box::new(
32 | color_schemes::Message::SaveCurrentColorScheme(None),
33 | ))),
34 | )
35 | .push(
36 | widget::button::standard(fl!("import-color-scheme"))
37 | .trailing_icon(icons::get_handle("document-save-symbolic", 16))
38 | .spacing(spacing.space_xs)
39 | .on_press(Message::ColorSchemes(Box::new(
40 | color_schemes::Message::StartImport,
41 | ))),
42 | )
43 | .spacing(spacing.space_xxs)
44 | .apply(widget::container)
45 | .class(cosmic::style::Container::Card)
46 | .padding(spacing.space_xxs)
47 | .into(),
48 | ),
49 | Some(Tab::Available) => Some(
50 | widget::row()
51 | .push(widget::horizontal_space())
52 | .push(match app.color_schemes.status {
53 | Status::Idle => widget::button::standard(fl!("show-more"))
54 | .leading_icon(crate::app::core::icons::get_handle(
55 | "content-loading-symbolic",
56 | 16,
57 | ))
58 | .on_press(Message::ColorSchemes(Box::new(
59 | color_schemes::Message::FetchAvailableColorSchemes(
60 | color_schemes::ColorSchemeProvider::CosmicThemes,
61 | app.color_schemes.limit,
62 | ),
63 | ))),
64 | Status::LoadingMore | Status::Loading => {
65 | widget::button::standard(fl!("loading"))
66 | }
67 | })
68 | .spacing(spacing.space_xxs)
69 | .apply(widget::container)
70 | .class(cosmic::style::Container::Card)
71 | .padding(spacing.space_xxs)
72 | .into(),
73 | ),
74 | None => None,
75 | },
76 | Some(Page::Layouts) => Some(
77 | widget::row()
78 | .push(widget::horizontal_space())
79 | .push(
80 | widget::button::standard(fl!("save-current-layout"))
81 | .trailing_icon(icons::get_handle("arrow-into-box-symbolic", 16))
82 | .spacing(spacing.space_xs)
83 | .on_press(Message::ToggleDialogPage(DialogPage::CreateLayout(
84 | CreateLayoutDialog::new(
85 | String::new(),
86 | LayoutPreview::default(),
87 | None,
88 | ),
89 | ))),
90 | )
91 | .push_maybe(app.layouts.selected_layout.as_ref().map(|_| {
92 | widget::button::standard(fl!("apply-layout"))
93 | .trailing_icon(icons::get_handle("checkmark-symbolic", 16))
94 | .spacing(spacing.space_xs)
95 | .on_press(Message::Layouts(pages::layouts::Message::Apply))
96 | }))
97 | .push_maybe(app.layouts.selected_layout.as_ref().and_then(|selected| {
98 | if selected.custom {
99 | Some(
100 | widget::button::standard(fl!("delete-layout"))
101 | .trailing_icon(icons::get_handle("recycling-bin-symbolic", 16))
102 | .spacing(spacing.space_xs)
103 | .on_press(Message::Layouts(pages::layouts::Message::Delete)),
104 | )
105 | } else {
106 | None
107 | }
108 | }))
109 | .spacing(spacing.space_xxs)
110 | .apply(widget::container)
111 | .class(cosmic::style::Container::Card)
112 | .padding(spacing.space_xxs)
113 | .into(),
114 | ),
115 | Some(Page::Snapshots) => Some(
116 | widget::row()
117 | .push(widget::horizontal_space())
118 | .push(
119 | widget::button::standard(fl!("create-snapshot"))
120 | .trailing_icon(icons::get_handle("list-add-symbolic", 16))
121 | .spacing(spacing.space_xs)
122 | .on_press(Message::ToggleDialogPage(DialogPage::CreateSnapshot(
123 | String::new(),
124 | ))),
125 | )
126 | .spacing(spacing.space_xxs)
127 | .apply(widget::container)
128 | .class(cosmic::style::Container::Card)
129 | .padding(spacing.space_xxs)
130 | .into(),
131 | ),
132 | _ => None,
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/app/header.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | widget::menu::{self, ItemHeight, ItemWidth},
3 | Element,
4 | };
5 |
6 | use crate::app::action::TweaksAction;
7 | use crate::app::message::Message;
8 | use crate::app::App;
9 |
10 | use crate::fl;
11 | use crate::app::core::icons;
12 | use super::Cosmic;
13 |
14 | impl Cosmic {
15 | pub fn header_start(app: &App) -> Vec> {
16 | let menu_bar = menu::bar(vec![menu::Tree::with_children(
17 | menu::root(fl!("view")),
18 | menu::items(
19 | &app.cosmic.key_binds,
20 | vec![
21 | menu::Item::Button(
22 | fl!("settings"),
23 | Some(icons::get_handle("settings-symbolic", 14)),
24 | TweaksAction::Settings,
25 | ),
26 | menu::Item::Divider,
27 | menu::Item::Button(
28 | fl!("about"),
29 | Some(icons::get_handle("info-outline-symbolic", 14)),
30 | TweaksAction::About,
31 | ),
32 | ],
33 | ),
34 | )])
35 | .item_height(ItemHeight::Dynamic(40))
36 | .item_width(ItemWidth::Uniform(240))
37 | .spacing(4.0);
38 |
39 | vec![menu_bar.into()]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/app/init.rs:
--------------------------------------------------------------------------------
1 | use std::collections::VecDeque;
2 |
3 | use cosmic::{
4 | app::{self, Core},
5 | iced::keyboard::Modifiers,
6 | widget::{self, about::About},
7 | Application, ApplicationExt, Task,
8 | };
9 |
10 | use crate::app::flags::Flags;
11 | use crate::app::message::Message;
12 | use crate::app::page::Page;
13 | use crate::app::App;
14 | use crate::app::{context::ContextPage, pages::snapshots::config::SnapshotKind};
15 |
16 | use super::Cosmic;
17 | use crate::app::core::key_bindings::KeyBindings;
18 | use crate::app::pages::{
19 | self,
20 | color_schemes::{self, ColorSchemes},
21 | dock::Dock,
22 | layouts::Layouts,
23 | panel::Panel,
24 | shortcuts::Shortcuts,
25 | snapshots::Snapshots,
26 | };
27 | use crate::fl;
28 |
29 | impl Cosmic {
30 | pub fn init(core: Core, flags: Flags) -> (App, app::Task) {
31 | log::info!("Starting Cosmic Tweak Tool...");
32 |
33 | let mut nav_model = widget::segmented_button::SingleSelectModel::default();
34 | for &nav_page in Page::all() {
35 | let id = nav_model
36 | .insert()
37 | .icon(nav_page.icon())
38 | .text(nav_page.title())
39 | .data::(nav_page)
40 | .id();
41 |
42 | if nav_page == Page::default() {
43 | nav_model.activate(id);
44 | }
45 | }
46 |
47 | let about = About::default()
48 | .name(fl!("app-title"))
49 | .icon(App::APP_ID)
50 | .version("0.1.3")
51 | .author("Eduardo Flores")
52 | .license("GPL-3.0-only")
53 | .links([
54 | (
55 | fl!("support"),
56 | "https://github.com/cosmic-utils/tweaks/issues",
57 | ),
58 | (fl!("repository"), "https://github.com/cosmic-utils/tweaks"),
59 | ])
60 | .developers([("Eduardo Flores", "edfloreshz@proton.me")]);
61 |
62 | let mut app = App {
63 | cosmic: Cosmic {
64 | core,
65 | nav_model,
66 | about,
67 | dialog_pages: VecDeque::new(),
68 | dialog_text_input: widget::Id::unique(),
69 | key_binds: KeyBindings::new(),
70 | modifiers: Modifiers::empty(),
71 | context_page: ContextPage::About,
72 | app_themes: vec![fl!("match-desktop"), fl!("dark"), fl!("light")],
73 | },
74 | handler: flags.handler,
75 | config: flags.config,
76 | color_schemes: ColorSchemes::default(),
77 | layouts: Layouts::default(),
78 | dock: Dock::default(),
79 | panel: Panel::default(),
80 | snapshots: Snapshots::default(),
81 | shortcuts: Shortcuts::new(),
82 | };
83 |
84 | let mut tasks = vec![
85 | app.update(Message::ColorSchemes(Box::new(
86 | color_schemes::Message::FetchAvailableColorSchemes(
87 | color_schemes::ColorSchemeProvider::CosmicThemes,
88 | app.color_schemes.limit,
89 | ),
90 | ))),
91 | app.update(Message::Snapshots(
92 | pages::snapshots::Message::CreateSnapshot(
93 | fl!("application-opened"),
94 | SnapshotKind::System,
95 | ),
96 | )),
97 | ];
98 |
99 | match pages::layouts::config::Layout::list() {
100 | Ok(list) => {
101 | tasks.push(app.update(Message::Layouts(pages::layouts::Message::LoadLayouts(list))))
102 | }
103 | Err(error) => log::error!("Failed to load layouts: {}", error),
104 | }
105 |
106 | tasks.push(app.set_window_title(fl!("app-title")));
107 |
108 | (app, Task::batch(tasks))
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/app/message.rs:
--------------------------------------------------------------------------------
1 | use crate::app::pages::{self, layouts::preview::LayoutPreview};
2 |
3 | use super::{context::ContextPage, dialog::DialogPage};
4 | use cosmic::{
5 | iced::keyboard::{Key, Modifiers},
6 | widget,
7 | };
8 |
9 | #[derive(Debug, Clone)]
10 | pub enum Message {
11 | Dock(pages::dock::Message),
12 | Panel(pages::panel::Message),
13 | Layouts(pages::layouts::Message),
14 | Shortcuts(pages::shortcuts::Message),
15 | Snapshots(pages::snapshots::Message),
16 | ColorSchemes(Box),
17 | UpdatePanelLayoutPosition(widget::segmented_button::Entity, String, LayoutPreview),
18 | UpdateDockLayoutPosition(widget::segmented_button::Entity, String, LayoutPreview),
19 | DialogUpdate(DialogPage),
20 | DialogComplete,
21 | DialogCancel,
22 | SaveNewColorScheme(String),
23 | ToggleContextPage(ContextPage),
24 | ToggleContextDrawer,
25 | ToggleDialogPage(DialogPage),
26 | AppTheme(usize),
27 | Key(Modifiers, Key),
28 | Modifiers(Modifiers),
29 | SystemThemeModeChange,
30 | Open(String),
31 | }
32 |
--------------------------------------------------------------------------------
/src/app/nav.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | app::{self},
3 | widget::{self},
4 | ApplicationExt, Task,
5 | };
6 |
7 | use crate::app::message::Message;
8 | use crate::app::page::Page;
9 | use crate::app::App;
10 | use crate::fl;
11 |
12 | use super::Cosmic;
13 |
14 | impl Cosmic {
15 | pub fn on_nav_select(app: &mut App, id: widget::nav_bar::Id) -> app::Task {
16 | app.cosmic.nav_model.activate(id);
17 |
18 | let title = if let Some(page) = app.cosmic.nav_model.data::(id) {
19 | format!("{} - {}", page.title(), fl!("app-title"))
20 | } else {
21 | fl!("app-title")
22 | };
23 |
24 | Task::batch(vec![app.set_window_title(title)])
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/page.rs:
--------------------------------------------------------------------------------
1 | use cosmic::widget::Icon;
2 |
3 | use crate::fl;
4 |
5 | use crate::app::core::icons;
6 |
7 | #[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
8 | pub enum Page {
9 | #[default]
10 | ColorSchemes,
11 | Dock,
12 | Panel,
13 | Layouts,
14 | Shortcuts,
15 | Snapshots,
16 | }
17 |
18 | impl Default for &Page {
19 | fn default() -> Self {
20 | &Page::ColorSchemes
21 | }
22 | }
23 |
24 | impl Page {
25 | pub fn title(&self) -> String {
26 | match self {
27 | Self::ColorSchemes => fl!("color-schemes"),
28 | Self::Dock => fl!("dock"),
29 | Self::Panel => fl!("panel"),
30 | Self::Layouts => fl!("layouts"),
31 | Self::Shortcuts => fl!("shortcuts"),
32 | Self::Snapshots => fl!("snapshots"),
33 | }
34 | }
35 |
36 | pub fn icon(&self) -> Icon {
37 | match self {
38 | Self::ColorSchemes => icons::get_icon("dark-mode-symbolic", 18),
39 | Self::Dock => icons::get_icon("dock-bottom-symbolic", 18),
40 | Self::Panel => icons::get_icon("dock-top-symbolic", 18),
41 | Self::Layouts => icons::get_icon("view-coverflow-symbolic", 18),
42 | Self::Shortcuts => icons::get_icon("keyboard-symbolic", 18),
43 | Self::Snapshots => icons::get_icon("snapshots-symbolic", 18),
44 | }
45 | }
46 |
47 | pub fn all() -> &'static [Self] {
48 | &[
49 | Self::ColorSchemes,
50 | Self::Dock,
51 | Self::Panel,
52 | Self::Layouts,
53 | Self::Shortcuts,
54 | Self::Snapshots,
55 | ]
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/app/pages/color_schemes/config.rs:
--------------------------------------------------------------------------------
1 | use std::path::PathBuf;
2 |
3 | use cosmic::{
4 | cosmic_config::{self, Config},
5 | cosmic_theme::{ThemeBuilder, ThemeMode},
6 | };
7 | use cosmic_config::cosmic_config_derive::CosmicConfigEntry;
8 | use cosmic_config::CosmicConfigEntry;
9 | use serde::{Deserialize, Serialize};
10 |
11 | use crate::Error;
12 |
13 | const CONFIG_ID: &str = "dev.edfloreshz.CosmicTweaks.ColorScheme";
14 |
15 | #[derive(Debug, Serialize, Clone, Default, Deserialize, PartialEq, CosmicConfigEntry)]
16 | #[version = 1]
17 | pub struct ColorScheme {
18 | pub name: String,
19 | pub path: Option,
20 | pub link: Option,
21 | pub author: Option,
22 | pub theme: ThemeBuilder,
23 | }
24 |
25 | impl ColorScheme {
26 | #[allow(dead_code)]
27 | pub const fn version() -> u64 {
28 | Self::VERSION
29 | }
30 |
31 | pub fn config() -> Config {
32 | match Config::new(CONFIG_ID, Self::VERSION) {
33 | Ok(config) => config,
34 | Err(err) => panic!("Failed to load config: {}", err),
35 | }
36 | }
37 |
38 | pub fn read_theme(&self) -> Result {
39 | let Some(path) = self.path.as_ref() else {
40 | return Err(Error::ThemePathNotFound);
41 | };
42 |
43 | let file = std::fs::read_to_string(path)?;
44 | let theme: ThemeBuilder = ron::from_str(&file)?;
45 | Ok(theme)
46 | }
47 |
48 | pub fn current_theme() -> ThemeBuilder {
49 | let theme_mode_config = ThemeMode::config().ok();
50 | let theme_mode = theme_mode_config
51 | .as_ref()
52 | .map(|c| match ThemeMode::get_entry(c) {
53 | Ok(t) => t,
54 | Err((errors, t)) => {
55 | for e in errors {
56 | log::error!("{e}");
57 | }
58 | t
59 | }
60 | })
61 | .unwrap_or_default();
62 | let theme_builder_config = if theme_mode.is_dark {
63 | ThemeBuilder::dark_config()
64 | } else {
65 | ThemeBuilder::light_config()
66 | }
67 | .ok();
68 |
69 | theme_builder_config.as_ref().map_or_else(
70 | || {
71 | if theme_mode.is_dark {
72 | ThemeBuilder::dark()
73 | } else {
74 | ThemeBuilder::light()
75 | }
76 | },
77 | |c| match ThemeBuilder::get_entry(c) {
78 | Ok(t) => t,
79 | Err((errors, t)) => {
80 | for e in errors {
81 | log::error!("{e}");
82 | }
83 | t
84 | }
85 | },
86 | )
87 | }
88 |
89 | pub fn installed() -> Result, Error> {
90 | let mut color_schemes = vec![];
91 |
92 | let xdg_data_home = std::env::var("XDG_DATA_HOME")
93 | .ok()
94 | .and_then(|value| {
95 | if value.is_empty() {
96 | None
97 | } else {
98 | Some(PathBuf::from(value))
99 | }
100 | })
101 | .or_else(dirs::data_local_dir)
102 | .map(|dir| dir.join("themes/cosmic"));
103 |
104 | if let Some(ref xdg_data_home) = xdg_data_home {
105 | if !xdg_data_home.exists() {
106 | if let Err(e) = std::fs::create_dir_all(xdg_data_home) {
107 | log::error!("failed to create the themes directory: {e}")
108 | };
109 | }
110 | }
111 |
112 | let xdg_data_dirs = std::env::var("XDG_DATA_DIRS").ok();
113 |
114 | let xdg_data_dirs = xdg_data_dirs
115 | .as_deref()
116 | .or(Some("/usr/local/share/:/usr/share/"))
117 | .into_iter()
118 | .flat_map(|arg| std::env::split_paths(arg).map(|dir| dir.join("themes/cosmic")));
119 |
120 | for themes_directory in xdg_data_dirs.chain(xdg_data_home) {
121 | let Ok(read_dir) = std::fs::read_dir(&themes_directory) else {
122 | continue;
123 | };
124 |
125 | for entry in read_dir.filter_map(Result::ok) {
126 | let path = entry.path();
127 | let color_scheme = std::fs::read_to_string(&path)?;
128 | let theme: ThemeBuilder = ron::from_str(&color_scheme)?;
129 | let name = path
130 | .file_stem()
131 | .and_then(|name| name.to_str())
132 | .map(|name| name.to_string())
133 | .unwrap_or_default();
134 | let color_scheme = ColorScheme {
135 | name,
136 | path: Some(path),
137 | link: None,
138 | author: None,
139 | theme,
140 | };
141 | color_schemes.push(color_scheme);
142 | }
143 | }
144 |
145 | Ok(color_schemes)
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/app/pages/color_schemes/cosmic_theme.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | use super::config::ColorScheme;
4 |
5 | #[derive(Debug, Clone, Serialize, Deserialize)]
6 | pub struct CosmicTheme {
7 | pub name: String,
8 | pub ron: String,
9 | pub author: String,
10 | pub link: String,
11 | }
12 |
13 | impl From for ColorScheme {
14 | fn from(theme: CosmicTheme) -> Self {
15 | Self {
16 | name: theme.name,
17 | path: None,
18 | link: Some(theme.link),
19 | author: Some(theme.author),
20 | theme: ron::from_str(&theme.ron).unwrap(),
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/pages/color_schemes/preview.rs:
--------------------------------------------------------------------------------
1 | use crate::fl;
2 | use cosmic::{
3 | iced::{Alignment, Length},
4 | widget::{self, tooltip},
5 | Apply, Element,
6 | };
7 | use crate::app::core::{
8 | icons,
9 | style::{destructive_button, link_button, standard_button},
10 | };
11 | use super::config::ColorScheme;
12 |
13 | pub fn installed<'a>(
14 | color_scheme: &ColorScheme,
15 | selected: &ColorScheme,
16 | spacing: &cosmic::cosmic_theme::Spacing,
17 | item_width: usize,
18 | ) -> Element<'a, super::Message> {
19 | let theme = color_scheme.theme.clone().build();
20 | let color_scheme_name = color_scheme.name.clone();
21 | widget::column()
22 | .push(
23 | widget::row()
24 | .push(widget::horizontal_space())
25 | .push(widget::text(color_scheme_name))
26 | .push(widget::horizontal_space())
27 | .padding(spacing.space_xxs),
28 | )
29 | .push(
30 | widget::row()
31 | .push(
32 | widget::container(widget::text(fl!("navigation")))
33 | .padding(spacing.space_xxs)
34 | .width(90.0)
35 | .height(Length::Fill)
36 | .class(crate::app::core::style::card(theme.clone())),
37 | )
38 | .push(widget::horizontal_space())
39 | .push(widget::tooltip::tooltip(
40 | icons::get_handle("selection-mode-symbolic", 14)
41 | .apply(widget::button::icon)
42 | .class(link_button(theme.clone()))
43 | .padding(spacing.space_xxs)
44 | .class(if selected.name == color_scheme.name {
45 | cosmic::style::Button::Standard
46 | } else {
47 | cosmic::style::Button::Icon
48 | })
49 | .on_press(super::Message::SetColorScheme(color_scheme.clone())),
50 | widget::text(fl!("set-color-scheme")),
51 | tooltip::Position::Bottom,
52 | ))
53 | .push(widget::tooltip::tooltip(
54 | icons::get_handle("symbolic-link-symbolic", 14)
55 | .apply(widget::button::icon)
56 | .class(link_button(theme.clone()))
57 | .padding(spacing.space_xxs)
58 | .on_press(super::Message::OpenContainingFolder(color_scheme.clone())),
59 | widget::text(fl!("open-containing-folder")),
60 | tooltip::Position::Bottom,
61 | ))
62 | .push(widget::tooltip::tooltip(
63 | icons::get_handle("user-trash-symbolic", 14)
64 | .apply(widget::button::icon)
65 | .class(destructive_button(theme.clone()))
66 | .padding(spacing.space_xxs)
67 | .on_press(super::Message::DeleteColorScheme(color_scheme.clone())),
68 | widget::text(fl!("delete-color-scheme")),
69 | tooltip::Position::Bottom,
70 | ))
71 | .align_y(Alignment::End)
72 | .spacing(spacing.space_xxs)
73 | .padding([0, spacing.space_xxs, spacing.space_xxs, spacing.space_xxs]),
74 | )
75 | .width(item_width as f32)
76 | .height(160.)
77 | .apply(widget::container)
78 | .class(crate::app::core::style::background(&theme))
79 | .into()
80 | }
81 |
82 | pub fn available<'a>(
83 | color_scheme: &'a ColorScheme,
84 | spacing: &cosmic::cosmic_theme::Spacing,
85 | item_width: usize,
86 | ) -> Element<'a, super::Message> {
87 | let theme = color_scheme.theme.clone().build();
88 | widget::column()
89 | .push(
90 | widget::column()
91 | .push(widget::text(&color_scheme.name))
92 | .push_maybe(
93 | color_scheme
94 | .author
95 | .as_ref()
96 | .and_then(|author| Some(widget::text::caption(author.clone()))),
97 | )
98 | .width(Length::Fill)
99 | .align_x(Alignment::Center)
100 | .padding([spacing.space_xxs, spacing.space_none]),
101 | )
102 | .push(
103 | widget::row()
104 | .push(
105 | widget::container(widget::text(fl!("navigation")))
106 | .padding(spacing.space_xxs)
107 | .width(90.0)
108 | .height(Length::Fill)
109 | .class(crate::app::core::style::card(theme.clone())),
110 | )
111 | .push(widget::horizontal_space())
112 | .push(widget::tooltip::tooltip(
113 | icons::get_handle("symbolic-link-symbolic", 14)
114 | .apply(widget::button::icon)
115 | .class(link_button(theme.clone()))
116 | .padding(spacing.space_xxs)
117 | .on_press(super::Message::OpenLink(color_scheme.link.clone())),
118 | widget::text(fl!("open-link")),
119 | cosmic::widget::tooltip::Position::Bottom,
120 | ))
121 | .push(widget::tooltip::tooltip(
122 | icons::get_handle("folder-download-symbolic", 14)
123 | .apply(widget::button::icon)
124 | .class(standard_button(theme.clone()))
125 | .padding(spacing.space_xxs)
126 | .on_press(super::Message::InstallColorScheme(color_scheme.clone())),
127 | widget::text(fl!("install-color-scheme")),
128 | cosmic::widget::tooltip::Position::Bottom,
129 | ))
130 | .align_y(Alignment::End)
131 | .spacing(spacing.space_xxs)
132 | .padding([0, spacing.space_xxs, spacing.space_xxs, spacing.space_xxs]),
133 | )
134 | .width(item_width as f32)
135 | .height(160.)
136 | .apply(widget::container)
137 | .class(crate::app::core::style::background(&theme))
138 | .into()
139 | }
140 |
--------------------------------------------------------------------------------
/src/app/pages/dock.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | cosmic_config::{Config, CosmicConfigEntry},
3 | widget, Element, Task,
4 | };
5 | use cosmic_panel_config::CosmicPanelConfig;
6 |
7 | use crate::fl;
8 | use crate::app::core::icons;
9 |
10 | #[derive(Debug)]
11 | pub struct Dock {
12 | pub dock_helper: Option,
13 | pub dock_config: Option,
14 | pub padding: u32,
15 | pub spacing: u32,
16 | }
17 |
18 | impl Default for Dock {
19 | fn default() -> Self {
20 | let dock_helper = CosmicPanelConfig::cosmic_config("Dock").ok();
21 | let dock_config = dock_helper.as_ref().and_then(|config_helper| {
22 | let panel_config = CosmicPanelConfig::get_entry(config_helper).ok()?;
23 | (panel_config.name == "Dock").then_some(panel_config)
24 | });
25 | let padding = dock_config
26 | .clone()
27 | .map(|config| config.padding)
28 | .unwrap_or(0);
29 | let spacing = dock_config
30 | .clone()
31 | .map(|config| config.spacing)
32 | .unwrap_or(0);
33 | Self {
34 | dock_helper,
35 | dock_config,
36 | padding,
37 | spacing,
38 | }
39 | }
40 | }
41 |
42 | #[derive(Debug, Clone)]
43 | pub enum Message {
44 | SetPadding(u32),
45 | SetSpacing(u32),
46 | }
47 |
48 | impl Dock {
49 | pub fn view<'a>(&self) -> Element<'a, Message> {
50 | let spacing = cosmic::theme::spacing();
51 | widget::scrollable(
52 | widget::settings::section()
53 | .title("Dock")
54 | .add(
55 | widget::settings::item::builder(fl!("padding"))
56 | .description(fl!("padding-description"))
57 | .icon(icons::get_icon("resize-mode-symbolic", 18))
58 | .control(
59 | widget::row()
60 | .push(widget::slider(0..=28, self.padding, Message::SetPadding))
61 | .push(widget::text::text(format!("{} px", self.padding)))
62 | .spacing(spacing.space_xxs),
63 | ),
64 | )
65 | .add(
66 | widget::settings::item::builder(fl!("spacing"))
67 | .description(fl!("spacing-description"))
68 | .icon(icons::get_icon("size-horizontally-symbolic", 18))
69 | .control(
70 | widget::row()
71 | .push(widget::slider(0..=28, self.spacing, Message::SetSpacing))
72 | .push(widget::text::text(format!("{} px", self.spacing)))
73 | .spacing(spacing.space_xxs),
74 | ),
75 | ),
76 | )
77 | .into()
78 | }
79 |
80 | pub fn update(&mut self, message: Message) -> Task {
81 | let Some(dock_helper) = &mut self.dock_helper else {
82 | return cosmic::Task::none();
83 | };
84 | let Some(dock_config) = &mut self.dock_config else {
85 | return cosmic::Task::none();
86 | };
87 |
88 | match message {
89 | Message::SetPadding(padding) => {
90 | self.padding = padding;
91 | let update = dock_config.set_padding(dock_helper, self.padding);
92 | if let Err(err) = update {
93 | log::error!("Error updating dock padding: {}", err);
94 | }
95 | }
96 | Message::SetSpacing(spacing) => {
97 | self.spacing = spacing;
98 | let update = dock_config.set_spacing(dock_helper, self.spacing);
99 | if let Err(err) = update {
100 | log::error!("Error updating dock spacing: {}", err);
101 | }
102 | }
103 | }
104 | Task::none()
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/app/pages/layouts/config.rs:
--------------------------------------------------------------------------------
1 | use crate::{app::App, Error};
2 |
3 | use super::{preview::LayoutPreview, Message};
4 | use cosmic::{widget, Application, Element};
5 | use cosmic_ext_config_templates::{panel::PanelSchema, Schema};
6 | use serde::{Deserialize, Serialize};
7 | use uuid::Uuid;
8 |
9 | #[derive(Debug, Clone, Serialize, Deserialize)]
10 | pub struct Layout {
11 | pub id: Uuid,
12 | pub name: String,
13 | pub custom: bool,
14 | pub schema: Schema,
15 | pub preview: LayoutPreview,
16 | }
17 |
18 | impl Layout {
19 | pub fn new(name: String, preview: LayoutPreview) -> Self {
20 | Self {
21 | id: Uuid::new_v4(),
22 | name,
23 | custom: true,
24 | schema: Schema::Panel(PanelSchema::generate().unwrap()),
25 | preview,
26 | }
27 | }
28 |
29 | pub fn preview(
30 | &self,
31 | spacing: &cosmic::cosmic_theme::Spacing,
32 | item_width: usize,
33 | preview_height: u16,
34 | selected_layout: &Option,
35 | ) -> Element {
36 | let mut button = widget::button::custom(self.preview.view(&spacing, preview_height))
37 | .on_press(Message::Select(self.clone()))
38 | .class(cosmic::style::Button::Image)
39 | .width(item_width as f32);
40 | if let Some(selected) = selected_layout {
41 | button = button.selected(selected.name == self.name);
42 | }
43 | button.into()
44 | }
45 |
46 | pub fn list() -> Result, Error> {
47 | let mut layouts = Vec::new();
48 | let layouts_dir = dirs::data_local_dir()
49 | .map(|path| path.join(App::APP_ID).join("layouts"))
50 | .ok_or(Error::LayoutPathNotFound)?;
51 |
52 | if let Ok(entries) = std::fs::read_dir(layouts_dir) {
53 | for entry in entries.flatten() {
54 | let path = entry.path();
55 | if path.extension().and_then(|s| s.to_str()) == Some("ron") {
56 | let contents = std::fs::read_to_string(&path)?;
57 | let layout = ron::from_str::(&contents)?;
58 | layouts.push(layout);
59 | }
60 | }
61 | }
62 | Ok(layouts)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/app/pages/layouts/dialog.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{iced::Alignment, widget, Element};
2 |
3 | use crate::app::dialog::DialogPage;
4 | use crate::app::message::Message;
5 |
6 | use crate::app::core::icons;
7 | use crate::app::pages::layouts::preview::{LayoutPreview, PanelProperties};
8 | use crate::fl;
9 |
10 | #[derive(Debug, Clone, PartialEq, Eq)]
11 | pub struct CreateLayoutDialog {
12 | pub name: String,
13 | pub preview: LayoutPreview,
14 | pub error: Option,
15 | }
16 |
17 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
18 | pub enum PanelType {
19 | Panel,
20 | Dock,
21 | }
22 |
23 | impl CreateLayoutDialog {
24 | pub fn new(name: String, preview: LayoutPreview, error: Option) -> Self {
25 | Self {
26 | name,
27 | preview,
28 | error,
29 | }
30 | }
31 |
32 | pub fn section<'a>(
33 | &'a self,
34 | panel_type: PanelType,
35 | model: &'a widget::segmented_button::SingleSelectModel,
36 | ) -> widget::settings::Section<'a, Message> {
37 | let title = match panel_type {
38 | PanelType::Panel => fl!("panel"),
39 | PanelType::Dock => fl!("dock"),
40 | };
41 |
42 | let current_props = match panel_type {
43 | PanelType::Panel => &self.preview.panel,
44 | PanelType::Dock => &self.preview.dock,
45 | };
46 |
47 | let mut section = widget::settings::section()
48 | .title(title)
49 | .add(self.create_show_toggle(panel_type, ¤t_props))
50 | .add(self.create_extend_toggle(panel_type, ¤t_props))
51 | .add(self.create_position_control(panel_type, model))
52 | .add(self.create_size_control(panel_type, ¤t_props));
53 |
54 | if panel_type == PanelType::Dock {
55 | section = section.add(self.create_dock_icons_control());
56 | }
57 |
58 | section
59 | }
60 |
61 | pub fn create_show_toggle<'a>(
62 | &'a self,
63 | panel_type: PanelType,
64 | current_props: &'a PanelProperties,
65 | ) -> impl Into> {
66 | widget::settings::item::builder(fl!("show"))
67 | .icon(icons::get_icon("resize-mode-symbolic", 18))
68 | .control(
69 | widget::toggler(!current_props.hidden).on_toggle(move |hidden| {
70 | let mut new_preview = self.preview.clone();
71 | let new_props = PanelProperties {
72 | hidden: !hidden,
73 | ..current_props.clone()
74 | };
75 |
76 | match panel_type {
77 | PanelType::Panel => {
78 | new_preview.panel = new_props;
79 | }
80 | PanelType::Dock => {
81 | new_preview.dock = new_props;
82 | }
83 | }
84 |
85 | Message::DialogUpdate(DialogPage::CreateLayout(CreateLayoutDialog::new(
86 | self.name.to_string(),
87 | new_preview,
88 | self.error.clone(),
89 | )))
90 | }),
91 | )
92 | }
93 |
94 | pub fn create_extend_toggle<'a>(
95 | &'a self,
96 | panel_type: PanelType,
97 | current_props: &'a PanelProperties,
98 | ) -> impl Into> {
99 | widget::settings::item::builder(fl!("extend"))
100 | .icon(icons::get_icon("resize-mode-symbolic", 18))
101 | .control(
102 | widget::toggler(current_props.extend).on_toggle(move |extend| {
103 | let mut new_preview = self.preview.clone();
104 | let new_props = PanelProperties {
105 | extend,
106 | ..current_props.clone()
107 | };
108 |
109 | match panel_type {
110 | PanelType::Panel => {
111 | new_preview.panel = new_props;
112 | }
113 | PanelType::Dock => {
114 | new_preview.dock = new_props;
115 | }
116 | }
117 |
118 | Message::DialogUpdate(DialogPage::CreateLayout(CreateLayoutDialog::new(
119 | self.name.to_string(),
120 | new_preview,
121 | self.error.clone(),
122 | )))
123 | }),
124 | )
125 | }
126 |
127 | pub fn create_position_control<'a>(
128 | &'a self,
129 | panel_type: PanelType,
130 | model: &'a widget::segmented_button::SingleSelectModel,
131 | ) -> impl Into> {
132 | let spacing = cosmic::theme::spacing();
133 | let name = self.name.to_string().clone();
134 | let preview = self.preview.clone();
135 | let panel_type = panel_type.clone();
136 |
137 | widget::settings::item::builder(fl!("position"))
138 | .icon(icons::get_icon("resize-mode-symbolic", 18))
139 | .control(
140 | widget::segmented_button::horizontal(model)
141 | .on_activate(move |entity| match panel_type {
142 | PanelType::Panel => Message::UpdatePanelLayoutPosition(
143 | entity,
144 | name.clone(),
145 | preview.clone(),
146 | ),
147 | PanelType::Dock => {
148 | Message::UpdateDockLayoutPosition(entity, name.clone(), preview.clone())
149 | }
150 | })
151 | .button_alignment(Alignment::Center)
152 | .button_spacing(spacing.space_xxs),
153 | )
154 | }
155 |
156 | pub fn create_size_control<'a>(
157 | &'a self,
158 | panel_type: PanelType,
159 | panel_props: &PanelProperties,
160 | ) -> impl Into> {
161 | let name = self.name.to_string();
162 | let preview = self.preview.clone();
163 | let error = self.error.clone();
164 | let panel_props = panel_props.clone();
165 | let panel_type = panel_type;
166 |
167 | widget::settings::item::builder(fl!("size"))
168 | .icon(icons::get_icon("resize-mode-symbolic", 18))
169 | .control(widget::spin_button(
170 | panel_props.size.to_string(),
171 | panel_props.size as f32,
172 | 1.0,
173 | 0.0,
174 | 50.0,
175 | move |size| {
176 | let mut new_preview = preview.clone();
177 | let new_props = PanelProperties {
178 | size: size as usize,
179 | ..panel_props.clone()
180 | };
181 |
182 | match panel_type {
183 | PanelType::Panel => {
184 | new_preview.panel = new_props;
185 | }
186 | PanelType::Dock => {
187 | new_preview.dock = new_props;
188 | }
189 | }
190 |
191 | Message::DialogUpdate(DialogPage::CreateLayout(CreateLayoutDialog::new(
192 | name.clone(),
193 | new_preview,
194 | error.clone(),
195 | )))
196 | },
197 | ))
198 | }
199 |
200 | pub fn create_dock_icons_control<'a>(&'a self) -> impl Into> {
201 | let name = self.name.to_string();
202 | let preview = self.preview.clone();
203 | let error = self.error.clone();
204 |
205 | widget::settings::item::builder(fl!("dock-icons"))
206 | .icon(icons::get_icon("resize-mode-symbolic", 18))
207 | .control(widget::spin_button(
208 | preview.dock_icons.to_string(),
209 | preview.dock_icons,
210 | 1,
211 | 1,
212 | 20,
213 | move |size| {
214 | Message::DialogUpdate(DialogPage::CreateLayout(CreateLayoutDialog::new(
215 | name.clone(),
216 | LayoutPreview {
217 | dock_icons: size,
218 | ..preview.clone()
219 | },
220 | error.clone(),
221 | )))
222 | },
223 | ))
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/app/pages/layouts/mod.rs:
--------------------------------------------------------------------------------
1 | use config::Layout;
2 | use cosmic::{
3 | iced::{alignment::Horizontal, Length},
4 | widget::{
5 | self,
6 | segmented_button::{self, SingleSelect},
7 | },
8 | Application, Element, Task,
9 | };
10 | use cosmic_ext_config_templates::load_template;
11 | use preview::{LayoutPreview, Position};
12 |
13 | use crate::app::{core::grid::GridMetrics, App};
14 | use crate::{fl, Error};
15 |
16 | pub mod config;
17 | pub mod dialog;
18 | pub mod preview;
19 |
20 | pub struct Layouts {
21 | layouts: Vec,
22 | pub selected_layout: Option,
23 | pub panel_model: segmented_button::Model,
24 | pub dock_model: segmented_button::Model,
25 | }
26 |
27 | impl Default for Layouts {
28 | fn default() -> Self {
29 | Self {
30 | layouts: Vec::new(),
31 | selected_layout: None,
32 | panel_model: segmented_button::Model::builder()
33 | .insert(|b| b.text(fl!("left")).data(Position::Left))
34 | .insert(|b| b.text(fl!("top")).data(Position::Top).activate())
35 | .insert(|b| b.text(fl!("right")).data(Position::Right))
36 | .insert(|b| b.text(fl!("bottom")).data(Position::Bottom))
37 | .build(),
38 | dock_model: segmented_button::Model::builder()
39 | .insert(|b| b.text(fl!("left")).data(Position::Left))
40 | .insert(|b| b.text(fl!("top")).data(Position::Top))
41 | .insert(|b| b.text(fl!("right")).data(Position::Right))
42 | .insert(|b| b.text(fl!("bottom")).data(Position::Bottom).activate())
43 | .build(),
44 | }
45 | }
46 | }
47 |
48 | #[derive(Debug, Clone)]
49 | pub enum Message {
50 | Select(Layout),
51 | Apply,
52 | Delete,
53 | LoadLayouts(Vec),
54 | Create(String, LayoutPreview),
55 | }
56 |
57 | impl Layouts {
58 | pub fn init() -> Result<(), Error> {
59 | let layouts_dir = dirs::data_local_dir()
60 | .map(|path| path.join(App::APP_ID).join("layouts"))
61 | .ok_or(Error::LayoutPathNotFound)?;
62 |
63 | if !layouts_dir.exists() {
64 | std::fs::create_dir_all(&layouts_dir)?;
65 | }
66 |
67 | let layouts = vec![
68 | ("cosmic", include_str!("../../../../res/layouts/cosmic.ron")),
69 | ("mac", include_str!("../../../../res/layouts/mac.ron")),
70 | (
71 | "windows",
72 | include_str!("../../../../res/layouts/windows.ron"),
73 | ),
74 | ("ubuntu", include_str!("../../../../res/layouts/ubuntu.ron")),
75 | ];
76 |
77 | for (name, content) in layouts {
78 | let file_path = layouts_dir.join(name.to_lowercase()).with_extension("ron");
79 | std::fs::write(file_path, content)?;
80 | }
81 |
82 | Ok(())
83 | }
84 |
85 | pub fn view(&self) -> Element {
86 | let spacing = cosmic::theme::spacing();
87 | let grid = widget::responsive(move |size| {
88 | let GridMetrics {
89 | cols,
90 | item_width,
91 | column_spacing,
92 | } = GridMetrics::custom(&spacing, size.width as usize);
93 |
94 | let mut grid = widget::grid();
95 | let mut col = 0;
96 | for layout in self.layouts.iter() {
97 | if col >= cols {
98 | grid = grid.insert_row();
99 | col = 0;
100 | }
101 | grid = grid.push(
102 | widget::column()
103 | .push(layout.preview(&spacing, item_width, 130, &self.selected_layout))
104 | .push(widget::text(&layout.name))
105 | .spacing(spacing.space_xs)
106 | .align_x(Horizontal::Center),
107 | );
108 | col += 1;
109 | }
110 | widget::scrollable(
111 | grid.column_spacing(column_spacing)
112 | .row_spacing(column_spacing),
113 | )
114 | .height(Length::Fill)
115 | .width(Length::Fill)
116 | .into()
117 | });
118 |
119 | widget::column()
120 | .push(widget::settings::section().title(fl!("layouts")).add(grid))
121 | .into()
122 | }
123 |
124 | pub fn update(&mut self, message: Message) -> Task {
125 | match message {
126 | Message::LoadLayouts(layouts) => {
127 | self.layouts = layouts;
128 | }
129 | Message::Select(layout) => {
130 | self.selected_layout = Some(layout.clone());
131 | }
132 | Message::Apply => {
133 | if let Some(layout) = &self.selected_layout {
134 | if let Err(e) = load_template(layout.schema.clone()) {
135 | eprintln!("Failed to load template: {}", e);
136 | }
137 | self.selected_layout = None;
138 | }
139 | }
140 | Message::Delete => {
141 | if let Some(layout) = self.selected_layout.clone() {
142 | let layouts_dir = dirs::data_local_dir()
143 | .unwrap()
144 | .join(App::APP_ID)
145 | .join("layouts");
146 | let file_path = layouts_dir
147 | .join(&layout.id.to_string())
148 | .with_extension("ron");
149 | if file_path.exists() {
150 | match std::fs::remove_file(file_path) {
151 | Ok(_) => {
152 | self.selected_layout = None;
153 | self.layouts = self
154 | .layouts
155 | .clone()
156 | .into_iter()
157 | .filter(|l| l.id != layout.id)
158 | .collect();
159 | }
160 | Err(e) => {
161 | eprintln!("Failed to delete layout: {}", e);
162 | }
163 | }
164 | }
165 | }
166 | }
167 | Message::Create(name, preview) => {
168 | let layout = Layout::new(name, preview);
169 |
170 | let layouts_dir = dirs::data_local_dir()
171 | .unwrap()
172 | .join(App::APP_ID)
173 | .join("layouts");
174 |
175 | let file_path = layouts_dir
176 | .join(&layout.id.to_string())
177 | .with_extension("ron");
178 | if file_path.exists() {
179 | return Task::none();
180 | }
181 |
182 | match std::fs::write(&file_path, ron::to_string(&layout).unwrap()) {
183 | Ok(_) => match crate::app::pages::layouts::config::Layout::list() {
184 | Ok(layouts) => self.layouts = layouts,
185 | Err(e) => eprintln!("Failed to reload layouts: {e}"),
186 | },
187 | Err(e) => {
188 | log::error!("Failed to write layout: {}", e);
189 | }
190 | };
191 | }
192 | }
193 | Task::none()
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/app/pages/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod color_schemes;
2 | pub mod dock;
3 | pub mod layouts;
4 | pub mod panel;
5 | pub mod shortcuts;
6 | pub mod snapshots;
7 |
8 | pub use color_schemes::ColorSchemes;
9 | pub use dock::Dock;
10 | pub use layouts::Layouts;
11 | pub use panel::Panel;
12 | pub use shortcuts::Shortcuts;
13 | pub use snapshots::Snapshots;
14 |
--------------------------------------------------------------------------------
/src/app/pages/panel/config.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use cosmic::cosmic_config;
4 | use cosmic_config::{cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry};
5 | use serde::{Deserialize, Serialize};
6 |
7 | #[derive(Debug, Deserialize, Serialize, PartialEq, Clone, CosmicConfigEntry)]
8 | #[version = 1]
9 | #[serde(deny_unknown_fields)]
10 | pub struct CosmicPanelButtonConfig {
11 | pub configs: HashMap,
12 | }
13 |
14 | impl Default for CosmicPanelButtonConfig {
15 | fn default() -> Self {
16 | Self {
17 | configs: HashMap::from([
18 | (
19 | "Panel".to_string(),
20 | IndividualConfig {
21 | force_presentation: None,
22 | },
23 | ),
24 | (
25 | "Dock".to_string(),
26 | IndividualConfig {
27 | force_presentation: Some(Override::Icon),
28 | },
29 | ),
30 | ]),
31 | }
32 | }
33 | }
34 |
35 | #[derive(Debug, Deserialize, Serialize, PartialEq, Default, Clone)]
36 | pub struct IndividualConfig {
37 | pub force_presentation: Option,
38 | }
39 |
40 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
41 | pub enum Override {
42 | Icon,
43 | Text,
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/pages/shortcuts.rs:
--------------------------------------------------------------------------------
1 | use std::{env, fs, path::Path};
2 |
3 | use cosmic::{
4 | iced::padding,
5 | widget::{button, column, horizontal_space, row, text, vertical_space},
6 | Element, Task,
7 | };
8 |
9 | use crate::fl;
10 |
11 | pub struct Shortcuts {}
12 |
13 | #[derive(Debug, Clone)]
14 | enum Shortcut {
15 | Windows,
16 | }
17 |
18 | impl Shortcut {
19 | fn name(&self) -> &'static str {
20 | match self {
21 | Shortcut::Windows => "Windows",
22 | }
23 | }
24 |
25 | fn desc(&self) -> String {
26 | match self {
27 | Shortcut::Windows => fl!("windows-desc"),
28 | }
29 | }
30 |
31 | fn schema(&self) -> &'static str {
32 | match self {
33 | Self::Windows => include_str!("../../../res/shortcuts/windows.ron"),
34 | }
35 | }
36 | }
37 |
38 | #[derive(Debug, Clone)]
39 | #[allow(private_interfaces)]
40 | pub enum Message {
41 | ApplyShortcuts(Shortcut),
42 | }
43 |
44 | impl Shortcuts {
45 | pub fn new() -> Self {
46 | Self {}
47 | }
48 |
49 | pub fn update(&mut self, message: Message) -> Task {
50 | match message {
51 | Message::ApplyShortcuts(shortcut) => {
52 | let path = Path::new(&env::var("HOME").unwrap())
53 | .join(".config/cosmic/com.system76.CosmicSettings.Shortcuts/v1/custom");
54 |
55 | if let Err(e) = fs::write(&path, shortcut.schema()) {
56 | eprintln!("Failed to write shortcuts: {}", e);
57 | }
58 | }
59 | }
60 | Task::none()
61 | }
62 |
63 | pub fn view<'a>(&self) -> Element<'a, Message> {
64 | column()
65 | .push(text::heading(fl!("warning")))
66 | .push(vertical_space().height(25))
67 | .push(
68 | column().spacing(5).push(
69 | row()
70 | .push(view_button(Shortcut::Windows))
71 | .push(view_button(Shortcut::Windows)),
72 | ),
73 | )
74 | .into()
75 | }
76 | }
77 |
78 | fn view_button<'a>(shortcuts: Shortcut) -> Element<'a, Message> {
79 | button::custom(
80 | row()
81 | .push(text(shortcuts.name()))
82 | .push(horizontal_space())
83 | .push(text(shortcuts.desc())),
84 | )
85 | .padding(padding::all(10))
86 | .on_press(Message::ApplyShortcuts(shortcuts))
87 | .into()
88 | }
89 |
--------------------------------------------------------------------------------
/src/app/pages/snapshots/config.rs:
--------------------------------------------------------------------------------
1 | use std::{fmt::Display, path::PathBuf};
2 |
3 | use crate::{app::App, fl};
4 | use chrono::{NaiveDateTime, Utc};
5 | use cosmic::Application;
6 | use cosmic_ext_config_templates::{panel::PanelSchema, Schema};
7 | use serde::{Deserialize, Serialize};
8 | use uuid::Uuid;
9 |
10 | #[derive(Debug, Serialize, Clone, Default, Deserialize)]
11 | pub struct Snapshot {
12 | pub id: Uuid,
13 | pub name: String,
14 | pub kind: SnapshotKind,
15 | pub created: NaiveDateTime,
16 | pub schema: Option,
17 | }
18 |
19 | #[derive(Debug, Serialize, Clone, Default, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
20 | pub enum SnapshotKind {
21 | #[default]
22 | System,
23 | User,
24 | }
25 |
26 | impl Display for SnapshotKind {
27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 | match self {
29 | Self::System => write!(f, "{}", fl!("system")),
30 | Self::User => write!(f, "{}", fl!("user")),
31 | }
32 | }
33 | }
34 |
35 | impl Snapshot {
36 | pub fn new(name: impl ToString, kind: SnapshotKind) -> Self {
37 | let id = Uuid::new_v4();
38 | let created = Utc::now().naive_local();
39 |
40 | Self {
41 | id,
42 | name: name.to_string(),
43 | kind,
44 | created,
45 | schema: PanelSchema::generate()
46 | .ok()
47 | .map(|panel_schema| Schema::Panel(panel_schema)),
48 | }
49 | }
50 |
51 | pub fn created(&self) -> String {
52 | self.created.format("%Y-%m-%d %H:%M:%S").to_string()
53 | }
54 |
55 | pub fn schema(&self) -> Schema {
56 | Schema::from_file(&self.path()).unwrap()
57 | }
58 |
59 | pub fn path(&self) -> PathBuf {
60 | dirs::data_local_dir()
61 | .unwrap()
62 | .join(App::APP_ID)
63 | .join("snapshots")
64 | .join(&self.id.to_string())
65 | .with_extension("ron")
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/app/pages/snapshots/mod.rs:
--------------------------------------------------------------------------------
1 | use config::Snapshot;
2 | use cosmic::{iced::Length, widget, Application, Element, Task};
3 | use cosmic_ext_config_templates::load_template;
4 | use dirs::data_local_dir;
5 |
6 | use crate::app::core::icons;
7 | use crate::app::pages::snapshots::config::SnapshotKind;
8 | use crate::{app::App, fl};
9 |
10 | pub mod config;
11 |
12 | #[derive(Debug, Default)]
13 | pub struct Snapshots {
14 | snapshots: Vec,
15 | }
16 |
17 | impl Snapshots {
18 | pub fn list() -> Vec {
19 | dirs::data_local_dir()
20 | .unwrap()
21 | .join(App::APP_ID)
22 | .join("snapshots")
23 | .read_dir()
24 | .unwrap()
25 | .filter_map(|entry| entry.ok())
26 | .filter_map(|entry| std::fs::read_to_string(entry.path()).ok())
27 | .filter_map(|entry| ron::from_str(&entry).ok())
28 | .collect()
29 | }
30 | }
31 |
32 | #[derive(Debug, Clone)]
33 | pub enum Message {
34 | CreateSnapshot(String, SnapshotKind),
35 | ReloadSnapshots,
36 | RestoreSnapshot(Snapshot),
37 | DeleteSnapshot(Snapshot),
38 | }
39 |
40 | impl Snapshots {
41 | pub fn view(&self) -> Element {
42 | let spacing = cosmic::theme::spacing();
43 | let snapshots = self
44 | .snapshots
45 | .iter()
46 | .map(|snapshot| {
47 | widget::settings::item_row(vec![
48 | widget::text(&snapshot.name)
49 | .width(Length::FillPortion(2))
50 | .into(),
51 | widget::text(snapshot.kind.to_string())
52 | .width(Length::FillPortion(1))
53 | .into(),
54 | widget::text(snapshot.created())
55 | .width(Length::FillPortion(1))
56 | .into(),
57 | widget::row()
58 | .push(widget::tooltip(
59 | widget::button::icon(icons::get_handle(
60 | "arrow-circular-bottom-right-symbolic",
61 | 14,
62 | ))
63 | .class(cosmic::style::Button::Standard)
64 | .on_press(Message::RestoreSnapshot(snapshot.clone())),
65 | widget::text(fl!("restore-snapshot")),
66 | widget::tooltip::Position::Bottom,
67 | ))
68 | .push(widget::tooltip(
69 | widget::button::icon(icons::get_handle("user-trash-symbolic", 14))
70 | .class(cosmic::style::Button::Destructive)
71 | .on_press(Message::DeleteSnapshot(snapshot.clone())),
72 | widget::text(fl!("delete-snapshot")),
73 | widget::tooltip::Position::Bottom,
74 | ))
75 | .align_y(cosmic::iced::Alignment::Center)
76 | .spacing(spacing.space_xxs)
77 | .width(Length::FillPortion(1))
78 | .into(),
79 | ])
80 | .align_y(cosmic::iced::Alignment::Center)
81 | .spacing(spacing.space_xxxs)
82 | .width(Length::Fill)
83 | .into()
84 | })
85 | .collect::>>();
86 |
87 | let heading_item = |name, width| {
88 | widget::row()
89 | .push(widget::text::heading(name))
90 | .align_y(cosmic::iced::Alignment::Center)
91 | .spacing(spacing.space_xxxs)
92 | .width(width)
93 | };
94 |
95 | let header = if snapshots.is_empty() {
96 | None
97 | } else {
98 | Some(
99 | widget::row()
100 | .push(heading_item(fl!("name"), Length::FillPortion(2)))
101 | .push(heading_item(fl!("type"), Length::FillPortion(1)))
102 | .push(heading_item(fl!("created"), Length::FillPortion(1)))
103 | .push(heading_item(fl!("actions"), Length::FillPortion(1)))
104 | .padding([0, spacing.space_m]),
105 | )
106 | };
107 |
108 | let snapshots: Element<_> = if snapshots.is_empty() {
109 | widget::text(fl!("no-snapshots")).into()
110 | } else {
111 | widget::settings::section().extend(snapshots).into()
112 | };
113 |
114 | widget::scrollable(
115 | widget::column()
116 | .push(
117 | widget::row()
118 | .push(widget::text::title3(fl!("snapshots")))
119 | .push(widget::horizontal_space())
120 | .spacing(spacing.space_xxs),
121 | )
122 | .push(widget::text::body("Each time you open Tweaks, we save the current state of your desktop, if you ever break it, simply restore it."))
123 | .push_maybe(header)
124 | .push(snapshots)
125 | .spacing(spacing.space_xs),
126 | )
127 | .into()
128 | }
129 |
130 | pub fn update(&mut self, message: Message) -> Task {
131 | let mut tasks = vec![];
132 | match message {
133 | Message::ReloadSnapshots => {
134 | self.snapshots = Snapshots::list();
135 | self.snapshots.sort_by(|a, b| {
136 | b.created
137 | .and_utc()
138 | .timestamp()
139 | .cmp(&a.created.and_utc().timestamp())
140 | });
141 | }
142 | Message::RestoreSnapshot(snapshot) => {
143 | if let Err(e) = load_template(snapshot.schema()) {
144 | eprintln!("Failed to load template: {}", e);
145 | }
146 | }
147 | Message::CreateSnapshot(name, kind) => {
148 | let path = data_local_dir()
149 | .unwrap()
150 | .join(App::APP_ID)
151 | .join("snapshots");
152 | if !path.exists() {
153 | if let Err(e) = std::fs::create_dir_all(&path) {
154 | log::error!("{e}");
155 | }
156 | }
157 | let snapshot = Snapshot::new(name, kind);
158 | match ron::to_string(&snapshot) {
159 | Ok(data) => {
160 | if let Err(e) = std::fs::write(snapshot.path(), data) {
161 | log::error!("Failed to write snapshot: {}", e);
162 | }
163 | log::info!("Snapshot created: {}", snapshot.name);
164 | tasks.push(self.update(Message::ReloadSnapshots));
165 | }
166 | Err(e) => {
167 | log::error!("Failed to serialize snapshot: {}", e);
168 | }
169 | }
170 | }
171 | Message::DeleteSnapshot(snapshot) => {
172 | if snapshot.path().exists() {
173 | if let Err(e) = std::fs::remove_file(&snapshot.path()) {
174 | log::error!("Failed to delete layout: {}", e);
175 | return Task::batch(tasks);
176 | }
177 | tasks.push(self.update(Message::ReloadSnapshots));
178 | }
179 | }
180 | }
181 | Task::batch(tasks)
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/app/subscription.rs:
--------------------------------------------------------------------------------
1 | use std::any::TypeId;
2 |
3 | use cosmic::{
4 | cosmic_config::{self, Update},
5 | cosmic_theme::{self, ThemeMode},
6 | iced::{event, keyboard::Event as KeyEvent, Event, Subscription},
7 | Application,
8 | };
9 |
10 | use crate::app::message::Message;
11 | use crate::app::App;
12 |
13 | use crate::app::core::config::CONFIG_VERSION;
14 |
15 | use super::Cosmic;
16 |
17 | impl Cosmic {
18 | pub fn subscription() -> cosmic::iced::Subscription {
19 | struct ConfigSubscription;
20 | struct ThemeSubscription;
21 |
22 | let subscriptions = vec![
23 | event::listen_with(|event, _status, _window_id| match event {
24 | Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => {
25 | Some(Message::Key(modifiers, key))
26 | }
27 | Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
28 | Some(Message::Modifiers(modifiers))
29 | }
30 | _ => None,
31 | }),
32 | cosmic_config::config_subscription(
33 | TypeId::of::(),
34 | App::APP_ID.into(),
35 | CONFIG_VERSION,
36 | )
37 | .map(|update: Update| {
38 | if !update.errors.is_empty() {
39 | log::info!(
40 | "errors loading config {:?}: {:?}",
41 | update.keys,
42 | update.errors
43 | );
44 | }
45 | Message::SystemThemeModeChange
46 | }),
47 | cosmic_config::config_subscription::<_, cosmic_theme::ThemeMode>(
48 | TypeId::of::(),
49 | cosmic_theme::THEME_MODE_ID.into(),
50 | cosmic_theme::ThemeMode::version(),
51 | )
52 | .map(|update: Update| {
53 | if !update.errors.is_empty() {
54 | log::info!(
55 | "errors loading theme mode {:?}: {:?}",
56 | update.keys,
57 | update.errors
58 | );
59 | }
60 | Message::SystemThemeModeChange
61 | }),
62 | ];
63 |
64 | Subscription::batch(subscriptions)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/app/update.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | app::{self},
3 | widget::{self, menu::Action},
4 | Application, Task,
5 | };
6 |
7 | use crate::app::{dialog::DialogPage, pages::snapshots::config::SnapshotKind, App};
8 | use crate::app::{message::Message, pages::layouts::dialog::CreateLayoutDialog};
9 |
10 | use super::Cosmic;
11 | use crate::app::core::config::AppTheme;
12 | use crate::app::pages::{self, layouts::preview::Position};
13 |
14 | impl Cosmic {
15 | pub fn update(app: &mut App, message: Message) -> app::Task {
16 | let mut tasks = vec![];
17 | match message {
18 | Message::Open(url) => {
19 | if let Err(err) = open::that_detached(url) {
20 | log::error!("{err}")
21 | }
22 | }
23 | Message::AppTheme(index) => {
24 | let app_theme = match index {
25 | 1 => AppTheme::Dark,
26 | 2 => AppTheme::Light,
27 | _ => AppTheme::System,
28 | };
29 | if let Err(err) = app.config.set_app_theme(&app.handler, app_theme) {
30 | log::warn!("failed to save config: {}", err);
31 | };
32 | tasks.push(app.update_config());
33 | }
34 | Message::ToggleContextPage(page) => {
35 | if app.cosmic.context_page == page {
36 | app.core_mut().window.show_context = !app.core().window.show_context;
37 | } else {
38 | app.cosmic.context_page = page;
39 | app.core_mut().window.show_context = true;
40 | }
41 | }
42 | Message::ToggleContextDrawer => {
43 | app.core_mut().window.show_context = !app.core().window.show_context;
44 | }
45 | Message::Dock(message) => tasks.push(app.dock.update(message).map(cosmic::action::app)),
46 | Message::Panel(message) => {
47 | tasks.push(app.panel.update(message).map(cosmic::action::app))
48 | }
49 | Message::Layouts(message) => {
50 | tasks.push(app.layouts.update(message).map(cosmic::action::app))
51 | }
52 | Message::Shortcuts(message) => {
53 | tasks.push(app.shortcuts.update(message).map(cosmic::action::app))
54 | }
55 | Message::Snapshots(message) => match message {
56 | _ => tasks.push(app.snapshots.update(message).map(cosmic::action::app)),
57 | },
58 | Message::ColorSchemes(message) => match *message {
59 | pages::color_schemes::Message::SaveCurrentColorScheme(None) => {
60 | tasks.push(app.update(Message::ToggleDialogPage(
61 | DialogPage::SaveCurrentColorScheme(String::new()),
62 | )))
63 | }
64 | _ => tasks.push(
65 | app.color_schemes
66 | .update(*message)
67 | .map(Box::new)
68 | .map(Message::ColorSchemes)
69 | .map(cosmic::action::app),
70 | ),
71 | },
72 | Message::UpdatePanelLayoutPosition(entity, name, mut preview) => {
73 | app.layouts.panel_model.activate(entity);
74 | if let Some(position) = app.layouts.panel_model.data::(entity) {
75 | preview.panel.position = position.clone();
76 | tasks.push(app.update(Message::DialogUpdate(DialogPage::CreateLayout(
77 | CreateLayoutDialog::new(name, preview, None),
78 | ))))
79 | }
80 | }
81 | Message::UpdateDockLayoutPosition(entity, name, mut preview) => {
82 | app.layouts.dock_model.activate(entity);
83 | if let Some(position) = app.layouts.dock_model.data::(entity) {
84 | preview.dock.position = position.clone();
85 | tasks.push(app.update(Message::DialogUpdate(DialogPage::CreateLayout(
86 | CreateLayoutDialog::new(name, preview, None),
87 | ))))
88 | }
89 | }
90 | Message::SaveNewColorScheme(name) => {
91 | tasks.push(app.update(Message::ColorSchemes(Box::new(
92 | pages::color_schemes::Message::SaveCurrentColorScheme(Some(name)),
93 | ))))
94 | }
95 | Message::ToggleDialogPage(dialog_page) => {
96 | app.cosmic.dialog_pages.push_back(dialog_page);
97 | tasks.push(widget::text_input::focus(
98 | app.cosmic.dialog_text_input.clone(),
99 | ));
100 | }
101 | Message::DialogUpdate(dialog_page) => {
102 | app.cosmic.dialog_pages[0] = dialog_page;
103 | }
104 | Message::DialogComplete => {
105 | if let Some(dialog_page) = app.cosmic.dialog_pages.pop_front() {
106 | match dialog_page {
107 | DialogPage::SaveCurrentColorScheme(name) => {
108 | tasks.push(app.update(Message::SaveNewColorScheme(name)))
109 | }
110 | DialogPage::CreateSnapshot(name) => {
111 | tasks.push(app.update(Message::Snapshots(
112 | pages::snapshots::Message::CreateSnapshot(name, SnapshotKind::User),
113 | )))
114 | }
115 | DialogPage::CreateLayout(dialog) => {
116 | let CreateLayoutDialog {
117 | name,
118 | preview,
119 | error,
120 | } = dialog;
121 | if let Some(error) = error {
122 | tasks.push(app.update(Message::ToggleDialogPage(
123 | DialogPage::CreateLayout(CreateLayoutDialog::new(
124 | name,
125 | preview,
126 | Some(error),
127 | )),
128 | )));
129 | } else {
130 | tasks.push(app.update(Message::Layouts(
131 | pages::layouts::Message::Create(name, preview),
132 | )));
133 | }
134 | }
135 | }
136 | }
137 | }
138 | Message::DialogCancel => {
139 | app.cosmic.dialog_pages.pop_front();
140 | }
141 | Message::Key(modifiers, key) => {
142 | for (key_bind, action) in &app.cosmic.key_binds {
143 | if key_bind.matches(modifiers, &key) {
144 | return app.update(action.message());
145 | }
146 | }
147 | }
148 | Message::Modifiers(modifiers) => {
149 | app.cosmic.modifiers = modifiers;
150 | }
151 | Message::SystemThemeModeChange => {
152 | tasks.push(app.update_config());
153 | }
154 | }
155 | Task::batch(tasks)
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/app/view.rs:
--------------------------------------------------------------------------------
1 | use cosmic::{
2 | iced::{Alignment, Length},
3 | widget, Element,
4 | };
5 |
6 | use crate::app::{message::Message, page::Page, App};
7 |
8 | use super::Cosmic;
9 |
10 | impl Cosmic {
11 | pub fn view(app: &App) -> Element {
12 | let spacing = cosmic::theme::spacing();
13 | let entity = app.cosmic.nav_model.active();
14 | let nav_page = app
15 | .cosmic
16 | .nav_model
17 | .data::(entity)
18 | .unwrap_or_default();
19 |
20 | let view = match nav_page {
21 | Page::ColorSchemes => app
22 | .color_schemes
23 | .view()
24 | .map(Box::new)
25 | .map(Message::ColorSchemes),
26 | Page::Dock => app.dock.view().map(Message::Dock),
27 | Page::Panel => app.panel.view().map(Message::Panel),
28 | Page::Layouts => app.layouts.view().map(Message::Layouts),
29 | Page::Snapshots => app.snapshots.view().map(Message::Snapshots),
30 | Page::Shortcuts => app.shortcuts.view().map(Message::Shortcuts),
31 | };
32 |
33 | widget::column()
34 | .push(view)
35 | .padding(spacing.space_xs)
36 | .width(Length::Fill)
37 | .height(Length::Fill)
38 | .align_x(Alignment::Center)
39 | .into()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | mod app;
2 |
3 | pub use app::core::error::Error;
4 | use app::core::settings;
5 |
6 | fn main() -> Result<(), Error> {
7 | settings::init()?;
8 | cosmic::app::run::(settings::settings(), settings::flags())?;
9 | Ok(())
10 | }
11 |
--------------------------------------------------------------------------------