├── .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 | ![color-schemes-light.png](res/screenshots/color-schemes-light.png#gh-light-mode-only) 11 | ![color-schemes-dark.png](res/screenshots/color-schemes-dark.png#gh-dark-mode-only) 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 | 68 | 69 |
70 |
71 |
72 | Workflow 75 |
76 |
77 |
78 |

80 | Go Further 81 |

82 |

83 | Beyond the limits of your desktop 84 |

85 |

86 | Personalize your COSMIC™ desktop beyond infinity 87 |

88 |
89 | 90 | Get it on Flathub 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 | --------------------------------------------------------------------------------