├── .github └── workflows │ ├── build.yml │ └── lint.yml ├── .gitignore ├── LICENSE ├── README.md ├── biome.jsonc ├── justfile ├── package-lock.json ├── package.json ├── po ├── ar.po ├── cs.po ├── de.po ├── eo.po ├── es.po ├── et.po ├── fi.po ├── fr.po ├── hu.po ├── id.po ├── it.po ├── ja.po ├── nb_NO.po ├── nl.po ├── pl.po ├── pt.po ├── pt_BR.po ├── rounded-window-corners@fxgn.pot ├── ru.po ├── tr.po ├── uk.po └── zh_CN.po ├── resources ├── metadata.json ├── schemas │ └── org.gnome.shell.extensions.rounded-window-corners-reborn.gschema.xml ├── stylesheet-prefs.css └── stylesheet.css ├── src ├── effect │ ├── README.md │ ├── clip_shadow_effect.ts │ ├── linear_filter_effect.ts │ ├── rounded_corners_effect.ts │ └── shader │ │ ├── clip_shadow.frag │ │ └── rounded_corners.frag ├── extension.ts ├── global.d.ts ├── manager │ ├── README.md │ ├── event_handlers.ts │ ├── event_manager.ts │ └── utils.ts ├── patch │ ├── README.md │ ├── add_shadow_in_overview.ts │ └── workspace_switch.ts ├── preferences │ ├── README.md │ ├── index.ts │ ├── pages │ │ ├── blacklist.ts │ │ ├── blacklist.ui │ │ ├── custom.ts │ │ ├── custom.ui │ │ ├── edit-shadow.ui │ │ ├── edit_shadow.ts │ │ ├── general.ts │ │ ├── general.ui │ │ ├── reset.ts │ │ └── reset.ui │ └── widgets │ │ ├── app_row.ts │ │ ├── custom_settings_row.ts │ │ ├── paddings-row.ui │ │ └── paddings_row.ts ├── prefs.ts ├── utils │ ├── README.md │ ├── background_menu.ts │ ├── box_shadow.ts │ ├── constants.ts │ ├── file.ts │ ├── log.ts │ ├── settings.ts │ └── types.ts └── window_picker │ ├── README.md │ ├── client.ts │ ├── iface.xml │ └── service.ts └── tsconfig.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build the extension 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | pull_request: 8 | paths-ignore: 9 | - README.md 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | name: Build the extension 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout the repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Set up node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: latest 24 | cache: 'npm' 25 | 26 | - name: Set up just 27 | uses: extractions/setup-just@v2 28 | 29 | - name: Install gettext 30 | run: sudo apt install gettext 31 | 32 | - name: Build the extension 33 | run: just build 34 | 35 | - name: Upload artifacts 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: rounded-window-corners@fxgn.shell-extension 39 | path: _build/ 40 | compression-level: 9 41 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint the code 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'src/**' 7 | - '**/*.json' 8 | pull_request: 9 | paths: 10 | - 'src/**' 11 | - '**/*.json' 12 | 13 | jobs: 14 | lint: 15 | name: Lint the code 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout the repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up Biome 22 | uses: biomejs/setup-biome@v2 23 | with: 24 | version: latest 25 | 26 | - name: Run Biome 27 | run: biome ci . 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | *.zip 3 | node_modules 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![2022-07-29 23-49-57][6] 2 | 3 |
4 |

Rounded Window Corners Reborn

5 |

A GNOME extension that adds rounded corners to all windows

6 | 7 | 8 | 9 |
10 |
11 | 12 | > [!NOTE] 13 | > This is the fork of the [original rounded-window-corners extension][14] by @yilozt, which is no longer maintained. 14 | 15 | ## Features 16 | 17 | - Works with Gnome 46+ 18 | - Custom border radius and clip paddings for windows 19 | - Blocklist for applications that draw their own window decorations 20 | - Custom shadow for windows with rounded corners 21 | - Option to skip libadwaita / libhandy application 22 | - [Superelliptical][1] shape for rounded corners, thanks to [@YuraIz][2] 23 | - A simple reset preferences dialog 24 | 25 | ## Installation 26 | 27 | ### From Gnome Extensions 28 | 29 | The extension is available on [extensions.gnome.org](https://extensions.gnome.org). You can install it directly [from here](https://extensions.gnome.org/extension/7048/rounded-window-corners-reborn/), or from the Extension Manager app. 30 | 31 | ### From pre-built archives 32 | 33 | If you want to install the latest commit of the extension, you can get a 34 | pre-built archive from GitHub Actions. 35 | 36 | 1. Sign in to GitHub. 37 | 2. Go to [the build action page](https://github.com/flexagoon/rounded-window-corners/actions/workflows/build.yml) 38 | 3. Click on the latest workflow run 39 | 4. Download the extension from the "artifacts" section at the bottom 40 | 5. Install it with the `gnome-extensions install` command 41 | 42 | ### From source code 43 | 44 | 1. Install the dependencies: 45 | - Node.js 46 | - npm 47 | - gettext 48 | - [just](https://just.systems) 49 | 50 | Those packages are available in the repositories of most linux distros, so 51 | you can simply install them with your package manager. 52 | 53 | 2. Build the extension 54 | 55 | ```bash 56 | git clone https://github.com/flexagoon/rounded-window-corners 57 | cd rounded-window-corners 58 | just install 59 | ``` 60 | 61 | After this, the extension will be installed to 62 | `~/.local/share/gnome-shell/extensions`. 63 | 64 | ### From unofficial AUR packages on Arch Linux 65 | 66 | If you use Arch, by the way, you can also install from the provided [AUR](https://aur.archlinux.org/) packages using [paru](https://github.com/Morganamilo/paru) or [yay](https://github.com/Jguer/yay). Two packages are available: 67 | 68 | - [gnome-shell-extension-rounded-window-corners-reborn](https://aur.archlinux.org/packages/gnome-shell-extension-rounded-window-corners-reborn) uses the pre-build archives 69 | - [gnome-shell-extension-rounded-window-corners-reborn-git](https://aur.archlinux.org/packages/gnome-shell-extension-rounded-window-corners-reborn-git) builds on your machine 70 | 71 | Installation: 72 | 73 | ```zsh 74 | paru gnome-shell-extension-rounded-window-corners-reborn 75 | ``` 76 | 77 | Note these packages are not official. 78 | 79 | ## Translation 80 | 81 | You can help with the translation of the extension by submitting translations 82 | on [Weblate](https://hosted.weblate.org/engage/rounded-window-corners-reborn) 83 | 84 | [![Translation status](https://hosted.weblate.org/widget/rounded-window-corners-reborn/multi-auto.svg)](https://hosted.weblate.org/engage/rounded-window-corners-reborn/) 85 | 86 | You can also manually edit .po files and submit a PR if you know how to do that. 87 | 88 | ## Development 89 | 90 | Here are the avaliable `just` commands (run `just --list` to see this message): 91 | 92 | ```bash 93 | Available recipes: 94 | build # Compile the extension and all resources 95 | clean # Delete the build directory 96 | install # Build and install the extension from source 97 | pack # Build and pack the extension 98 | pot # Update and compile the translation files 99 | ``` 100 | 101 | ## Credits 102 | 103 | Thanks to [yotamguttman](https://github.com/yotamguttman) for making an icon for the extension! 104 | 105 | 106 | 107 | [1]: https://en.wikipedia.org/wiki/Superellipse 108 | [2]: https://github.com/YuraIz 109 | [3]: https://extensions.gnome.org/extension/3740/compiz-alike-magic-lamp-effect/ 110 | [4]: https://gitlab.gnome.org/GNOME/mutter/-/blob/main/src/compositor/meta-background-content.c#L138 111 | [6]: https://user-images.githubusercontent.com/32430186/181902857-d4d10740-82fe-4941-b064-d436b9ea7317.png 112 | [7]: https://extensions.gnome.org/extension/5237/rounded-window-corners/ 113 | [8]: https://github.com/yilozt/rounded-window-corners/releases 114 | [9]: https://github.com/yilozt/rounded-window-corners/actions/workflows/pack.yml 115 | [10]: https://img.shields.io/github/v/release/yilozt/rounded-window-corners?style=flat-square 116 | [11]: https://img.shields.io/github/actions/workflow/status/yilozt/rounded-window-corners/pack.yml?branch=main&style=flat-square 117 | [12]: https://hosted.weblate.org/widgets/rounded-window-corners/-/rounded-window-corners/multi-auto.svg 118 | [13]: https://hosted.weblate.org/engage/rounded-window-corners/ 119 | [14]: https://github.com/yilozt/rounded-window-corners 120 | -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.7.2/schema.json", 3 | "files": { 4 | "ignore": ["_build/*"] 5 | }, 6 | "organizeImports": { 7 | "enabled": true 8 | }, 9 | "linter": { 10 | "ignore": ["**/*.d.ts"], 11 | "rules": { 12 | "all": true, 13 | "style": { 14 | "noNamespaceImport": "off", 15 | "noDefaultExport": "off", 16 | "useNamingConvention": { 17 | "level": "warn", 18 | "options": { 19 | "conventions": [ 20 | { 21 | // Whitelist some GJS names that don't follow the JS naming conventions 22 | "match": "(?:G[A-Z].*|vfunc_.*|Template|InternalChildren|icon_name|css_classes|draw_value|value_pos|round_digits|Properties|Padding.*|(.*))" 23 | } 24 | ] 25 | } 26 | } 27 | }, 28 | "performance": { 29 | "noDelete": "off" 30 | }, 31 | "suspicious": { 32 | "noConsole": "off", 33 | "noConsoleLog": "off" 34 | } 35 | } 36 | }, 37 | "formatter": { 38 | "indentStyle": "space", 39 | "indentWidth": 4 40 | }, 41 | "javascript": { 42 | "formatter": { 43 | "quoteStyle": "single", 44 | "bracketSpacing": false, 45 | "arrowParentheses": "asNeeded" 46 | }, 47 | "globals": ["global", "log", "logError"] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # Expand path patterns like **/*.ui 2 | set shell := ['bash', '-O', 'globstar', '-c'] 3 | 4 | buildDir := './_build' 5 | uuid := 'rounded-window-corners@fxgn' 6 | 7 | # Compile the extension and all resources 8 | build: clean && pot 9 | # Compile TypeScript 10 | npm install 11 | npx tsc --outDir {{buildDir}} 12 | 13 | # Copy non-JS files 14 | cp -r ./resources/* {{buildDir}} 15 | for file in $(find src -type f ! -name "*.ts" -printf '%P\n'); do \ 16 | path={{buildDir}}/$(dirname $file); \ 17 | mkdir -p $path; \ 18 | cp src/$file $path; \ 19 | done; 20 | 21 | # Compile schemas 22 | glib-compile-schemas {{buildDir}}/schemas 23 | 24 | # Build and install the extension from source 25 | install: build 26 | rm -rf ~/.local/share/gnome-shell/extensions/{{uuid}} 27 | cp -r {{buildDir}} ~/.local/share/gnome-shell/extensions/{{uuid}} 28 | 29 | # Build and pack the extension 30 | pack: build 31 | cd {{buildDir}} && zip -9r ../{{uuid}}.shell-extension.zip . 32 | 33 | # Delete the build directory 34 | clean: 35 | rm -rf {{buildDir}} {{uuid}}.shell-extension.zip 36 | 37 | # Update and compile the translation files 38 | pot: 39 | xgettext --from-code=UTF-8 \ 40 | --output=po/{{uuid}}.pot \ 41 | src/**/*.ui 42 | 43 | xgettext --from-code=UTF-8 \ 44 | --output=po/{{uuid}}.pot \ 45 | --language=JavaScript \ 46 | --join-existing \ 47 | src/**/*.ts 48 | 49 | for file in po/*.po; do \ 50 | msgmerge -q -U --backup=off $file po/{{uuid}}.pot; \ 51 | done; 52 | 53 | for file in po/*.po; do \ 54 | locale=$(basename $file .po); \ 55 | dir="{{buildDir}}/locale/$locale/LC_MESSAGES"; \ 56 | mkdir -p $dir; \ 57 | msgfmt -o $dir/{{uuid}}.mo $file; \ 58 | done; 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gnome-shell-extension-rounded-corners-effect", 3 | "version": "v1", 4 | "description": "A GNOME extension that adds rounded corners to all windows", 5 | "author": "flexagoon ", 6 | "license": "GPL-3.0-or-later", 7 | "main": "src/extension.ts", 8 | "devDependencies": { 9 | "@biomejs/biome": "^1.9.4", 10 | "@girs/gnome-shell": "^47.0.2", 11 | "typescript": "^5.7.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /po/ar.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: 12\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 6 | "PO-Revision-Date: 2023-12-10 10:05+0000\n" 7 | "Last-Translator: Nadjib Chergui \n" 8 | "Language-Team: Arabic \n" 10 | "Language: ar\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " 14 | "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" 15 | "X-Generator: Weblate 5.3-dev\n" 16 | 17 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 18 | msgid "Blacklist" 19 | msgstr "اللائحة السوداء" 20 | 21 | #: src/preferences/pages/blacklist.ui:11 22 | #, fuzzy 23 | msgid "" 24 | "Not all application can works well with rounded corners effects, add them to " 25 | "this list to disable effects." 26 | msgstr "" 27 | "ليست كل التطبيقات تعمل مع تأثير الزوايا المدورة، أضفهم لإيقاف التأثيرات لهم." 28 | 29 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 30 | #, fuzzy 31 | msgid "Add window" 32 | msgstr "إضافة نافذة" 33 | 34 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 35 | msgid "Custom" 36 | msgstr "خاص" 37 | 38 | #: src/preferences/pages/custom.ui:11 39 | #, fuzzy 40 | msgid "Set custom effect setting for each window class" 41 | msgstr "تفعيل الإعدادات الخاصة بهذه النافذة" 42 | 43 | #: src/preferences/pages/edit-shadow.ui:6 44 | msgid "Edit shadows" 45 | msgstr "" 46 | 47 | #: src/preferences/pages/edit-shadow.ui:28 48 | #, fuzzy 49 | msgid "Focused window" 50 | msgstr "التركيز على النافذة" 51 | 52 | #: src/preferences/pages/edit-shadow.ui:43 53 | #, fuzzy 54 | msgid "Unfocused window" 55 | msgstr "النافذة الغير المختارة" 56 | 57 | #: src/preferences/pages/edit-shadow.ui:66 58 | msgid "Focused shadow prefrences" 59 | msgstr "" 60 | 61 | #: src/preferences/pages/edit-shadow.ui:69 62 | #: src/preferences/pages/edit-shadow.ui:209 63 | #, fuzzy 64 | msgid "Horizontal offset" 65 | msgstr "الميلان الأفقي" 66 | 67 | #: src/preferences/pages/edit-shadow.ui:96 68 | #: src/preferences/pages/edit-shadow.ui:236 69 | #, fuzzy 70 | msgid "Vertical offset" 71 | msgstr "الميلان العمودي" 72 | 73 | #: src/preferences/pages/edit-shadow.ui:123 74 | #: src/preferences/pages/edit-shadow.ui:263 75 | #, fuzzy 76 | msgid "Blur radius" 77 | msgstr "درجة تدوير زوايا الحدود" 78 | 79 | #: src/preferences/pages/edit-shadow.ui:150 80 | #: src/preferences/pages/edit-shadow.ui:290 81 | #, fuzzy 82 | msgid "Spread radius" 83 | msgstr "درجة تدوير زوايا الحدود" 84 | 85 | #: src/preferences/pages/edit-shadow.ui:177 86 | #: src/preferences/pages/edit-shadow.ui:317 87 | msgid "Opacity" 88 | msgstr "" 89 | 90 | #: src/preferences/pages/edit-shadow.ui:206 91 | msgid "Unfocused shadow prefrences" 92 | msgstr "" 93 | 94 | #: src/preferences/pages/general.ui:6 95 | msgid "General" 96 | msgstr "عام" 97 | 98 | #: src/preferences/pages/general.ui:10 99 | msgid "Applications" 100 | msgstr "تطبيقات" 101 | 102 | #: src/preferences/pages/general.ui:11 103 | #, fuzzy 104 | msgid "" 105 | "Some applications created with LibAdwaita or LibHandy already have rounded " 106 | "corners. You can enable those settings to avoid rounding their corners an " 107 | "extra time." 108 | msgstr "" 109 | "بعض التطبيقات التي تستخدم (LibAdwaita) أو (LibHandy) لديها زوايا دائرية " 110 | "فعلًا، إذن يمكن تجنبها بواسطة هذه الإعدادات" 111 | 112 | #: src/preferences/pages/general.ui:14 113 | #, fuzzy 114 | msgid "Skip LibAdwaita applications" 115 | msgstr "تجنب تطبيقات (LibAdwaita)" 116 | 117 | #: src/preferences/pages/general.ui:19 118 | #, fuzzy 119 | msgid "Skip LibHandy applications" 120 | msgstr "تجنب تطبيقات (LibHandy)" 121 | 122 | #: src/preferences/pages/general.ui:26 123 | #, fuzzy 124 | msgid "Global settings" 125 | msgstr "الإعدادات الشاملة" 126 | 127 | #: src/preferences/pages/general.ui:27 128 | #, fuzzy 129 | msgid "These settings will have an effect on all windows" 130 | msgstr "هذه الإعدادات ستحاول في التأثير على كل النوافذ" 131 | 132 | #: src/preferences/pages/general.ui:30 133 | #, fuzzy 134 | msgid "Border width" 135 | msgstr "عرض الحدود" 136 | 137 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 138 | #: src/preferences/widgets/custom_settings_row.ts:19 139 | #, fuzzy 140 | msgid "Border color" 141 | msgstr "لون الحدود" 142 | 143 | #: src/preferences/pages/general.ui:74 144 | #: src/preferences/widgets/custom_settings_row.ts:27 145 | #, fuzzy 146 | msgid "Corner radius" 147 | msgstr "درجة تدوير زوايا الحدود" 148 | 149 | #: src/preferences/pages/general.ui:101 150 | #: src/preferences/widgets/custom_settings_row.ts:37 151 | #, fuzzy 152 | msgid "Corner smoothing" 153 | msgstr "تمليس الزاوية" 154 | 155 | #: src/preferences/pages/general.ui:127 156 | #, fuzzy 157 | msgid "Window shadow" 158 | msgstr "ظل النافذة" 159 | 160 | #: src/preferences/pages/general.ui:128 161 | msgid "Customize the shadow of the rounded corner window" 162 | msgstr "تخصيص نمط ظل النافذة ذات الزوايا المدورة" 163 | 164 | #: src/preferences/pages/general.ui:140 165 | #, fuzzy 166 | msgid "Keep rounded corners for maximized windows" 167 | msgstr "إبقاء الزوايا المدورة في وضع التكبير" 168 | 169 | #: src/preferences/pages/general.ui:145 170 | #, fuzzy 171 | msgid "Keep rounded corners for fullscreen windows" 172 | msgstr "إبقاء الزوايا المدورة في وضع ملء الشاشة" 173 | 174 | #: src/preferences/pages/general.ui:155 175 | msgid "Tweaks" 176 | msgstr "تعديلات" 177 | 178 | #: src/preferences/pages/general.ui:158 179 | #, fuzzy 180 | msgid "Add rounded corners to Kitty Terminal on Wayland" 181 | msgstr "حاول إضافة الزوايا المدورة لطرفية كيتي (Kitty) في وايلاند (Wayland)" 182 | 183 | #: src/preferences/pages/general.ui:159 184 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 185 | msgstr "" 186 | 187 | #: src/preferences/pages/general.ui:164 188 | #, fuzzy 189 | msgid "Add a settings entry in right-click menu of the desktop" 190 | msgstr "أضف بند الإعدادات في قائمة النقرة اليمنى للخلفية" 191 | 192 | #: src/preferences/pages/general.ui:171 193 | msgid "Debug" 194 | msgstr "تصحيح الأخطاء" 195 | 196 | #: src/preferences/pages/general.ui:174 197 | #, fuzzy 198 | msgid "Enable debug logs" 199 | msgstr "تفعيل التسجيلات التقنية" 200 | 201 | #: src/preferences/pages/general.ui:175 202 | #, fuzzy 203 | msgid "" 204 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 205 | msgstr "" 206 | "شغل journalctl -o cat -f /usr/bin/gnome-shell في الطرفية لتظهر " 207 | "التسجيلات" 208 | 209 | #: src/preferences/pages/general.ui:184 210 | #, fuzzy 211 | msgid "Reset preferences" 212 | msgstr "إعادة ضبط الإعدادات" 213 | 214 | #: src/preferences/pages/reset.ui:6 215 | msgid "Reset settings" 216 | msgstr "" 217 | 218 | #: src/preferences/pages/reset.ui:16 219 | #, fuzzy 220 | msgid "Settings to reset" 221 | msgstr "اختر الأشياء لإعادة ضبطها" 222 | 223 | #: src/preferences/pages/reset.ui:22 224 | #, fuzzy 225 | msgid "Select all" 226 | msgstr "اختر الكل" 227 | 228 | #: src/preferences/pages/reset.ui:39 229 | msgid "Reset" 230 | msgstr "" 231 | 232 | #: src/preferences/pages/reset.ui:55 233 | msgid "Reset these settings?" 234 | msgstr "" 235 | 236 | #: src/preferences/pages/reset.ui:60 237 | msgid "_Cancel" 238 | msgstr "" 239 | 240 | #: src/preferences/pages/reset.ui:61 241 | msgid "_Reset" 242 | msgstr "" 243 | 244 | #: src/preferences/widgets/paddings-row.ui:12 245 | msgid "Paddings" 246 | msgstr "تباعدات" 247 | 248 | #: src/preferences/widgets/paddings-row.ui:25 249 | msgid "Top" 250 | msgstr "أعلى" 251 | 252 | #: src/preferences/widgets/paddings-row.ui:40 253 | msgid "Bottom" 254 | msgstr "أسفل" 255 | 256 | #: src/preferences/widgets/paddings-row.ui:55 257 | msgid "Left" 258 | msgstr "يسار" 259 | 260 | #: src/preferences/widgets/paddings-row.ui:70 261 | msgid "Right" 262 | msgstr "يمين" 263 | 264 | #: src/preferences/widgets/app_row.ts:33 265 | #, fuzzy 266 | msgid "Window class" 267 | msgstr "ظل النافذة" 268 | 269 | #: src/preferences/widgets/app_row.ts:54 270 | #, fuzzy 271 | msgid "Expand this row, to pick a window" 272 | msgstr "كبر الصف لاختيار نافذة." 273 | 274 | #: src/preferences/widgets/app_row.ts:94 275 | #, fuzzy 276 | msgid "Can't pick window from this position" 277 | msgstr "لا يمكن اختيار النافذة من هذا المكان" 278 | 279 | #: src/preferences/widgets/custom_settings_row.ts:15 280 | #, fuzzy 281 | msgid "Enabled" 282 | msgstr "تفعيل" 283 | 284 | #: src/preferences/widgets/custom_settings_row.ts:47 285 | #, fuzzy 286 | msgid "Keep rounded corners when maximized" 287 | msgstr "إبقاء الزوايا المدورة في وضع التكبير" 288 | 289 | #: src/preferences/widgets/custom_settings_row.ts:49 290 | #, fuzzy 291 | msgid "Always clip rounded corners even if window is maximized or tiled" 292 | msgstr "إبقاء زوايا النافذة مدورة في وضع التكبير أو التحجيم." 293 | 294 | #: src/preferences/widgets/custom_settings_row.ts:53 295 | #, fuzzy 296 | msgid "Keep rounded corners when in fullscreen" 297 | msgstr "إبقاء الزوايا المدورة في وضع ملء الشاشة" 298 | 299 | #: src/preferences/widgets/custom_settings_row.ts:54 300 | #, fuzzy 301 | msgid "Always clip rounded corners even for fullscreen window" 302 | msgstr "إبقاء الزوايا المدورة في حال النافذة في وضع ملء الشاشة." 303 | 304 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 305 | msgid "Rounded Corners Settings..." 306 | msgstr "إعدادات الزوايا المدورة..." 307 | 308 | #, fuzzy 309 | #~ msgid "Open prefereces reset page" 310 | #~ msgstr "فتح نافذة إعادة الضبط" 311 | 312 | #~ msgid "Skip LibAdwaita Applications" 313 | #~ msgstr "تجنب تطبيقات (LibAdwaita)" 314 | 315 | #~ msgid "Skip LibHandy Applications" 316 | #~ msgstr "تجنب تطبيقات (LibHandy)" 317 | 318 | #~ msgid "Focus Window Shadow Style" 319 | #~ msgstr "نمط ظل النافذة المختارة" 320 | 321 | #~ msgid "Unfocus Window Shadow Style" 322 | #~ msgstr "نمط ظل النافذة الغير المختارة" 323 | 324 | #, fuzzy 325 | #~ msgid "Border Width" 326 | #~ msgstr "عرض الحدود" 327 | 328 | #~ msgid "Border Color" 329 | #~ msgstr "لون الحدود" 330 | 331 | #~ msgid "Border Radius" 332 | #~ msgstr "درجة تدوير زوايا الحدود" 333 | 334 | #~ msgid "Padding" 335 | #~ msgstr "تباعد" 336 | 337 | #~ msgid "Keep Rounded Corners when Maximized or Fullscreen" 338 | #~ msgstr "إبقاء الزوايا المدورة في وضع التكبير أو ملء الشاشة" 339 | 340 | #~ msgid "Corner Smoothing" 341 | #~ msgstr "تمليس الزاوية" 342 | 343 | #~ msgid "Expand this row to pick a window." 344 | #~ msgstr "كبر الصف لاختيار نافذة." 345 | 346 | #~ msgid "Remove Window from this List" 347 | #~ msgstr "إزالة النافذة من القائمة" 348 | 349 | #~ msgid "Pick Window to add into list" 350 | #~ msgstr "اختر نافذةً لإضافتها إلى القائمة" 351 | 352 | #~ msgid "Apply" 353 | #~ msgstr "تطبيق" 354 | 355 | #~ msgid "Blur Offset" 356 | #~ msgstr "توازن الضبابية" 357 | 358 | #, fuzzy 359 | #~ msgid "Clip paddings of window" 360 | #~ msgstr "تصغير حواف النافذة" 361 | 362 | #~ msgid "Can't add to list, because it has exists" 363 | #~ msgstr "لا يمك‏ن إضافته للقائمة، لأنه موجودة فيها" 364 | 365 | #~ msgid "Edit Shadow for Rounded Corners Windows" 366 | #~ msgstr "تعديل ظل النوافذ ذات الزوايا المدورة" 367 | -------------------------------------------------------------------------------- /po/eo.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 5 | "PO-Revision-Date: 2023-01-04 20:51+0000\n" 6 | "Last-Translator: phlostically \n" 7 | "Language-Team: Esperanto \n" 9 | "Language: eo\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 13 | "X-Generator: Weblate 4.15.1-dev\n" 14 | 15 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 16 | msgid "Blacklist" 17 | msgstr "" 18 | 19 | #: src/preferences/pages/blacklist.ui:11 20 | msgid "" 21 | "Not all application can works well with rounded corners effects, add them to " 22 | "this list to disable effects." 23 | msgstr "" 24 | 25 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 26 | msgid "Add window" 27 | msgstr "" 28 | 29 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 30 | msgid "Custom" 31 | msgstr "Propra" 32 | 33 | #: src/preferences/pages/custom.ui:11 34 | msgid "Set custom effect setting for each window class" 35 | msgstr "" 36 | 37 | #: src/preferences/pages/edit-shadow.ui:6 38 | msgid "Edit shadows" 39 | msgstr "" 40 | 41 | #: src/preferences/pages/edit-shadow.ui:28 42 | #, fuzzy 43 | msgid "Focused window" 44 | msgstr "Enfokusa fenestro" 45 | 46 | #: src/preferences/pages/edit-shadow.ui:43 47 | #, fuzzy 48 | msgid "Unfocused window" 49 | msgstr "Eksterfokusa fenestro" 50 | 51 | #: src/preferences/pages/edit-shadow.ui:66 52 | msgid "Focused shadow prefrences" 53 | msgstr "" 54 | 55 | #: src/preferences/pages/edit-shadow.ui:69 56 | #: src/preferences/pages/edit-shadow.ui:209 57 | msgid "Horizontal offset" 58 | msgstr "" 59 | 60 | #: src/preferences/pages/edit-shadow.ui:96 61 | #: src/preferences/pages/edit-shadow.ui:236 62 | msgid "Vertical offset" 63 | msgstr "" 64 | 65 | #: src/preferences/pages/edit-shadow.ui:123 66 | #: src/preferences/pages/edit-shadow.ui:263 67 | #, fuzzy 68 | msgid "Blur radius" 69 | msgstr "Randa radiuso" 70 | 71 | #: src/preferences/pages/edit-shadow.ui:150 72 | #: src/preferences/pages/edit-shadow.ui:290 73 | #, fuzzy 74 | msgid "Spread radius" 75 | msgstr "Randa radiuso" 76 | 77 | #: src/preferences/pages/edit-shadow.ui:177 78 | #: src/preferences/pages/edit-shadow.ui:317 79 | msgid "Opacity" 80 | msgstr "Opako" 81 | 82 | #: src/preferences/pages/edit-shadow.ui:206 83 | msgid "Unfocused shadow prefrences" 84 | msgstr "" 85 | 86 | #: src/preferences/pages/general.ui:6 87 | msgid "General" 88 | msgstr "Ĝenerala" 89 | 90 | #: src/preferences/pages/general.ui:10 91 | msgid "Applications" 92 | msgstr "Programoj" 93 | 94 | #: src/preferences/pages/general.ui:11 95 | msgid "" 96 | "Some applications created with LibAdwaita or LibHandy already have rounded " 97 | "corners. You can enable those settings to avoid rounding their corners an " 98 | "extra time." 99 | msgstr "" 100 | 101 | #: src/preferences/pages/general.ui:14 102 | #, fuzzy 103 | msgid "Skip LibAdwaita applications" 104 | msgstr "Preterpasi libadwaita-programojn" 105 | 106 | #: src/preferences/pages/general.ui:19 107 | #, fuzzy 108 | msgid "Skip LibHandy applications" 109 | msgstr "Preterpasi libhandy-programojn" 110 | 111 | #: src/preferences/pages/general.ui:26 112 | msgid "Global settings" 113 | msgstr "" 114 | 115 | #: src/preferences/pages/general.ui:27 116 | msgid "These settings will have an effect on all windows" 117 | msgstr "" 118 | 119 | #: src/preferences/pages/general.ui:30 120 | #, fuzzy 121 | msgid "Border width" 122 | msgstr "Randa larĝo" 123 | 124 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 125 | #: src/preferences/widgets/custom_settings_row.ts:19 126 | #, fuzzy 127 | msgid "Border color" 128 | msgstr "Randa koloro" 129 | 130 | #: src/preferences/pages/general.ui:74 131 | #: src/preferences/widgets/custom_settings_row.ts:27 132 | #, fuzzy 133 | msgid "Corner radius" 134 | msgstr "Randa radiuso" 135 | 136 | #: src/preferences/pages/general.ui:101 137 | #: src/preferences/widgets/custom_settings_row.ts:37 138 | msgid "Corner smoothing" 139 | msgstr "" 140 | 141 | #: src/preferences/pages/general.ui:127 142 | msgid "Window shadow" 143 | msgstr "" 144 | 145 | #: src/preferences/pages/general.ui:128 146 | msgid "Customize the shadow of the rounded corner window" 147 | msgstr "" 148 | 149 | #: src/preferences/pages/general.ui:140 150 | msgid "Keep rounded corners for maximized windows" 151 | msgstr "" 152 | 153 | #: src/preferences/pages/general.ui:145 154 | msgid "Keep rounded corners for fullscreen windows" 155 | msgstr "" 156 | 157 | #: src/preferences/pages/general.ui:155 158 | msgid "Tweaks" 159 | msgstr "" 160 | 161 | #: src/preferences/pages/general.ui:158 162 | msgid "Add rounded corners to Kitty Terminal on Wayland" 163 | msgstr "" 164 | 165 | #: src/preferences/pages/general.ui:159 166 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 167 | msgstr "" 168 | 169 | #: src/preferences/pages/general.ui:164 170 | msgid "Add a settings entry in right-click menu of the desktop" 171 | msgstr "" 172 | 173 | #: src/preferences/pages/general.ui:171 174 | msgid "Debug" 175 | msgstr "" 176 | 177 | #: src/preferences/pages/general.ui:174 178 | msgid "Enable debug logs" 179 | msgstr "" 180 | 181 | #: src/preferences/pages/general.ui:175 182 | msgid "" 183 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 184 | msgstr "" 185 | 186 | #: src/preferences/pages/general.ui:184 187 | msgid "Reset preferences" 188 | msgstr "" 189 | 190 | #: src/preferences/pages/reset.ui:6 191 | msgid "Reset settings" 192 | msgstr "" 193 | 194 | #: src/preferences/pages/reset.ui:16 195 | msgid "Settings to reset" 196 | msgstr "" 197 | 198 | #: src/preferences/pages/reset.ui:22 199 | #, fuzzy 200 | msgid "Select all" 201 | msgstr "Elekti ĉion" 202 | 203 | #: src/preferences/pages/reset.ui:39 204 | msgid "Reset" 205 | msgstr "" 206 | 207 | #: src/preferences/pages/reset.ui:55 208 | msgid "Reset these settings?" 209 | msgstr "" 210 | 211 | #: src/preferences/pages/reset.ui:60 212 | msgid "_Cancel" 213 | msgstr "" 214 | 215 | #: src/preferences/pages/reset.ui:61 216 | msgid "_Reset" 217 | msgstr "" 218 | 219 | #: src/preferences/widgets/paddings-row.ui:12 220 | msgid "Paddings" 221 | msgstr "" 222 | 223 | #: src/preferences/widgets/paddings-row.ui:25 224 | msgid "Top" 225 | msgstr "Supro" 226 | 227 | #: src/preferences/widgets/paddings-row.ui:40 228 | msgid "Bottom" 229 | msgstr "Malsupro" 230 | 231 | #: src/preferences/widgets/paddings-row.ui:55 232 | msgid "Left" 233 | msgstr "Maldekstro" 234 | 235 | #: src/preferences/widgets/paddings-row.ui:70 236 | msgid "Right" 237 | msgstr "Dekstro" 238 | 239 | #: src/preferences/widgets/app_row.ts:33 240 | msgid "Window class" 241 | msgstr "" 242 | 243 | #: src/preferences/widgets/app_row.ts:54 244 | msgid "Expand this row, to pick a window" 245 | msgstr "" 246 | 247 | #: src/preferences/widgets/app_row.ts:94 248 | msgid "Can't pick window from this position" 249 | msgstr "" 250 | 251 | #: src/preferences/widgets/custom_settings_row.ts:15 252 | #, fuzzy 253 | msgid "Enabled" 254 | msgstr "Ŝalti" 255 | 256 | #: src/preferences/widgets/custom_settings_row.ts:47 257 | msgid "Keep rounded corners when maximized" 258 | msgstr "" 259 | 260 | #: src/preferences/widgets/custom_settings_row.ts:49 261 | msgid "Always clip rounded corners even if window is maximized or tiled" 262 | msgstr "" 263 | 264 | #: src/preferences/widgets/custom_settings_row.ts:53 265 | msgid "Keep rounded corners when in fullscreen" 266 | msgstr "" 267 | 268 | #: src/preferences/widgets/custom_settings_row.ts:54 269 | msgid "Always clip rounded corners even for fullscreen window" 270 | msgstr "" 271 | 272 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 273 | msgid "Rounded Corners Settings..." 274 | msgstr "" 275 | 276 | #~ msgid "Skip LibAdwaita Applications" 277 | #~ msgstr "Preterpasi libadwaita-programojn" 278 | 279 | #~ msgid "Skip LibHandy Applications" 280 | #~ msgstr "Preterpasi libhandy-programojn" 281 | 282 | #~ msgid "Border Width" 283 | #~ msgstr "Randa larĝo" 284 | 285 | #~ msgid "Border Color" 286 | #~ msgstr "Randa koloro" 287 | 288 | #~ msgid "Border Radius" 289 | #~ msgstr "Randa radiuso" 290 | 291 | #~ msgid "Apply" 292 | #~ msgstr "Efektivigi" 293 | -------------------------------------------------------------------------------- /po/et.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 5 | "Language: et\n" 6 | "Content-Type: text/plain; charset=UTF-8\n" 7 | "Content-Transfer-Encoding: 8bit\n" 8 | 9 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 10 | msgid "Blacklist" 11 | msgstr "" 12 | 13 | #: src/preferences/pages/blacklist.ui:11 14 | msgid "" 15 | "Not all application can works well with rounded corners effects, add them to " 16 | "this list to disable effects." 17 | msgstr "" 18 | 19 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 20 | msgid "Add window" 21 | msgstr "" 22 | 23 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 24 | msgid "Custom" 25 | msgstr "" 26 | 27 | #: src/preferences/pages/custom.ui:11 28 | msgid "Set custom effect setting for each window class" 29 | msgstr "" 30 | 31 | #: src/preferences/pages/edit-shadow.ui:6 32 | msgid "Edit shadows" 33 | msgstr "" 34 | 35 | #: src/preferences/pages/edit-shadow.ui:28 36 | msgid "Focused window" 37 | msgstr "" 38 | 39 | #: src/preferences/pages/edit-shadow.ui:43 40 | msgid "Unfocused window" 41 | msgstr "" 42 | 43 | #: src/preferences/pages/edit-shadow.ui:66 44 | msgid "Focused shadow prefrences" 45 | msgstr "" 46 | 47 | #: src/preferences/pages/edit-shadow.ui:69 48 | #: src/preferences/pages/edit-shadow.ui:209 49 | msgid "Horizontal offset" 50 | msgstr "" 51 | 52 | #: src/preferences/pages/edit-shadow.ui:96 53 | #: src/preferences/pages/edit-shadow.ui:236 54 | msgid "Vertical offset" 55 | msgstr "" 56 | 57 | #: src/preferences/pages/edit-shadow.ui:123 58 | #: src/preferences/pages/edit-shadow.ui:263 59 | msgid "Blur radius" 60 | msgstr "" 61 | 62 | #: src/preferences/pages/edit-shadow.ui:150 63 | #: src/preferences/pages/edit-shadow.ui:290 64 | msgid "Spread radius" 65 | msgstr "" 66 | 67 | #: src/preferences/pages/edit-shadow.ui:177 68 | #: src/preferences/pages/edit-shadow.ui:317 69 | msgid "Opacity" 70 | msgstr "" 71 | 72 | #: src/preferences/pages/edit-shadow.ui:206 73 | msgid "Unfocused shadow prefrences" 74 | msgstr "" 75 | 76 | #: src/preferences/pages/general.ui:6 77 | msgid "General" 78 | msgstr "" 79 | 80 | #: src/preferences/pages/general.ui:10 81 | msgid "Applications" 82 | msgstr "" 83 | 84 | #: src/preferences/pages/general.ui:11 85 | msgid "" 86 | "Some applications created with LibAdwaita or LibHandy already have rounded " 87 | "corners. You can enable those settings to avoid rounding their corners an " 88 | "extra time." 89 | msgstr "" 90 | 91 | #: src/preferences/pages/general.ui:14 92 | msgid "Skip LibAdwaita applications" 93 | msgstr "" 94 | 95 | #: src/preferences/pages/general.ui:19 96 | msgid "Skip LibHandy applications" 97 | msgstr "" 98 | 99 | #: src/preferences/pages/general.ui:26 100 | msgid "Global settings" 101 | msgstr "" 102 | 103 | #: src/preferences/pages/general.ui:27 104 | msgid "These settings will have an effect on all windows" 105 | msgstr "" 106 | 107 | #: src/preferences/pages/general.ui:30 108 | msgid "Border width" 109 | msgstr "" 110 | 111 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 112 | #: src/preferences/widgets/custom_settings_row.ts:19 113 | msgid "Border color" 114 | msgstr "" 115 | 116 | #: src/preferences/pages/general.ui:74 117 | #: src/preferences/widgets/custom_settings_row.ts:27 118 | msgid "Corner radius" 119 | msgstr "" 120 | 121 | #: src/preferences/pages/general.ui:101 122 | #: src/preferences/widgets/custom_settings_row.ts:37 123 | msgid "Corner smoothing" 124 | msgstr "" 125 | 126 | #: src/preferences/pages/general.ui:127 127 | msgid "Window shadow" 128 | msgstr "" 129 | 130 | #: src/preferences/pages/general.ui:128 131 | msgid "Customize the shadow of the rounded corner window" 132 | msgstr "" 133 | 134 | #: src/preferences/pages/general.ui:140 135 | msgid "Keep rounded corners for maximized windows" 136 | msgstr "" 137 | 138 | #: src/preferences/pages/general.ui:145 139 | msgid "Keep rounded corners for fullscreen windows" 140 | msgstr "" 141 | 142 | #: src/preferences/pages/general.ui:155 143 | msgid "Tweaks" 144 | msgstr "" 145 | 146 | #: src/preferences/pages/general.ui:158 147 | msgid "Add rounded corners to Kitty Terminal on Wayland" 148 | msgstr "" 149 | 150 | #: src/preferences/pages/general.ui:159 151 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 152 | msgstr "" 153 | 154 | #: src/preferences/pages/general.ui:164 155 | msgid "Add a settings entry in right-click menu of the desktop" 156 | msgstr "" 157 | 158 | #: src/preferences/pages/general.ui:171 159 | msgid "Debug" 160 | msgstr "" 161 | 162 | #: src/preferences/pages/general.ui:174 163 | msgid "Enable debug logs" 164 | msgstr "" 165 | 166 | #: src/preferences/pages/general.ui:175 167 | msgid "" 168 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 169 | msgstr "" 170 | 171 | #: src/preferences/pages/general.ui:184 172 | msgid "Reset preferences" 173 | msgstr "" 174 | 175 | #: src/preferences/pages/reset.ui:6 176 | msgid "Reset settings" 177 | msgstr "" 178 | 179 | #: src/preferences/pages/reset.ui:16 180 | msgid "Settings to reset" 181 | msgstr "" 182 | 183 | #: src/preferences/pages/reset.ui:22 184 | msgid "Select all" 185 | msgstr "" 186 | 187 | #: src/preferences/pages/reset.ui:39 188 | msgid "Reset" 189 | msgstr "" 190 | 191 | #: src/preferences/pages/reset.ui:55 192 | msgid "Reset these settings?" 193 | msgstr "" 194 | 195 | #: src/preferences/pages/reset.ui:60 196 | msgid "_Cancel" 197 | msgstr "" 198 | 199 | #: src/preferences/pages/reset.ui:61 200 | msgid "_Reset" 201 | msgstr "" 202 | 203 | #: src/preferences/widgets/paddings-row.ui:12 204 | msgid "Paddings" 205 | msgstr "" 206 | 207 | #: src/preferences/widgets/paddings-row.ui:25 208 | msgid "Top" 209 | msgstr "" 210 | 211 | #: src/preferences/widgets/paddings-row.ui:40 212 | msgid "Bottom" 213 | msgstr "" 214 | 215 | #: src/preferences/widgets/paddings-row.ui:55 216 | msgid "Left" 217 | msgstr "" 218 | 219 | #: src/preferences/widgets/paddings-row.ui:70 220 | msgid "Right" 221 | msgstr "" 222 | 223 | #: src/preferences/widgets/app_row.ts:33 224 | msgid "Window class" 225 | msgstr "" 226 | 227 | #: src/preferences/widgets/app_row.ts:54 228 | msgid "Expand this row, to pick a window" 229 | msgstr "" 230 | 231 | #: src/preferences/widgets/app_row.ts:94 232 | msgid "Can't pick window from this position" 233 | msgstr "" 234 | 235 | #: src/preferences/widgets/custom_settings_row.ts:15 236 | msgid "Enabled" 237 | msgstr "" 238 | 239 | #: src/preferences/widgets/custom_settings_row.ts:47 240 | msgid "Keep rounded corners when maximized" 241 | msgstr "" 242 | 243 | #: src/preferences/widgets/custom_settings_row.ts:49 244 | msgid "Always clip rounded corners even if window is maximized or tiled" 245 | msgstr "" 246 | 247 | #: src/preferences/widgets/custom_settings_row.ts:53 248 | msgid "Keep rounded corners when in fullscreen" 249 | msgstr "" 250 | 251 | #: src/preferences/widgets/custom_settings_row.ts:54 252 | msgid "Always clip rounded corners even for fullscreen window" 253 | msgstr "" 254 | 255 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 256 | msgid "Rounded Corners Settings..." 257 | msgstr "" 258 | -------------------------------------------------------------------------------- /po/hu.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: 10\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 6 | "PO-Revision-Date: 2023-10-01 14:00+0000\n" 7 | "Last-Translator: olevo \n" 8 | "Language-Team: Hungarian \n" 10 | "Language: hu\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 14 | "X-Generator: Weblate 5.1-dev\n" 15 | 16 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 17 | msgid "Blacklist" 18 | msgstr "" 19 | 20 | #: src/preferences/pages/blacklist.ui:11 21 | msgid "" 22 | "Not all application can works well with rounded corners effects, add them to " 23 | "this list to disable effects." 24 | msgstr "" 25 | 26 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 27 | msgid "Add window" 28 | msgstr "" 29 | 30 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 31 | msgid "Custom" 32 | msgstr "Egyéni" 33 | 34 | #: src/preferences/pages/custom.ui:11 35 | msgid "Set custom effect setting for each window class" 36 | msgstr "" 37 | 38 | #: src/preferences/pages/edit-shadow.ui:6 39 | msgid "Edit shadows" 40 | msgstr "" 41 | 42 | #: src/preferences/pages/edit-shadow.ui:28 43 | #, fuzzy 44 | msgid "Focused window" 45 | msgstr "Ablak előtérbe helyezése" 46 | 47 | #: src/preferences/pages/edit-shadow.ui:43 48 | #, fuzzy 49 | msgid "Unfocused window" 50 | msgstr "Ablak előtérbe helyezése" 51 | 52 | #: src/preferences/pages/edit-shadow.ui:66 53 | msgid "Focused shadow prefrences" 54 | msgstr "" 55 | 56 | #: src/preferences/pages/edit-shadow.ui:69 57 | #: src/preferences/pages/edit-shadow.ui:209 58 | msgid "Horizontal offset" 59 | msgstr "" 60 | 61 | #: src/preferences/pages/edit-shadow.ui:96 62 | #: src/preferences/pages/edit-shadow.ui:236 63 | msgid "Vertical offset" 64 | msgstr "" 65 | 66 | #: src/preferences/pages/edit-shadow.ui:123 67 | #: src/preferences/pages/edit-shadow.ui:263 68 | #, fuzzy 69 | msgid "Blur radius" 70 | msgstr "A border radius vastagsága" 71 | 72 | #: src/preferences/pages/edit-shadow.ui:150 73 | #: src/preferences/pages/edit-shadow.ui:290 74 | #, fuzzy 75 | msgid "Spread radius" 76 | msgstr "A border radius vastagsága" 77 | 78 | #: src/preferences/pages/edit-shadow.ui:177 79 | #: src/preferences/pages/edit-shadow.ui:317 80 | msgid "Opacity" 81 | msgstr "Áttettszőség" 82 | 83 | #: src/preferences/pages/edit-shadow.ui:206 84 | msgid "Unfocused shadow prefrences" 85 | msgstr "" 86 | 87 | #: src/preferences/pages/general.ui:6 88 | msgid "General" 89 | msgstr "Általános" 90 | 91 | #: src/preferences/pages/general.ui:10 92 | msgid "Applications" 93 | msgstr "" 94 | 95 | #: src/preferences/pages/general.ui:11 96 | msgid "" 97 | "Some applications created with LibAdwaita or LibHandy already have rounded " 98 | "corners. You can enable those settings to avoid rounding their corners an " 99 | "extra time." 100 | msgstr "" 101 | 102 | #: src/preferences/pages/general.ui:14 103 | msgid "Skip LibAdwaita applications" 104 | msgstr "" 105 | 106 | #: src/preferences/pages/general.ui:19 107 | msgid "Skip LibHandy applications" 108 | msgstr "" 109 | 110 | #: src/preferences/pages/general.ui:26 111 | #, fuzzy 112 | msgid "Global settings" 113 | msgstr "Globál beállítások" 114 | 115 | #: src/preferences/pages/general.ui:27 116 | msgid "These settings will have an effect on all windows" 117 | msgstr "" 118 | 119 | #: src/preferences/pages/general.ui:30 120 | #, fuzzy 121 | msgid "Border width" 122 | msgstr "A border szélessége" 123 | 124 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 125 | #: src/preferences/widgets/custom_settings_row.ts:19 126 | #, fuzzy 127 | msgid "Border color" 128 | msgstr "A border széle" 129 | 130 | #: src/preferences/pages/general.ui:74 131 | #: src/preferences/widgets/custom_settings_row.ts:27 132 | #, fuzzy 133 | msgid "Corner radius" 134 | msgstr "A border radius vastagsága" 135 | 136 | #: src/preferences/pages/general.ui:101 137 | #: src/preferences/widgets/custom_settings_row.ts:37 138 | #, fuzzy 139 | msgid "Corner smoothing" 140 | msgstr "A sarkok simítása/simasága" 141 | 142 | #: src/preferences/pages/general.ui:127 143 | msgid "Window shadow" 144 | msgstr "" 145 | 146 | #: src/preferences/pages/general.ui:128 147 | msgid "Customize the shadow of the rounded corner window" 148 | msgstr "" 149 | 150 | #: src/preferences/pages/general.ui:140 151 | msgid "Keep rounded corners for maximized windows" 152 | msgstr "" 153 | 154 | #: src/preferences/pages/general.ui:145 155 | msgid "Keep rounded corners for fullscreen windows" 156 | msgstr "" 157 | 158 | #: src/preferences/pages/general.ui:155 159 | msgid "Tweaks" 160 | msgstr "" 161 | 162 | #: src/preferences/pages/general.ui:158 163 | msgid "Add rounded corners to Kitty Terminal on Wayland" 164 | msgstr "" 165 | 166 | #: src/preferences/pages/general.ui:159 167 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 168 | msgstr "" 169 | 170 | #: src/preferences/pages/general.ui:164 171 | msgid "Add a settings entry in right-click menu of the desktop" 172 | msgstr "" 173 | 174 | #: src/preferences/pages/general.ui:171 175 | msgid "Debug" 176 | msgstr "Debug" 177 | 178 | #: src/preferences/pages/general.ui:174 179 | #, fuzzy 180 | msgid "Enable debug logs" 181 | msgstr "Napló bekapcsolása" 182 | 183 | #: src/preferences/pages/general.ui:175 184 | msgid "" 185 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 186 | msgstr "" 187 | 188 | #: src/preferences/pages/general.ui:184 189 | msgid "Reset preferences" 190 | msgstr "" 191 | 192 | #: src/preferences/pages/reset.ui:6 193 | msgid "Reset settings" 194 | msgstr "" 195 | 196 | #: src/preferences/pages/reset.ui:16 197 | msgid "Settings to reset" 198 | msgstr "" 199 | 200 | #: src/preferences/pages/reset.ui:22 201 | msgid "Select all" 202 | msgstr "" 203 | 204 | #: src/preferences/pages/reset.ui:39 205 | msgid "Reset" 206 | msgstr "" 207 | 208 | #: src/preferences/pages/reset.ui:55 209 | msgid "Reset these settings?" 210 | msgstr "" 211 | 212 | #: src/preferences/pages/reset.ui:60 213 | msgid "_Cancel" 214 | msgstr "" 215 | 216 | #: src/preferences/pages/reset.ui:61 217 | msgid "_Reset" 218 | msgstr "" 219 | 220 | #: src/preferences/widgets/paddings-row.ui:12 221 | msgid "Paddings" 222 | msgstr "" 223 | 224 | #: src/preferences/widgets/paddings-row.ui:25 225 | msgid "Top" 226 | msgstr "" 227 | 228 | #: src/preferences/widgets/paddings-row.ui:40 229 | msgid "Bottom" 230 | msgstr "Alja" 231 | 232 | #: src/preferences/widgets/paddings-row.ui:55 233 | msgid "Left" 234 | msgstr "Bal" 235 | 236 | #: src/preferences/widgets/paddings-row.ui:70 237 | msgid "Right" 238 | msgstr "Jobb" 239 | 240 | #: src/preferences/widgets/app_row.ts:33 241 | msgid "Window class" 242 | msgstr "" 243 | 244 | #: src/preferences/widgets/app_row.ts:54 245 | msgid "Expand this row, to pick a window" 246 | msgstr "" 247 | 248 | #: src/preferences/widgets/app_row.ts:94 249 | msgid "Can't pick window from this position" 250 | msgstr "" 251 | 252 | #: src/preferences/widgets/custom_settings_row.ts:15 253 | #, fuzzy 254 | msgid "Enabled" 255 | msgstr "Bekapcsolás" 256 | 257 | #: src/preferences/widgets/custom_settings_row.ts:47 258 | msgid "Keep rounded corners when maximized" 259 | msgstr "" 260 | 261 | #: src/preferences/widgets/custom_settings_row.ts:49 262 | msgid "Always clip rounded corners even if window is maximized or tiled" 263 | msgstr "" 264 | 265 | #: src/preferences/widgets/custom_settings_row.ts:53 266 | msgid "Keep rounded corners when in fullscreen" 267 | msgstr "" 268 | 269 | #: src/preferences/widgets/custom_settings_row.ts:54 270 | msgid "Always clip rounded corners even for fullscreen window" 271 | msgstr "" 272 | 273 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 274 | msgid "Rounded Corners Settings..." 275 | msgstr "" 276 | 277 | #~ msgid "Border Width" 278 | #~ msgstr "A border szélessége" 279 | 280 | #, fuzzy 281 | #~ msgid "Border Color" 282 | #~ msgstr "A border széle" 283 | 284 | #~ msgid "Border Radius" 285 | #~ msgstr "A border radius vastagsága" 286 | 287 | #~ msgid "Corner Smoothing" 288 | #~ msgstr "A sarkok simítása/simasága" 289 | 290 | #~ msgid "Can't add to list, because it has exists" 291 | #~ msgstr "Sikertelen hozzáadás, ez már létezik" 292 | -------------------------------------------------------------------------------- /po/ja.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 5 | "PO-Revision-Date: 2023-03-21 15:37+0000\n" 6 | "Last-Translator: maboroshin \n" 7 | "Language-Team: Japanese \n" 9 | "Language: ja\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=1; plural=0;\n" 13 | "X-Generator: Weblate 4.16.2-dev\n" 14 | 15 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 16 | msgid "Blacklist" 17 | msgstr "ブラックリスト" 18 | 19 | #: src/preferences/pages/blacklist.ui:11 20 | #, fuzzy 21 | msgid "" 22 | "Not all application can works well with rounded corners effects, add them to " 23 | "this list to disable effects." 24 | msgstr "" 25 | "すべてのアプリで角丸の効果がうまく効くわけではないため、この一覧に追加して無" 26 | "効にしてください。\n" 27 | "\n" 28 | "一覧の項目は、ウィンドウの WM_CLASS プロパティのインスタンス部分です。一覧に" 29 | "これを追加するウィンドウを選択できます。" 30 | 31 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 32 | #, fuzzy 33 | msgid "Add window" 34 | msgstr "ウィンドウ追加" 35 | 36 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 37 | msgid "Custom" 38 | msgstr "カスタマイズ" 39 | 40 | #: src/preferences/pages/custom.ui:11 41 | #, fuzzy 42 | msgid "Set custom effect setting for each window class" 43 | msgstr "このウィンドウの独自設定を有効化" 44 | 45 | #: src/preferences/pages/edit-shadow.ui:6 46 | msgid "Edit shadows" 47 | msgstr "" 48 | 49 | #: src/preferences/pages/edit-shadow.ui:28 50 | msgid "Focused window" 51 | msgstr "" 52 | 53 | #: src/preferences/pages/edit-shadow.ui:43 54 | msgid "Unfocused window" 55 | msgstr "" 56 | 57 | #: src/preferences/pages/edit-shadow.ui:66 58 | msgid "Focused shadow prefrences" 59 | msgstr "" 60 | 61 | #: src/preferences/pages/edit-shadow.ui:69 62 | #: src/preferences/pages/edit-shadow.ui:209 63 | #, fuzzy 64 | msgid "Horizontal offset" 65 | msgstr "水平 補正" 66 | 67 | #: src/preferences/pages/edit-shadow.ui:96 68 | #: src/preferences/pages/edit-shadow.ui:236 69 | #, fuzzy 70 | msgid "Vertical offset" 71 | msgstr "垂直 補正" 72 | 73 | #: src/preferences/pages/edit-shadow.ui:123 74 | #: src/preferences/pages/edit-shadow.ui:263 75 | #, fuzzy 76 | msgid "Blur radius" 77 | msgstr "境界線の角丸サイズ" 78 | 79 | #: src/preferences/pages/edit-shadow.ui:150 80 | #: src/preferences/pages/edit-shadow.ui:290 81 | #, fuzzy 82 | msgid "Spread radius" 83 | msgstr "境界線の角丸サイズ" 84 | 85 | #: src/preferences/pages/edit-shadow.ui:177 86 | #: src/preferences/pages/edit-shadow.ui:317 87 | msgid "Opacity" 88 | msgstr "不透明度" 89 | 90 | #: src/preferences/pages/edit-shadow.ui:206 91 | msgid "Unfocused shadow prefrences" 92 | msgstr "" 93 | 94 | #: src/preferences/pages/general.ui:6 95 | msgid "General" 96 | msgstr "一般" 97 | 98 | #: src/preferences/pages/general.ui:10 99 | msgid "Applications" 100 | msgstr "アプリケーション" 101 | 102 | #: src/preferences/pages/general.ui:11 103 | #, fuzzy 104 | msgid "" 105 | "Some applications created with LibAdwaita or LibHandy already have rounded " 106 | "corners. You can enable those settings to avoid rounding their corners an " 107 | "extra time." 108 | msgstr "" 109 | "LibAdwaita や LibHandy を使ったアプリには、すでに角丸なものがあるので、この設" 110 | "定で無視することができます" 111 | 112 | #: src/preferences/pages/general.ui:14 113 | #, fuzzy 114 | msgid "Skip LibAdwaita applications" 115 | msgstr "LibAdwaita のアプリは無視" 116 | 117 | #: src/preferences/pages/general.ui:19 118 | #, fuzzy 119 | msgid "Skip LibHandy applications" 120 | msgstr "LibHandy のアプリは無視" 121 | 122 | #: src/preferences/pages/general.ui:26 123 | #, fuzzy 124 | msgid "Global settings" 125 | msgstr "全般設定" 126 | 127 | #: src/preferences/pages/general.ui:27 128 | #, fuzzy 129 | msgid "These settings will have an effect on all windows" 130 | msgstr "この設定はすべてのウィンドウへの効果を試みます" 131 | 132 | #: src/preferences/pages/general.ui:30 133 | #, fuzzy 134 | msgid "Border width" 135 | msgstr "境界線の幅" 136 | 137 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 138 | #: src/preferences/widgets/custom_settings_row.ts:19 139 | #, fuzzy 140 | msgid "Border color" 141 | msgstr "境界線の色" 142 | 143 | #: src/preferences/pages/general.ui:74 144 | #: src/preferences/widgets/custom_settings_row.ts:27 145 | #, fuzzy 146 | msgid "Corner radius" 147 | msgstr "境界線の角丸サイズ" 148 | 149 | #: src/preferences/pages/general.ui:101 150 | #: src/preferences/widgets/custom_settings_row.ts:37 151 | #, fuzzy 152 | msgid "Corner smoothing" 153 | msgstr "角を滑らかに" 154 | 155 | #: src/preferences/pages/general.ui:127 156 | #, fuzzy 157 | msgid "Window shadow" 158 | msgstr "ウィンドウの影" 159 | 160 | #: src/preferences/pages/general.ui:128 161 | msgid "Customize the shadow of the rounded corner window" 162 | msgstr "角丸ウィンドウの影を指定" 163 | 164 | #: src/preferences/pages/general.ui:140 165 | #, fuzzy 166 | msgid "Keep rounded corners for maximized windows" 167 | msgstr "最大化時に角丸を維持" 168 | 169 | #: src/preferences/pages/general.ui:145 170 | #, fuzzy 171 | msgid "Keep rounded corners for fullscreen windows" 172 | msgstr "全画面時に角丸を維持" 173 | 174 | #: src/preferences/pages/general.ui:155 175 | msgid "Tweaks" 176 | msgstr "" 177 | 178 | #: src/preferences/pages/general.ui:158 179 | msgid "Add rounded corners to Kitty Terminal on Wayland" 180 | msgstr "" 181 | 182 | #: src/preferences/pages/general.ui:159 183 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 184 | msgstr "" 185 | 186 | #: src/preferences/pages/general.ui:164 187 | #, fuzzy 188 | msgid "Add a settings entry in right-click menu of the desktop" 189 | msgstr "背景の右クリックメニューに設定項目を追加" 190 | 191 | #: src/preferences/pages/general.ui:171 192 | msgid "Debug" 193 | msgstr "デバッグ" 194 | 195 | #: src/preferences/pages/general.ui:174 196 | #, fuzzy 197 | msgid "Enable debug logs" 198 | msgstr "ログを有効化" 199 | 200 | #: src/preferences/pages/general.ui:175 201 | #, fuzzy 202 | msgid "" 203 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 204 | msgstr "" 205 | "ログ閲覧にはターミナルで journalctl -o cat -f /usr/bin/gnome-shell を" 206 | "実行" 207 | 208 | #: src/preferences/pages/general.ui:184 209 | #, fuzzy 210 | msgid "Reset preferences" 211 | msgstr "設定を初期化" 212 | 213 | #: src/preferences/pages/reset.ui:6 214 | msgid "Reset settings" 215 | msgstr "" 216 | 217 | #: src/preferences/pages/reset.ui:16 218 | #, fuzzy 219 | msgid "Settings to reset" 220 | msgstr "初期化する項目を選択" 221 | 222 | #: src/preferences/pages/reset.ui:22 223 | #, fuzzy 224 | msgid "Select all" 225 | msgstr "すべて選択" 226 | 227 | #: src/preferences/pages/reset.ui:39 228 | msgid "Reset" 229 | msgstr "" 230 | 231 | #: src/preferences/pages/reset.ui:55 232 | msgid "Reset these settings?" 233 | msgstr "" 234 | 235 | #: src/preferences/pages/reset.ui:60 236 | msgid "_Cancel" 237 | msgstr "" 238 | 239 | #: src/preferences/pages/reset.ui:61 240 | msgid "_Reset" 241 | msgstr "" 242 | 243 | #: src/preferences/widgets/paddings-row.ui:12 244 | msgid "Paddings" 245 | msgstr "" 246 | 247 | #: src/preferences/widgets/paddings-row.ui:25 248 | msgid "Top" 249 | msgstr "上" 250 | 251 | #: src/preferences/widgets/paddings-row.ui:40 252 | msgid "Bottom" 253 | msgstr "下" 254 | 255 | #: src/preferences/widgets/paddings-row.ui:55 256 | msgid "Left" 257 | msgstr "左" 258 | 259 | #: src/preferences/widgets/paddings-row.ui:70 260 | msgid "Right" 261 | msgstr "右" 262 | 263 | #: src/preferences/widgets/app_row.ts:33 264 | #, fuzzy 265 | msgid "Window class" 266 | msgstr "ウィンドウの影" 267 | 268 | #: src/preferences/widgets/app_row.ts:54 269 | #, fuzzy 270 | msgid "Expand this row, to pick a window" 271 | msgstr "この行を展開しウィンドウを選択。" 272 | 273 | #: src/preferences/widgets/app_row.ts:94 274 | #, fuzzy 275 | msgid "Can't pick window from this position" 276 | msgstr "この位置からウィンドウを選択できません" 277 | 278 | #: src/preferences/widgets/custom_settings_row.ts:15 279 | #, fuzzy 280 | msgid "Enabled" 281 | msgstr "有効化" 282 | 283 | #: src/preferences/widgets/custom_settings_row.ts:47 284 | #, fuzzy 285 | msgid "Keep rounded corners when maximized" 286 | msgstr "最大化時に角丸を維持" 287 | 288 | #: src/preferences/widgets/custom_settings_row.ts:49 289 | #, fuzzy 290 | msgid "Always clip rounded corners even if window is maximized or tiled" 291 | msgstr "ウィンドウの最大化や並べて表示でも角丸を維持。" 292 | 293 | #: src/preferences/widgets/custom_settings_row.ts:53 294 | #, fuzzy 295 | msgid "Keep rounded corners when in fullscreen" 296 | msgstr "全画面時に角丸を維持" 297 | 298 | #: src/preferences/widgets/custom_settings_row.ts:54 299 | #, fuzzy 300 | msgid "Always clip rounded corners even for fullscreen window" 301 | msgstr "全画面時でも角丸を維持。" 302 | 303 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 304 | msgid "Rounded Corners Settings..." 305 | msgstr "角丸の設定..." 306 | 307 | #, fuzzy 308 | #~ msgid "Open prefereces reset page" 309 | #~ msgstr "設定初期化のダイアログを開く" 310 | 311 | #~ msgid "Skip LibAdwaita Applications" 312 | #~ msgstr "LibAdwaita のアプリは無視" 313 | 314 | #~ msgid "Skip LibHandy Applications" 315 | #~ msgstr "LibHandy のアプリは無視" 316 | 317 | #~ msgid "Border Width" 318 | #~ msgstr "境界線の幅" 319 | 320 | #~ msgid "Border Color" 321 | #~ msgstr "境界線の色" 322 | 323 | #~ msgid "Border Radius" 324 | #~ msgstr "境界線の角丸サイズ" 325 | 326 | #~ msgid "Keep Rounded Corners when Maximized or Fullscreen" 327 | #~ msgstr "最大化や全画面時に角丸を維持" 328 | 329 | #~ msgid "Corner Smoothing" 330 | #~ msgstr "角を滑らかに" 331 | 332 | #~ msgid "Expand this row to pick a window." 333 | #~ msgstr "この行を展開しウィンドウを選択。" 334 | 335 | #~ msgid "" 336 | #~ "Setup different clip padding by add window into this list.\n" 337 | #~ "\n" 338 | #~ "The item of list is instance part of WM_CLASS property with window. You " 339 | #~ "can pick a window to add it into this list." 340 | #~ msgstr "" 341 | #~ "この一覧にウィンドウを追加してそれぞれの clip padding を設定できます。\n" 342 | #~ "\n" 343 | #~ "一覧の項目は、ウィンドウの WM_CLASS プロパティのインスタンス部分です。一覧" 344 | #~ "にこれを追加するウィンドウを選択できます。" 345 | 346 | #~ msgid "Remove Window from this List" 347 | #~ msgstr "この一覧からウィドウを除去" 348 | 349 | #~ msgid "Pick Window to add into list" 350 | #~ msgstr "一覧に追加するウィンドウを選択" 351 | 352 | #~ msgid "Apply" 353 | #~ msgstr "適用" 354 | 355 | #~ msgid "Blur Offset" 356 | #~ msgstr "ぼかし補正" 357 | 358 | #~ msgid "Can't add to list, because it has exists" 359 | #~ msgstr "既に存在するため、一覧に追加できません" 360 | 361 | #~ msgid "Edit Shadow for Rounded Corners Windows" 362 | #~ msgstr "角丸ウィンドウの影を編集" 363 | -------------------------------------------------------------------------------- /po/nb_NO.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 5 | "PO-Revision-Date: 2022-09-15 06:16+0000\n" 6 | "Last-Translator: Allan Nordhøy \n" 7 | "Language-Team: Norwegian Bokmål \n" 9 | "Language: nb_NO\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 13 | "X-Generator: Weblate 4.14.1-dev\n" 14 | 15 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 16 | msgid "Blacklist" 17 | msgstr "Svarteliste" 18 | 19 | #: src/preferences/pages/blacklist.ui:11 20 | msgid "" 21 | "Not all application can works well with rounded corners effects, add them to " 22 | "this list to disable effects." 23 | msgstr "" 24 | 25 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 26 | #, fuzzy 27 | msgid "Add window" 28 | msgstr "Legg til vindu" 29 | 30 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 31 | msgid "Custom" 32 | msgstr "" 33 | 34 | #: src/preferences/pages/custom.ui:11 35 | msgid "Set custom effect setting for each window class" 36 | msgstr "" 37 | 38 | #: src/preferences/pages/edit-shadow.ui:6 39 | msgid "Edit shadows" 40 | msgstr "" 41 | 42 | #: src/preferences/pages/edit-shadow.ui:28 43 | msgid "Focused window" 44 | msgstr "" 45 | 46 | #: src/preferences/pages/edit-shadow.ui:43 47 | msgid "Unfocused window" 48 | msgstr "" 49 | 50 | #: src/preferences/pages/edit-shadow.ui:66 51 | msgid "Focused shadow prefrences" 52 | msgstr "" 53 | 54 | #: src/preferences/pages/edit-shadow.ui:69 55 | #: src/preferences/pages/edit-shadow.ui:209 56 | msgid "Horizontal offset" 57 | msgstr "" 58 | 59 | #: src/preferences/pages/edit-shadow.ui:96 60 | #: src/preferences/pages/edit-shadow.ui:236 61 | msgid "Vertical offset" 62 | msgstr "" 63 | 64 | #: src/preferences/pages/edit-shadow.ui:123 65 | #: src/preferences/pages/edit-shadow.ui:263 66 | msgid "Blur radius" 67 | msgstr "" 68 | 69 | #: src/preferences/pages/edit-shadow.ui:150 70 | #: src/preferences/pages/edit-shadow.ui:290 71 | msgid "Spread radius" 72 | msgstr "" 73 | 74 | #: src/preferences/pages/edit-shadow.ui:177 75 | #: src/preferences/pages/edit-shadow.ui:317 76 | msgid "Opacity" 77 | msgstr "" 78 | 79 | #: src/preferences/pages/edit-shadow.ui:206 80 | msgid "Unfocused shadow prefrences" 81 | msgstr "" 82 | 83 | #: src/preferences/pages/general.ui:6 84 | msgid "General" 85 | msgstr "" 86 | 87 | #: src/preferences/pages/general.ui:10 88 | msgid "Applications" 89 | msgstr "Programmer" 90 | 91 | #: src/preferences/pages/general.ui:11 92 | msgid "" 93 | "Some applications created with LibAdwaita or LibHandy already have rounded " 94 | "corners. You can enable those settings to avoid rounding their corners an " 95 | "extra time." 96 | msgstr "" 97 | 98 | #: src/preferences/pages/general.ui:14 99 | #, fuzzy 100 | msgid "Skip LibAdwaita applications" 101 | msgstr "Programmer" 102 | 103 | #: src/preferences/pages/general.ui:19 104 | msgid "Skip LibHandy applications" 105 | msgstr "" 106 | 107 | #: src/preferences/pages/general.ui:26 108 | msgid "Global settings" 109 | msgstr "" 110 | 111 | #: src/preferences/pages/general.ui:27 112 | msgid "These settings will have an effect on all windows" 113 | msgstr "" 114 | 115 | #: src/preferences/pages/general.ui:30 116 | msgid "Border width" 117 | msgstr "" 118 | 119 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 120 | #: src/preferences/widgets/custom_settings_row.ts:19 121 | msgid "Border color" 122 | msgstr "" 123 | 124 | #: src/preferences/pages/general.ui:74 125 | #: src/preferences/widgets/custom_settings_row.ts:27 126 | msgid "Corner radius" 127 | msgstr "" 128 | 129 | #: src/preferences/pages/general.ui:101 130 | #: src/preferences/widgets/custom_settings_row.ts:37 131 | msgid "Corner smoothing" 132 | msgstr "" 133 | 134 | #: src/preferences/pages/general.ui:127 135 | msgid "Window shadow" 136 | msgstr "" 137 | 138 | #: src/preferences/pages/general.ui:128 139 | msgid "Customize the shadow of the rounded corner window" 140 | msgstr "" 141 | 142 | #: src/preferences/pages/general.ui:140 143 | #, fuzzy 144 | msgid "Keep rounded corners for maximized windows" 145 | msgstr "Behold avrundede hjørner når vinduet er maksimert eller flislagt." 146 | 147 | #: src/preferences/pages/general.ui:145 148 | #, fuzzy 149 | msgid "Keep rounded corners for fullscreen windows" 150 | msgstr "Behold avrundede vinduer når vindu er i fullskjermsvisning." 151 | 152 | #: src/preferences/pages/general.ui:155 153 | msgid "Tweaks" 154 | msgstr "" 155 | 156 | #: src/preferences/pages/general.ui:158 157 | msgid "Add rounded corners to Kitty Terminal on Wayland" 158 | msgstr "" 159 | 160 | #: src/preferences/pages/general.ui:159 161 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 162 | msgstr "" 163 | 164 | #: src/preferences/pages/general.ui:164 165 | #, fuzzy 166 | msgid "Add a settings entry in right-click menu of the desktop" 167 | msgstr "Legg til innstillingsoppføring i høyreklikksmenyen for bakgrunn" 168 | 169 | #: src/preferences/pages/general.ui:171 170 | msgid "Debug" 171 | msgstr "" 172 | 173 | #: src/preferences/pages/general.ui:174 174 | msgid "Enable debug logs" 175 | msgstr "" 176 | 177 | #: src/preferences/pages/general.ui:175 178 | msgid "" 179 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 180 | msgstr "" 181 | 182 | #: src/preferences/pages/general.ui:184 183 | msgid "Reset preferences" 184 | msgstr "" 185 | 186 | #: src/preferences/pages/reset.ui:6 187 | msgid "Reset settings" 188 | msgstr "" 189 | 190 | #: src/preferences/pages/reset.ui:16 191 | msgid "Settings to reset" 192 | msgstr "" 193 | 194 | #: src/preferences/pages/reset.ui:22 195 | msgid "Select all" 196 | msgstr "" 197 | 198 | #: src/preferences/pages/reset.ui:39 199 | msgid "Reset" 200 | msgstr "" 201 | 202 | #: src/preferences/pages/reset.ui:55 203 | msgid "Reset these settings?" 204 | msgstr "" 205 | 206 | #: src/preferences/pages/reset.ui:60 207 | msgid "_Cancel" 208 | msgstr "" 209 | 210 | #: src/preferences/pages/reset.ui:61 211 | msgid "_Reset" 212 | msgstr "" 213 | 214 | #: src/preferences/widgets/paddings-row.ui:12 215 | msgid "Paddings" 216 | msgstr "" 217 | 218 | #: src/preferences/widgets/paddings-row.ui:25 219 | msgid "Top" 220 | msgstr "" 221 | 222 | #: src/preferences/widgets/paddings-row.ui:40 223 | msgid "Bottom" 224 | msgstr "" 225 | 226 | #: src/preferences/widgets/paddings-row.ui:55 227 | msgid "Left" 228 | msgstr "" 229 | 230 | #: src/preferences/widgets/paddings-row.ui:70 231 | msgid "Right" 232 | msgstr "" 233 | 234 | #: src/preferences/widgets/app_row.ts:33 235 | msgid "Window class" 236 | msgstr "" 237 | 238 | #: src/preferences/widgets/app_row.ts:54 239 | msgid "Expand this row, to pick a window" 240 | msgstr "" 241 | 242 | #: src/preferences/widgets/app_row.ts:94 243 | msgid "Can't pick window from this position" 244 | msgstr "" 245 | 246 | #: src/preferences/widgets/custom_settings_row.ts:15 247 | msgid "Enabled" 248 | msgstr "" 249 | 250 | #: src/preferences/widgets/custom_settings_row.ts:47 251 | #, fuzzy 252 | msgid "Keep rounded corners when maximized" 253 | msgstr "Behold avrundede hjørner når vinduet er maksimert eller flislagt." 254 | 255 | #: src/preferences/widgets/custom_settings_row.ts:49 256 | #, fuzzy 257 | msgid "Always clip rounded corners even if window is maximized or tiled" 258 | msgstr "Behold avrundede hjørner når vinduet er maksimert eller flislagt." 259 | 260 | #: src/preferences/widgets/custom_settings_row.ts:53 261 | #, fuzzy 262 | msgid "Keep rounded corners when in fullscreen" 263 | msgstr "Behold avrundede vinduer når vindu er i fullskjermsvisning." 264 | 265 | #: src/preferences/widgets/custom_settings_row.ts:54 266 | #, fuzzy 267 | msgid "Always clip rounded corners even for fullscreen window" 268 | msgstr "Behold avrundede vinduer når vindu er i fullskjermsvisning." 269 | 270 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 271 | msgid "Rounded Corners Settings..." 272 | msgstr "" 273 | 274 | #~ msgid "Apply" 275 | #~ msgstr "Bruk" 276 | -------------------------------------------------------------------------------- /po/rounded-window-corners@fxgn.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-01-31 09:07+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 21 | msgid "Blacklist" 22 | msgstr "" 23 | 24 | #: src/preferences/pages/blacklist.ui:11 25 | msgid "" 26 | "Not all application can works well with rounded corners effects, add them to " 27 | "this list to disable effects." 28 | msgstr "" 29 | 30 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 31 | msgid "Add window" 32 | msgstr "" 33 | 34 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 35 | msgid "Custom" 36 | msgstr "" 37 | 38 | #: src/preferences/pages/custom.ui:11 39 | msgid "Set custom effect setting for each window class" 40 | msgstr "" 41 | 42 | #: src/preferences/pages/edit-shadow.ui:6 43 | msgid "Edit shadows" 44 | msgstr "" 45 | 46 | #: src/preferences/pages/edit-shadow.ui:28 47 | msgid "Focused window" 48 | msgstr "" 49 | 50 | #: src/preferences/pages/edit-shadow.ui:43 51 | msgid "Unfocused window" 52 | msgstr "" 53 | 54 | #: src/preferences/pages/edit-shadow.ui:66 55 | msgid "Focused shadow prefrences" 56 | msgstr "" 57 | 58 | #: src/preferences/pages/edit-shadow.ui:69 59 | #: src/preferences/pages/edit-shadow.ui:209 60 | msgid "Horizontal offset" 61 | msgstr "" 62 | 63 | #: src/preferences/pages/edit-shadow.ui:96 64 | #: src/preferences/pages/edit-shadow.ui:236 65 | msgid "Vertical offset" 66 | msgstr "" 67 | 68 | #: src/preferences/pages/edit-shadow.ui:123 69 | #: src/preferences/pages/edit-shadow.ui:263 70 | msgid "Blur radius" 71 | msgstr "" 72 | 73 | #: src/preferences/pages/edit-shadow.ui:150 74 | #: src/preferences/pages/edit-shadow.ui:290 75 | msgid "Spread radius" 76 | msgstr "" 77 | 78 | #: src/preferences/pages/edit-shadow.ui:177 79 | #: src/preferences/pages/edit-shadow.ui:317 80 | msgid "Opacity" 81 | msgstr "" 82 | 83 | #: src/preferences/pages/edit-shadow.ui:206 84 | msgid "Unfocused shadow prefrences" 85 | msgstr "" 86 | 87 | #: src/preferences/pages/general.ui:6 88 | msgid "General" 89 | msgstr "" 90 | 91 | #: src/preferences/pages/general.ui:10 92 | msgid "Applications" 93 | msgstr "" 94 | 95 | #: src/preferences/pages/general.ui:11 96 | msgid "" 97 | "Some applications created with LibAdwaita or LibHandy already have rounded " 98 | "corners. You can enable those settings to avoid rounding their corners an " 99 | "extra time." 100 | msgstr "" 101 | 102 | #: src/preferences/pages/general.ui:14 103 | msgid "Skip LibAdwaita applications" 104 | msgstr "" 105 | 106 | #: src/preferences/pages/general.ui:19 107 | msgid "Skip LibHandy applications" 108 | msgstr "" 109 | 110 | #: src/preferences/pages/general.ui:26 111 | msgid "Global settings" 112 | msgstr "" 113 | 114 | #: src/preferences/pages/general.ui:27 115 | msgid "These settings will have an effect on all windows" 116 | msgstr "" 117 | 118 | #: src/preferences/pages/general.ui:30 119 | msgid "Border width" 120 | msgstr "" 121 | 122 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 123 | #: src/preferences/widgets/custom_settings_row.ts:19 124 | msgid "Border color" 125 | msgstr "" 126 | 127 | #: src/preferences/pages/general.ui:74 128 | #: src/preferences/widgets/custom_settings_row.ts:27 129 | msgid "Corner radius" 130 | msgstr "" 131 | 132 | #: src/preferences/pages/general.ui:101 133 | #: src/preferences/widgets/custom_settings_row.ts:37 134 | msgid "Corner smoothing" 135 | msgstr "" 136 | 137 | #: src/preferences/pages/general.ui:127 138 | msgid "Window shadow" 139 | msgstr "" 140 | 141 | #: src/preferences/pages/general.ui:128 142 | msgid "Customize the shadow of the rounded corner window" 143 | msgstr "" 144 | 145 | #: src/preferences/pages/general.ui:140 146 | msgid "Keep rounded corners for maximized windows" 147 | msgstr "" 148 | 149 | #: src/preferences/pages/general.ui:145 150 | msgid "Keep rounded corners for fullscreen windows" 151 | msgstr "" 152 | 153 | #: src/preferences/pages/general.ui:155 154 | msgid "Tweaks" 155 | msgstr "" 156 | 157 | #: src/preferences/pages/general.ui:158 158 | msgid "Add rounded corners to Kitty Terminal on Wayland" 159 | msgstr "" 160 | 161 | #: src/preferences/pages/general.ui:159 162 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 163 | msgstr "" 164 | 165 | #: src/preferences/pages/general.ui:164 166 | msgid "Add a settings entry in right-click menu of the desktop" 167 | msgstr "" 168 | 169 | #: src/preferences/pages/general.ui:171 170 | msgid "Debug" 171 | msgstr "" 172 | 173 | #: src/preferences/pages/general.ui:174 174 | msgid "Enable debug logs" 175 | msgstr "" 176 | 177 | #: src/preferences/pages/general.ui:175 178 | msgid "" 179 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 180 | msgstr "" 181 | 182 | #: src/preferences/pages/general.ui:184 183 | msgid "Reset preferences" 184 | msgstr "" 185 | 186 | #: src/preferences/pages/reset.ui:6 187 | msgid "Reset settings" 188 | msgstr "" 189 | 190 | #: src/preferences/pages/reset.ui:16 191 | msgid "Settings to reset" 192 | msgstr "" 193 | 194 | #: src/preferences/pages/reset.ui:22 195 | msgid "Select all" 196 | msgstr "" 197 | 198 | #: src/preferences/pages/reset.ui:39 199 | msgid "Reset" 200 | msgstr "" 201 | 202 | #: src/preferences/pages/reset.ui:55 203 | msgid "Reset these settings?" 204 | msgstr "" 205 | 206 | #: src/preferences/pages/reset.ui:60 207 | msgid "_Cancel" 208 | msgstr "" 209 | 210 | #: src/preferences/pages/reset.ui:61 211 | msgid "_Reset" 212 | msgstr "" 213 | 214 | #: src/preferences/widgets/paddings-row.ui:12 215 | msgid "Paddings" 216 | msgstr "" 217 | 218 | #: src/preferences/widgets/paddings-row.ui:25 219 | msgid "Top" 220 | msgstr "" 221 | 222 | #: src/preferences/widgets/paddings-row.ui:40 223 | msgid "Bottom" 224 | msgstr "" 225 | 226 | #: src/preferences/widgets/paddings-row.ui:55 227 | msgid "Left" 228 | msgstr "" 229 | 230 | #: src/preferences/widgets/paddings-row.ui:70 231 | msgid "Right" 232 | msgstr "" 233 | 234 | #: src/preferences/widgets/app_row.ts:33 235 | msgid "Window class" 236 | msgstr "" 237 | 238 | #: src/preferences/widgets/app_row.ts:54 239 | msgid "Expand this row, to pick a window" 240 | msgstr "" 241 | 242 | #: src/preferences/widgets/app_row.ts:94 243 | msgid "Can't pick window from this position" 244 | msgstr "" 245 | 246 | #: src/preferences/widgets/custom_settings_row.ts:15 247 | msgid "Enabled" 248 | msgstr "" 249 | 250 | #: src/preferences/widgets/custom_settings_row.ts:47 251 | msgid "Keep rounded corners when maximized" 252 | msgstr "" 253 | 254 | #: src/preferences/widgets/custom_settings_row.ts:49 255 | msgid "Always clip rounded corners even if window is maximized or tiled" 256 | msgstr "" 257 | 258 | #: src/preferences/widgets/custom_settings_row.ts:53 259 | msgid "Keep rounded corners when in fullscreen" 260 | msgstr "" 261 | 262 | #: src/preferences/widgets/custom_settings_row.ts:54 263 | msgid "Always clip rounded corners even for fullscreen window" 264 | msgstr "" 265 | 266 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 267 | msgid "Rounded Corners Settings..." 268 | msgstr "" 269 | -------------------------------------------------------------------------------- /po/zh_CN.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2025-01-14 10:38+0100\n" 5 | "PO-Revision-Date: 2023-03-02 12:35+0000\n" 6 | "Last-Translator: Yi \n" 7 | "Language-Team: Chinese (Simplified) \n" 9 | "Language: zh_CN\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=1; plural=0;\n" 13 | "X-Generator: Weblate 4.16.2-dev\n" 14 | 15 | #: src/preferences/pages/blacklist.ui:6 src/preferences/pages/blacklist.ui:10 16 | msgid "Blacklist" 17 | msgstr "黑名单" 18 | 19 | #: src/preferences/pages/blacklist.ui:11 20 | #, fuzzy 21 | msgid "" 22 | "Not all application can works well with rounded corners effects, add them to " 23 | "this list to disable effects." 24 | msgstr "" 25 | "并非所有应用程序在使用圆角效果时都能够良好运行,可以将其添加到黑名单中以关闭" 26 | "圆角。\n" 27 | "\n" 28 | "每一项代表窗口的 WM_CLASS 属性,可以在添加窗口后选取窗口。" 29 | 30 | #: src/preferences/pages/blacklist.ui:19 src/preferences/pages/custom.ui:19 31 | #, fuzzy 32 | msgid "Add window" 33 | msgstr "添加窗口" 34 | 35 | #: src/preferences/pages/custom.ui:6 src/preferences/pages/custom.ui:10 36 | msgid "Custom" 37 | msgstr "自定义" 38 | 39 | #: src/preferences/pages/custom.ui:11 40 | #, fuzzy 41 | msgid "Set custom effect setting for each window class" 42 | msgstr "为窗口启用自定义的配置" 43 | 44 | #: src/preferences/pages/edit-shadow.ui:6 45 | msgid "Edit shadows" 46 | msgstr "" 47 | 48 | #: src/preferences/pages/edit-shadow.ui:28 49 | #, fuzzy 50 | msgid "Focused window" 51 | msgstr "已获取焦点的窗口" 52 | 53 | #: src/preferences/pages/edit-shadow.ui:43 54 | #, fuzzy 55 | msgid "Unfocused window" 56 | msgstr "未获取焦点的窗口" 57 | 58 | #: src/preferences/pages/edit-shadow.ui:66 59 | msgid "Focused shadow prefrences" 60 | msgstr "" 61 | 62 | #: src/preferences/pages/edit-shadow.ui:69 63 | #: src/preferences/pages/edit-shadow.ui:209 64 | #, fuzzy 65 | msgid "Horizontal offset" 66 | msgstr "水平偏移" 67 | 68 | #: src/preferences/pages/edit-shadow.ui:96 69 | #: src/preferences/pages/edit-shadow.ui:236 70 | #, fuzzy 71 | msgid "Vertical offset" 72 | msgstr "垂直偏移" 73 | 74 | #: src/preferences/pages/edit-shadow.ui:123 75 | #: src/preferences/pages/edit-shadow.ui:263 76 | #, fuzzy 77 | msgid "Blur radius" 78 | msgstr "圆角半径" 79 | 80 | #: src/preferences/pages/edit-shadow.ui:150 81 | #: src/preferences/pages/edit-shadow.ui:290 82 | #, fuzzy 83 | msgid "Spread radius" 84 | msgstr "扩散半径" 85 | 86 | #: src/preferences/pages/edit-shadow.ui:177 87 | #: src/preferences/pages/edit-shadow.ui:317 88 | msgid "Opacity" 89 | msgstr "透明度" 90 | 91 | #: src/preferences/pages/edit-shadow.ui:206 92 | msgid "Unfocused shadow prefrences" 93 | msgstr "" 94 | 95 | #: src/preferences/pages/general.ui:6 96 | msgid "General" 97 | msgstr "通用" 98 | 99 | #: src/preferences/pages/general.ui:10 100 | msgid "Applications" 101 | msgstr "应用设置" 102 | 103 | #: src/preferences/pages/general.ui:11 104 | #, fuzzy 105 | msgid "" 106 | "Some applications created with LibAdwaita or LibHandy already have rounded " 107 | "corners. You can enable those settings to avoid rounding their corners an " 108 | "extra time." 109 | msgstr "" 110 | "一些使用 LibAdwaita 与 LibHandy 编写的应用已经自带圆角,因此可以通过此选项跳" 111 | "过" 112 | 113 | #: src/preferences/pages/general.ui:14 114 | #, fuzzy 115 | msgid "Skip LibAdwaita applications" 116 | msgstr "跳过 LibAdwaita 应用" 117 | 118 | #: src/preferences/pages/general.ui:19 119 | #, fuzzy 120 | msgid "Skip LibHandy applications" 121 | msgstr "跳过 LibHandy 应用" 122 | 123 | #: src/preferences/pages/general.ui:26 124 | #, fuzzy 125 | msgid "Global settings" 126 | msgstr "全局设置" 127 | 128 | #: src/preferences/pages/general.ui:27 129 | #, fuzzy 130 | msgid "These settings will have an effect on all windows" 131 | msgstr "这里的设置将影响所有窗口" 132 | 133 | #: src/preferences/pages/general.ui:30 134 | #, fuzzy 135 | msgid "Border width" 136 | msgstr "边框大小" 137 | 138 | #: src/preferences/pages/general.ui:57 src/preferences/pages/general.ui:65 139 | #: src/preferences/widgets/custom_settings_row.ts:19 140 | #, fuzzy 141 | msgid "Border color" 142 | msgstr "边框颜色" 143 | 144 | #: src/preferences/pages/general.ui:74 145 | #: src/preferences/widgets/custom_settings_row.ts:27 146 | #, fuzzy 147 | msgid "Corner radius" 148 | msgstr "圆角半径" 149 | 150 | #: src/preferences/pages/general.ui:101 151 | #: src/preferences/widgets/custom_settings_row.ts:37 152 | #, fuzzy 153 | msgid "Corner smoothing" 154 | msgstr "圆角平滑程度" 155 | 156 | #: src/preferences/pages/general.ui:127 157 | #, fuzzy 158 | msgid "Window shadow" 159 | msgstr "窗口阴影" 160 | 161 | #: src/preferences/pages/general.ui:128 162 | msgid "Customize the shadow of the rounded corner window" 163 | msgstr "设置圆角窗口的阴影" 164 | 165 | #: src/preferences/pages/general.ui:140 166 | #, fuzzy 167 | msgid "Keep rounded corners for maximized windows" 168 | msgstr "最大化时保留圆角" 169 | 170 | #: src/preferences/pages/general.ui:145 171 | #, fuzzy 172 | msgid "Keep rounded corners for fullscreen windows" 173 | msgstr "窗口全屏时保留圆角" 174 | 175 | #: src/preferences/pages/general.ui:155 176 | msgid "Tweaks" 177 | msgstr "调整" 178 | 179 | #: src/preferences/pages/general.ui:158 180 | #, fuzzy 181 | msgid "Add rounded corners to Kitty Terminal on Wayland" 182 | msgstr "在 Wayland 下为 Kitty 终端添加圆角效果" 183 | 184 | #: src/preferences/pages/general.ui:159 185 | msgid "Adjust paddings for Kitty Terminal to correctly round its corners" 186 | msgstr "" 187 | 188 | #: src/preferences/pages/general.ui:164 189 | #, fuzzy 190 | msgid "Add a settings entry in right-click menu of the desktop" 191 | msgstr "将首选项入口添加到桌面背景的右键菜单" 192 | 193 | #: src/preferences/pages/general.ui:171 194 | msgid "Debug" 195 | msgstr "调试" 196 | 197 | #: src/preferences/pages/general.ui:174 198 | #, fuzzy 199 | msgid "Enable debug logs" 200 | msgstr "启用日志" 201 | 202 | #: src/preferences/pages/general.ui:175 203 | #, fuzzy 204 | msgid "" 205 | "Run journalctl -o cat -f /usr/bin/gnome-shell in your terminal to see the log" 206 | msgstr "" 207 | "可以在终端里运行 journalctl -o cat -f /usr/bin/gnome-shell 来查看日志" 208 | 209 | #: src/preferences/pages/general.ui:184 210 | #, fuzzy 211 | msgid "Reset preferences" 212 | msgstr "重置设置" 213 | 214 | #: src/preferences/pages/reset.ui:6 215 | msgid "Reset settings" 216 | msgstr "" 217 | 218 | #: src/preferences/pages/reset.ui:16 219 | #, fuzzy 220 | msgid "Settings to reset" 221 | msgstr "选择要重置的设置" 222 | 223 | #: src/preferences/pages/reset.ui:22 224 | #, fuzzy 225 | msgid "Select all" 226 | msgstr "全选" 227 | 228 | #: src/preferences/pages/reset.ui:39 229 | msgid "Reset" 230 | msgstr "" 231 | 232 | #: src/preferences/pages/reset.ui:55 233 | msgid "Reset these settings?" 234 | msgstr "" 235 | 236 | #: src/preferences/pages/reset.ui:60 237 | msgid "_Cancel" 238 | msgstr "" 239 | 240 | #: src/preferences/pages/reset.ui:61 241 | msgid "_Reset" 242 | msgstr "" 243 | 244 | #: src/preferences/widgets/paddings-row.ui:12 245 | msgid "Paddings" 246 | msgstr "剪裁距离" 247 | 248 | #: src/preferences/widgets/paddings-row.ui:25 249 | msgid "Top" 250 | msgstr "上边距" 251 | 252 | #: src/preferences/widgets/paddings-row.ui:40 253 | msgid "Bottom" 254 | msgstr "下边距" 255 | 256 | #: src/preferences/widgets/paddings-row.ui:55 257 | msgid "Left" 258 | msgstr "左边距" 259 | 260 | #: src/preferences/widgets/paddings-row.ui:70 261 | msgid "Right" 262 | msgstr "右边距" 263 | 264 | #: src/preferences/widgets/app_row.ts:33 265 | #, fuzzy 266 | msgid "Window class" 267 | msgstr "窗口阴影" 268 | 269 | #: src/preferences/widgets/app_row.ts:54 270 | #, fuzzy 271 | msgid "Expand this row, to pick a window" 272 | msgstr "展开此控件以选取窗口。" 273 | 274 | #: src/preferences/widgets/app_row.ts:94 275 | #, fuzzy 276 | msgid "Can't pick window from this position" 277 | msgstr "无法从此位置选取窗口" 278 | 279 | #: src/preferences/widgets/custom_settings_row.ts:15 280 | #, fuzzy 281 | msgid "Enabled" 282 | msgstr "启用" 283 | 284 | #: src/preferences/widgets/custom_settings_row.ts:47 285 | #, fuzzy 286 | msgid "Keep rounded corners when maximized" 287 | msgstr "最大化时保留圆角" 288 | 289 | #: src/preferences/widgets/custom_settings_row.ts:49 290 | #, fuzzy 291 | msgid "Always clip rounded corners even if window is maximized or tiled" 292 | msgstr "窗口最大化或垂直平铺时保留圆角。" 293 | 294 | #: src/preferences/widgets/custom_settings_row.ts:53 295 | #, fuzzy 296 | msgid "Keep rounded corners when in fullscreen" 297 | msgstr "窗口全屏时保留圆角" 298 | 299 | #: src/preferences/widgets/custom_settings_row.ts:54 300 | #, fuzzy 301 | msgid "Always clip rounded corners even for fullscreen window" 302 | msgstr "当窗口全屏时保留圆角效果。" 303 | 304 | #: src/utils/background_menu.ts:49 src/utils/background_menu.ts:74 305 | msgid "Rounded Corners Settings..." 306 | msgstr "圆角设置…" 307 | 308 | #~ msgid "Tweak clip paddings for Kitty Terminal" 309 | #~ msgstr "调整 Kitty 终端的剪裁半径" 310 | 311 | #, fuzzy 312 | #~ msgid "Open prefereces reset page" 313 | #~ msgstr "打开重置对话框" 314 | 315 | #~ msgid "Skip LibAdwaita Applications" 316 | #~ msgstr "跳过 LibAdwaita 应用" 317 | 318 | #~ msgid "Skip LibHandy Applications" 319 | #~ msgstr "跳过 LibHandy 应用" 320 | 321 | #~ msgid "Focus Window Shadow Style" 322 | #~ msgstr "已获取焦点窗口阴影" 323 | 324 | #~ msgid "Unfocus Window Shadow Style" 325 | #~ msgstr "未获取焦点窗口阴影" 326 | 327 | #~ msgid "Border Width" 328 | #~ msgstr "边框大小" 329 | 330 | #~ msgid "Border Color" 331 | #~ msgstr "边框颜色" 332 | 333 | #~ msgid "Border Radius" 334 | #~ msgstr "圆角半径" 335 | 336 | #~ msgid "Padding" 337 | #~ msgstr "剪裁边距" 338 | 339 | #~ msgid "Keep Rounded Corners when Maximized or Fullscreen" 340 | #~ msgstr "在窗口最大化或全屏时保留圆角" 341 | 342 | #~ msgid "Corner Smoothing" 343 | #~ msgstr "圆角平滑程度" 344 | 345 | #~ msgid "Expand this row to pick a window." 346 | #~ msgstr "展开此控件以选取窗口。" 347 | 348 | #~ msgid "" 349 | #~ "Setup different clip padding by add window into this list.\n" 350 | #~ "\n" 351 | #~ "The item of list is instance part of WM_CLASS property with window. You " 352 | #~ "can pick a window to add it into this list." 353 | #~ msgstr "" 354 | #~ "将窗口添加到此列表可以自定义圆角的裁剪距离。\n" 355 | #~ "\n" 356 | #~ "每一项代表窗口的 WM_CLASS 属性,可以在添加窗口后选取窗口。" 357 | 358 | #~ msgid "Remove Window from this List" 359 | #~ msgstr "从列表移除" 360 | 361 | #~ msgid "Pick Window to add into list" 362 | #~ msgstr "选取窗口" 363 | 364 | #~ msgid "Apply" 365 | #~ msgstr "应用" 366 | 367 | #~ msgid "Blur Offset" 368 | #~ msgstr "模糊半径" 369 | 370 | #~ msgid "Clip paddings of window" 371 | #~ msgstr "设置窗口的剪裁距离" 372 | 373 | #~ msgid "Can't add to list, because it has exists" 374 | #~ msgstr "已在列表中,无法添加" 375 | 376 | #~ msgid "Edit Shadow for Rounded Corners Windows" 377 | #~ msgstr "编辑圆角窗口阴影" 378 | -------------------------------------------------------------------------------- /resources/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rounded Window Corners Reborn", 3 | "description": "Add rounded corners to all windows. Fork of the now unmaintained Rounded Window Corners extension.", 4 | "uuid": "rounded-window-corners@fxgn", 5 | "url": "https://github.com/flexagoon/rounded-window-corners", 6 | "gettext-domain": "rounded-window-corners@fxgn", 7 | "settings-schema": "org.gnome.shell.extensions.rounded-window-corners-reborn", 8 | "shell-version": ["46", "47", "48"] 9 | } 10 | -------------------------------------------------------------------------------- /resources/schemas/org.gnome.shell.extensions.rounded-window-corners-reborn.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 0 11 | 12 | 13 | 14 | [] 15 | window here will not be rounded 16 | 17 | The contents of the list represent the instance part of the window's 18 | `WM_CLASS`, as same as the output of `xprop WM_CLASS|cut -d \" -f 2` 19 | 20 | 21 | 22 | 23 | Skip Libadwaita applications 24 | true 25 | 26 | 27 | 28 | Skip LibHandy applications 29 | false 30 | 31 | 32 | 33 | Border width for rounded corners window 34 | 35 | 0 36 | 37 | 38 | 39 | Global rounded corners settings for all windows 40 | 41 | 46 | { 47 | 'padding': <{ 48 | 'left': <uint32 1>, 49 | 'right': <uint32 1>, 50 | 'top': <uint32 1>, 51 | 'bottom': <uint32 1> 52 | }>, 53 | 'keepRoundedCorners': <{ 54 | 'maximized': <false>, 55 | 'fullscreen': <false> 56 | }>, 57 | 'borderRadius': <uint32 12>, 58 | 'smoothing': <0>, 59 | 'borderColor': <[0.5, 0.5, 0.5, 1.0]>, 60 | 'enabled': <true> 61 | } 62 | 63 | 64 | 65 | 66 | A directory to setup custom paddings for special windows 67 | {} 68 | 69 | 70 | 71 | 72 | Shadow for focused window 73 | 74 | { 75 | 'horizontalOffset': 0, 76 | 'verticalOffset': 4, 77 | 'blurOffset': 28, 78 | 'spreadRadius': 4, 79 | 'opacity': 60 80 | } 81 | 82 | 83 | 84 | 85 | Shadow for unfocused window 86 | 87 | { 88 | 'horizontalOffset': 0, 89 | 'verticalOffset': 2, 90 | 'blurOffset': 12, 91 | 'spreadRadius': -1, 92 | 'opacity': 65 93 | } 94 | 95 | 96 | 97 | 98 | If enabled, extension will show debug info into journalctl 99 | false 100 | 101 | 102 | 103 | false 104 | 105 | 106 | 107 | false 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /resources/stylesheet-prefs.css: -------------------------------------------------------------------------------- 1 | .page { 2 | padding: 10px 30px; 3 | } 4 | 5 | .code { 6 | padding: 10px 16px; 7 | } 8 | 9 | .edit-win { 10 | background: #eeeeee; 11 | color: black; 12 | padding: 20px 80px; 13 | } 14 | 15 | .flat { 16 | background: none; 17 | border: none; 18 | } 19 | 20 | .expander_img { 21 | transition: all 100ms; 22 | } 23 | 24 | .rotated { 25 | transform: rotate(90deg); 26 | } 27 | 28 | .dialog-vbox { 29 | padding: 48px; 30 | } 31 | 32 | .dialog-vbox .heading { 33 | padding-bottom: 24px; 34 | } 35 | 36 | .dialog-vbox checkbutton { 37 | padding-right: 12px; 38 | } 39 | -------------------------------------------------------------------------------- /resources/stylesheet.css: -------------------------------------------------------------------------------- 1 | .shadow { 2 | box-shadow: 0px 0px 0px 0px rgba(83, 83, 83, 0.0); 3 | transition-duration: 300ms; 4 | transition-property: box-shadow; 5 | transition-timing-function: linear; 6 | } 7 | -------------------------------------------------------------------------------- /src/effect/README.md: -------------------------------------------------------------------------------- 1 | # `effect` 2 | 3 | This directory contains the code for applying GLSL effects to windows. 4 | 5 | ## `clip_shadow_effect.ts` 6 | 7 | Due to a bug in GNOME, window shadows are drawn behind window contents. This 8 | effect loads a simple Fragment shader that clips the shadow behind the window. 9 | 10 | ## `linear_filter_effect.ts` 11 | 12 | This effect applies linear interpolation to the window, which makes windows 13 | in the overview look less blurry. 14 | 15 | ## `rounded_corners_effect.ts` 16 | 17 | This effect loads the actual Fragment shader that rounds the corners and draws 18 | custom borders for the window. The class applies the effect and provides a 19 | function to change uniforms passed to the effect. 20 | 21 | ## `shader` 22 | 23 | This is the directory where the Fragment shaders are stored. 24 | 25 | If you're interested in implementation details of the shader, you can read the 26 | `shader/rounded_corners.frag` file, which is well commented and explains how 27 | it works in great detail. 28 | -------------------------------------------------------------------------------- /src/effect/clip_shadow_effect.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Clips shadows for windows. 3 | * 4 | * Needed because of this issue: 5 | * https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4474 6 | */ 7 | 8 | import Cogl from 'gi://Cogl'; 9 | import GObject from 'gi://GObject'; 10 | import Shell from 'gi://Shell'; 11 | 12 | import {readShader} from '../utils/file.js'; 13 | 14 | const [declarations, code] = readShader( 15 | import.meta.url, 16 | 'shader/clip_shadow.frag', 17 | ); 18 | 19 | export const ClipShadowEffect = GObject.registerClass( 20 | {}, 21 | class extends Shell.GLSLEffect { 22 | vfunc_build_pipeline() { 23 | this.add_glsl_snippet( 24 | Cogl.SnippetHook.FRAGMENT, 25 | declarations, 26 | code, 27 | false, 28 | ); 29 | } 30 | }, 31 | ); 32 | -------------------------------------------------------------------------------- /src/effect/linear_filter_effect.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Applies linear interpolation to a window. This is used to make windows 3 | * in the overview look better. 4 | */ 5 | 6 | import Cogl from 'gi://Cogl'; 7 | import GObject from 'gi://GObject'; 8 | import Shell from 'gi://Shell'; 9 | 10 | import type Clutter from 'gi://Clutter'; 11 | 12 | export const LinearFilterEffect = GObject.registerClass( 13 | {}, 14 | class extends Shell.GLSLEffect { 15 | vfunc_build_pipeline(): void { 16 | this.add_glsl_snippet(Cogl.SnippetHook.FRAGMENT, '', '', false); 17 | } 18 | 19 | vfunc_paint_target( 20 | node: Clutter.PaintNode, 21 | ctx: Clutter.PaintContext, 22 | ): void { 23 | this.get_pipeline()?.set_layer_filters( 24 | 0, 25 | Cogl.PipelineFilter.LINEAR_MIPMAP_LINEAR, 26 | Cogl.PipelineFilter.LINEAR, 27 | ); 28 | super.vfunc_paint_target(node, ctx); 29 | } 30 | }, 31 | ); 32 | -------------------------------------------------------------------------------- /src/effect/rounded_corners_effect.ts: -------------------------------------------------------------------------------- 1 | /** @file Binds the actual corner rounding shader to the windows. */ 2 | 3 | import Cogl from 'gi://Cogl'; 4 | import GObject from 'gi://GObject'; 5 | import Shell from 'gi://Shell'; 6 | 7 | import {readShader} from '../utils/file.js'; 8 | import {getPref} from '../utils/settings.js'; 9 | 10 | import type {Bounds, RoundedCornerSettings} from '../utils/types.js'; 11 | 12 | const [declarations, code] = readShader( 13 | import.meta.url, 14 | 'shader/rounded_corners.frag', 15 | ); 16 | 17 | class Uniforms { 18 | bounds = 0; 19 | clipRadius = 0; 20 | borderWidth = 0; 21 | borderColor = 0; 22 | borderedAreaBounds = 0; 23 | borderedAreaClipRadius = 0; 24 | exponent = 0; 25 | pixelStep = 0; 26 | } 27 | 28 | export const RoundedCornersEffect = GObject.registerClass( 29 | {}, 30 | class Effect extends Shell.GLSLEffect { 31 | /** 32 | * To store a uniform value, we need to know its location in the shader, 33 | * which is done by calling `this.get_uniform_location()`. This is 34 | * expensive, so we cache the location of uniforms when the shader is 35 | * created. 36 | */ 37 | static uniforms: Uniforms = new Uniforms(); 38 | 39 | constructor() { 40 | super(); 41 | 42 | for (const k in Effect.uniforms) { 43 | Effect.uniforms[k as keyof Uniforms] = 44 | this.get_uniform_location(k); 45 | } 46 | } 47 | 48 | vfunc_build_pipeline() { 49 | this.add_glsl_snippet( 50 | Cogl.SnippetHook.FRAGMENT, 51 | declarations, 52 | code, 53 | false, 54 | ); 55 | } 56 | 57 | /** 58 | * Update uniforms of the shader. 59 | * For more information, see the comments in the shader file. 60 | * 61 | * @param scaleFactor - Desktop scaling factor 62 | * @param config - Rounded corners configuration 63 | * @param windowBounds - Bounds of the window without padding 64 | */ 65 | updateUniforms( 66 | scaleFactor: number, 67 | config: RoundedCornerSettings, 68 | windowBounds: Bounds, 69 | ) { 70 | const borderWidth = getPref('border-width') * scaleFactor; 71 | const borderColor = config.borderColor; 72 | 73 | const outerRadius = config.borderRadius * scaleFactor; 74 | const {padding, smoothing} = config; 75 | 76 | const bounds = [ 77 | windowBounds.x1 + padding.left * scaleFactor, 78 | windowBounds.y1 + padding.top * scaleFactor, 79 | windowBounds.x2 - padding.right * scaleFactor, 80 | windowBounds.y2 - padding.bottom * scaleFactor, 81 | ]; 82 | 83 | const borderedAreaBounds = [ 84 | bounds[0] + borderWidth, 85 | bounds[1] + borderWidth, 86 | bounds[2] - borderWidth, 87 | bounds[3] - borderWidth, 88 | ]; 89 | 90 | let borderedAreaRadius = outerRadius - borderWidth; 91 | if (borderedAreaRadius < 0.001) { 92 | borderedAreaRadius = 0.0; 93 | } 94 | 95 | const pixelStep = [ 96 | 1 / this.actor.get_width(), 97 | 1 / this.actor.get_height(), 98 | ]; 99 | 100 | // This is needed for squircle corners 101 | let exponent = smoothing * 10 + 2; 102 | let radius = outerRadius * 0.5 * exponent; 103 | const maxRadius = Math.min( 104 | bounds[3] - bounds[0], 105 | bounds[4] - bounds[1], 106 | ); 107 | if (radius > maxRadius) { 108 | exponent *= maxRadius / radius; 109 | radius = maxRadius; 110 | } 111 | borderedAreaRadius *= radius / outerRadius; 112 | 113 | this.#setUniforms( 114 | bounds, 115 | radius, 116 | borderWidth, 117 | borderColor, 118 | borderedAreaBounds, 119 | borderedAreaRadius, 120 | pixelStep, 121 | exponent, 122 | ); 123 | } 124 | 125 | #setUniforms( 126 | bounds: number[], 127 | radius: number, 128 | borderWidth: number, 129 | borderColor: [number, number, number, number], 130 | borderedAreaBounds: number[], 131 | borderedAreaRadius: number, 132 | pixelStep: number[], 133 | exponent: number, 134 | ) { 135 | const uniforms = Effect.uniforms; 136 | this.set_uniform_float(uniforms.bounds, 4, bounds); 137 | this.set_uniform_float(uniforms.clipRadius, 1, [radius]); 138 | this.set_uniform_float(uniforms.borderWidth, 1, [borderWidth]); 139 | this.set_uniform_float(uniforms.borderColor, 4, borderColor); 140 | this.set_uniform_float( 141 | uniforms.borderedAreaBounds, 142 | 4, 143 | borderedAreaBounds, 144 | ); 145 | this.set_uniform_float(uniforms.borderedAreaClipRadius, 1, [ 146 | borderedAreaRadius, 147 | ]); 148 | this.set_uniform_float(uniforms.pixelStep, 2, pixelStep); 149 | this.set_uniform_float(uniforms.exponent, 1, [exponent]); 150 | this.queue_repaint(); 151 | } 152 | }, 153 | ); 154 | -------------------------------------------------------------------------------- /src/effect/shader/clip_shadow.frag: -------------------------------------------------------------------------------- 1 | // Clip shadows to avoid showing them behind the window contents. 2 | void main() { 3 | vec4 color = cogl_color_out; 4 | float gray = (color.r + color.g + color.b) / 3.0; 5 | cogl_color_out *= (1.0 - smoothstep(0.4, 1.0, gray)) * color.a; 6 | } 7 | -------------------------------------------------------------------------------- /src/effect/shader/rounded_corners.frag: -------------------------------------------------------------------------------- 1 | // Based on this shader from Mutter: 2 | // https://gitlab.gnome.org/GNOME/mutter/-/blob/main/src/compositor/meta-background-content.c 3 | 4 | // This shader is what actually does the most important part of the extension - 5 | // corner rounding. 6 | // 7 | // The way Fragment shaders work in GNOME is that they receive the information 8 | // about a point on the screen, process it in some way, and set the `cogl_color_out` 9 | // variable which indicates what color should be drawn to the screen at that 10 | // point. 11 | // 12 | // Corner rounding is works by doing a bunch of simple calculations on 13 | // the point coordinates to determine if the point lies within the visible part 14 | // of the window, and making it invisible (multiplying `cogl_color_out` by 0) 15 | // if it's not. 16 | 17 | // First, we declare our uniforms. 18 | // Uniforms are parameters that are passed to the shader from external code. 19 | 20 | // Bounds of the window as {x, y, z, w}: 21 | // x - left 22 | // y - top 23 | // z - right 24 | // w - bottom 25 | uniform vec4 bounds; 26 | 27 | // The border radius of the corner clipping. 28 | uniform float clipRadius; 29 | 30 | // Width and color of the border 31 | uniform float borderWidth; 32 | uniform vec4 borderColor; 33 | 34 | // Bounds and clip radius of the area inside of borders. 35 | // When using inner borders, this is the smaller window area within those borders. 36 | // With outer borders, this is the total area of the window together with them. 37 | uniform vec4 borderedAreaBounds; 38 | uniform float borderedAreaClipRadius; 39 | 40 | // Exponent to use for squircle corners 41 | uniform float exponent; 42 | 43 | // I'm acually not sure what this does, but it's used in the original Mutter code :) 44 | // It seems like this is needed to convert between texture coordinates and pixel 45 | // coordinates, but I'm not sure. 46 | uniform vec2 pixelStep; 47 | 48 | // Calculate whether a point is within the rounded window when using regular 49 | // circular corner rounding. 50 | // 51 | // p | The point 52 | // center | The center of the circle used for border rounding 53 | // clipRadius | Border radius 54 | float circleBounds(vec2 p, vec2 center, float clipRadius) { 55 | // Get the distance from the circle center to the point 56 | vec2 delta = p - center; 57 | float distSquared = dot(delta, delta); 58 | 59 | // If the distance is larger than the border radius, the point is outside 60 | // of the rounded corner, and should be invisible. 61 | float outerRadius = clipRadius + 0.5; 62 | if (distSquared >= (outerRadius * outerRadius)) 63 | return 0.0; 64 | 65 | // Likewise, if it is smaller, the point is inside of the window. 66 | float borderedAreaRadius = clipRadius - 0.5; 67 | if (distSquared <= (borderedAreaRadius * borderedAreaRadius)) 68 | return 1.0; 69 | 70 | // If the difference between border radius and the distance is less than 71 | // 0.5, the point is located on the edge of the window, and should be 72 | // antialiased. 73 | // 74 | // Taking a square root is computationally expensive, so this is avoided as 75 | // much as possible. 76 | return outerRadius - sqrt(distSquared); 77 | } 78 | 79 | // Calculate whether a point is within the rounded window when using squircle 80 | // corner rounding. 81 | // 82 | // A squircle is what's called a "superellipse", which is defined by this formula: 83 | // (|x|^n + |y|^n)^(1/n) 84 | // 85 | // p | The point 86 | // center | The center of the circle used for border rounding 87 | // clipRadius | Border radius 88 | // exponent | The exponent (n) of the superellipse 89 | float squircleBounds(vec2 p, vec2 center, float clipRadius, float exponent) { 90 | // Get the distances from the circle center to the point (|x|, |y|) 91 | vec2 delta = abs(p - center); 92 | 93 | // Raise the distances to the given power (|x|^n, |y|^n) 94 | float powDx = pow(delta.x, exponent); 95 | float powDy = pow(delta.y, exponent); 96 | 97 | // Calculate the end result of the formula (|x|^n + |y|^n)^(1/n) 98 | float dist = pow(powDx + powDy, 1.0 / exponent); 99 | 100 | // Calculate the opacity for the point and normalize it between 0 and 1 101 | return clamp(clipRadius - dist + 0.5, 0.0, 1.0); 102 | } 103 | 104 | // Calculate the correct opacity for a given point within the window. 105 | // 106 | // p | The point 107 | // bounds | The bounds of the window 108 | // clipRadius | Border radius 109 | // exponent | The exponent (smoothness) of the squircle corners. 110 | // | See `squircleBounds` for more information. 111 | float getPointOpacity(vec2 p, vec4 bounds, float clipRadius, float exponent) { 112 | // If the point is completely outside of the window bounds, it should 113 | // obviously not be visible. 114 | if (p.x < bounds.x || p.x > bounds.z || p.y < bounds.y || p.y > bounds.w) 115 | return 0.0; 116 | 117 | // This is the center of the circle that is used for corner rounding. 118 | vec2 center; 119 | 120 | // First, we find the centers of the circles on the horizontal axis. 121 | // `centerLeft` is for two corners on the left of the window, and 122 | // `centerRight` is for corners on the right. 123 | float centerLeft = bounds.x + clipRadius; 124 | float centerRight = bounds.z - clipRadius; 125 | 126 | // If the point is closer to the window border than the circle center, it 127 | // has a chance of being within the rounded area, so we store the center 128 | // x position and continue. If it's not, then it should always be visible 129 | // and we can return 1.0 early to avoid extra computation (this is the case 130 | // for the vast majority of points). 131 | if (p.x < centerLeft) 132 | center.x = centerLeft; 133 | else if (p.x > centerRight) 134 | center.x = centerRight; 135 | else 136 | return 1.0; 137 | 138 | // Now we do the same, but for the vertical coordinate of the center. 139 | float centerTop = bounds.y + clipRadius; 140 | float centerBottom = bounds.w - clipRadius; 141 | 142 | if (p.y < centerTop) 143 | center.y = centerTop; 144 | else if (p.y > centerBottom) 145 | center.y = centerBottom; 146 | else 147 | return 1.0; 148 | 149 | // If the exponent is set, apply squircle bounds, otherwise use regular 150 | // circular ones. 151 | if (exponent <= 2.0) 152 | return circleBounds(p, center, clipRadius); 153 | else 154 | return squircleBounds(p, center, clipRadius, exponent); 155 | } 156 | 157 | // This is the main function of the shader. It's what is actually being called 158 | // when rendering a point to the screen. It has no return value; as explained 159 | // at the beginning of the file, the `cogl_color_out` variable is used to set 160 | // the point color. 161 | void main() { 162 | vec2 p = cogl_tex_coord0_in.xy / pixelStep; 163 | 164 | float pointAlpha = getPointOpacity(p, bounds, clipRadius, exponent); 165 | 166 | if (borderWidth > 0.9 || borderWidth < -0.9) { 167 | // If there is a border, we have to paint it. 168 | 169 | // Calculate if the point lies within the bordered area (see the 170 | // `borderedAreaBounds` uniform for an explanation of what this means) 171 | float borderedAreaAlpha = getPointOpacity(p, borderedAreaBounds, borderedAreaClipRadius, exponent); 172 | 173 | if (borderWidth > 0.0) { 174 | // Inner borders 175 | 176 | // Clip points that are not inside of the window 177 | cogl_color_out *= pointAlpha; 178 | // Calculate if the point is located on the border itself 179 | float borderAlpha = clamp(abs(pointAlpha - borderedAreaAlpha), 0.0, 1.0); 180 | // Then, mix the window color and the border color 181 | cogl_color_out = mix(cogl_color_out, vec4(borderColor.rgb, 1.0), borderAlpha * borderColor.a); 182 | } else { 183 | // Outer borders 184 | 185 | // If the point is within the bordered area, paint it with the 186 | // border color 187 | vec4 borderRect = vec4(borderColor.rgb, 1.0) * borderedAreaAlpha * borderColor.a; 188 | // Then, if the point is also inside of the actual window 189 | // (pointAlpha = 1), draw the correct window pixel on top 190 | cogl_color_out = mix(borderRect, cogl_color_out, pointAlpha); 191 | } 192 | } else { 193 | // If there's no border, just multiply the output color by the calculated 194 | // alpha value. 195 | cogl_color_out *= pointAlpha; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Extension, 3 | InjectionManager, 4 | } from 'resource:///org/gnome/shell/extensions/extension.js'; 5 | import {layoutManager} from 'resource:///org/gnome/shell/ui/main.js'; 6 | import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js'; 7 | import {WorkspaceAnimationController} from 'resource:///org/gnome/shell/ui/workspaceAnimation.js'; 8 | import {disableEffect, enableEffect} from './manager/event_manager.js'; 9 | import {addShadowInOverview} from './patch/add_shadow_in_overview.js'; 10 | import { 11 | addShadowsInWorkspaceSwitch, 12 | removeShadowsAfterWorkspaceSwitch, 13 | } from './patch/workspace_switch.js'; 14 | import { 15 | disableBackgroundMenuItem, 16 | enableBackgroundMenuItem, 17 | } from './utils/background_menu.js'; 18 | import {logDebug} from './utils/log.js'; 19 | import {getPref, initPrefs, prefs, uninitPrefs} from './utils/settings.js'; 20 | import {WindowPicker} from './window_picker/service.js'; 21 | 22 | import type GObject from 'gi://GObject'; 23 | import type Gio from 'gi://Gio'; 24 | 25 | export default class RoundedWindowCornersReborn extends Extension { 26 | // The extension works by overriding (monkey patching) the code of GNOME 27 | // Shell's internal methods. InjectionManager is a convenience class that 28 | // stores references to the original methods and allows to easily restore 29 | // them when the extension is disabled. 30 | #injectionManager: InjectionManager | null = null; 31 | 32 | #windowPicker: WindowPicker | null = null; 33 | 34 | #layoutManagerStartupConnection: number | null = null; 35 | #workspaceSwitchConnections: {object: GObject.Object; id: number}[] | null = 36 | null; 37 | 38 | enable() { 39 | // Initialize extension preferences 40 | initPrefs(this.getSettings()); 41 | 42 | this.#injectionManager = new InjectionManager(); 43 | 44 | // Export the d-bus interface of the window picker in preferences. 45 | // See the readme in the `window_picker` directory for more information. 46 | this.#windowPicker = new WindowPicker(); 47 | this.#windowPicker.export(); 48 | 49 | if (layoutManager._startingUp) { 50 | // Wait for GNOME Shell to be ready before enabling rounded corners 51 | this.#layoutManagerStartupConnection = layoutManager.connect( 52 | 'startup-complete', 53 | () => { 54 | enableEffect(); 55 | 56 | if (getPref('enable-preferences-entry')) { 57 | enableBackgroundMenuItem(); 58 | } 59 | 60 | layoutManager.disconnect( 61 | // Since this happens inside of the connection, there 62 | // is no way for this to be null. 63 | // biome-ignore lint/style/noNonNullAssertion: 64 | this.#layoutManagerStartupConnection!, 65 | ); 66 | }, 67 | ); 68 | } else { 69 | enableEffect(); 70 | 71 | if (getPref('enable-preferences-entry')) { 72 | enableBackgroundMenuItem(); 73 | } 74 | } 75 | 76 | const self = this; 77 | 78 | // WindowPreview is a widget that shows a window in the overview. 79 | // We need to override its `_addWindow` method to add a shadow actor 80 | // to the preview, otherwise overview windows won't have custom 81 | // shadows. 82 | this.#injectionManager.overrideMethod( 83 | WindowPreview.prototype, 84 | '_addWindow', 85 | addWindow => 86 | function (window) { 87 | addWindow.call(this, window); 88 | addShadowInOverview(window, this); 89 | }, 90 | ); 91 | 92 | // The same way we applied a cloned shadow actor to window previews in 93 | // the overview, we also need to apply it to windows during workspace 94 | // switching. 95 | this.#injectionManager.overrideMethod( 96 | WorkspaceAnimationController.prototype, 97 | '_prepareWorkspaceSwitch', 98 | prepareWorkspaceSwitch => 99 | function (workspaceIndices) { 100 | prepareWorkspaceSwitch.call(this, workspaceIndices); 101 | self.#workspaceSwitchConnections = 102 | addShadowsInWorkspaceSwitch(this); 103 | }, 104 | ); 105 | this.#injectionManager.overrideMethod( 106 | WorkspaceAnimationController.prototype, 107 | '_finishWorkspaceSwitch', 108 | finishWorkspaceSwitch => 109 | function (switchData) { 110 | removeShadowsAfterWorkspaceSwitch(this); 111 | finishWorkspaceSwitch.call(this, switchData); 112 | }, 113 | ); 114 | 115 | // Watch for changes of the `enable-preferences-entry` prefs key. 116 | prefs.connect('changed', (_: Gio.Settings, key: string) => { 117 | if (key === 'enable-preferences-entry') { 118 | getPref('enable-preferences-entry') 119 | ? enableBackgroundMenuItem() 120 | : disableBackgroundMenuItem(); 121 | } 122 | }); 123 | 124 | logDebug('Enabled'); 125 | } 126 | 127 | disable() { 128 | // Restore patched methods 129 | this.#injectionManager?.clear(); 130 | this.#injectionManager = null; 131 | 132 | // Remove the item to open preferences page in background menu 133 | disableBackgroundMenuItem(); 134 | 135 | this.#windowPicker?.unexport(); 136 | disableEffect(); 137 | 138 | // Set all props to null 139 | this.#windowPicker = null; 140 | 141 | if (this.#layoutManagerStartupConnection !== null) { 142 | layoutManager.disconnect(this.#layoutManagerStartupConnection); 143 | this.#layoutManagerStartupConnection = null; 144 | } 145 | 146 | for (const connection of this.#workspaceSwitchConnections ?? []) { 147 | connection.object.disconnect(connection.id); 148 | } 149 | 150 | logDebug('Disabled'); 151 | 152 | uninitPrefs(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | import '@girs/gjs'; 2 | import '@girs/gjs/dom'; 3 | import '@girs/gnome-shell/ambient'; 4 | import '@girs/gnome-shell/extensions/global'; 5 | import '@girs/cogl-2.0'; 6 | -------------------------------------------------------------------------------- /src/manager/README.md: -------------------------------------------------------------------------------- 1 | # `manager` 2 | 3 | The rounded corners effect has to perform some actions when differen events 4 | happen. For example, when a new window is opened, the effect has to detect 5 | it and add rounded corners to it. 6 | 7 | This directory contains the code that handles these events. 8 | 9 | ## `event_manager.ts` 10 | 11 | Manages connections between gnome shell events and the rounded corners 12 | effect. It attaches the necessary signals to matching handlers on each effect. 13 | 14 | ## `event_handlers.ts` 15 | 16 | Contains the implementation of handlers for all of the events. 17 | 18 | ## `utils.ts` 19 | 20 | Provides various utility functions used withing signal handling code. 21 | -------------------------------------------------------------------------------- /src/manager/event_handlers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Contains the implementation of handlers for various events that need 3 | * to be processed by the extension. Those handlers are bound to event signals 4 | * in effect_manager.ts. 5 | */ 6 | 7 | import Clutter from 'gi://Clutter'; 8 | import GLib from 'gi://GLib'; 9 | import GObject from 'gi://GObject'; 10 | import St from 'gi://St'; 11 | 12 | import {ClipShadowEffect} from '../effect/clip_shadow_effect.js'; 13 | import {RoundedCornersEffect} from '../effect/rounded_corners_effect.js'; 14 | import { 15 | CLIP_SHADOW_EFFECT, 16 | ROUNDED_CORNERS_EFFECT, 17 | } from '../utils/constants.js'; 18 | import {logDebug} from '../utils/log.js'; 19 | import {getPref} from '../utils/settings.js'; 20 | import { 21 | computeBounds, 22 | computeShadowActorOffset, 23 | computeWindowContentsOffset, 24 | getRoundedCornersCfg, 25 | getRoundedCornersEffect, 26 | shouldEnableEffect, 27 | unwrapActor, 28 | updateShadowActorStyle, 29 | windowScaleFactor, 30 | } from './utils.js'; 31 | 32 | import type Meta from 'gi://Meta'; 33 | import type {RoundedWindowActor} from '../utils/types.js'; 34 | 35 | export function onAddEffect(actor: RoundedWindowActor) { 36 | logDebug(`Adding effect to ${actor?.metaWindow.title}`); 37 | 38 | const win = actor.metaWindow; 39 | 40 | if (!shouldEnableEffect(win)) { 41 | logDebug(`Skipping ${win.title}`); 42 | return; 43 | } 44 | 45 | unwrapActor(actor)?.add_effect_with_name( 46 | ROUNDED_CORNERS_EFFECT, 47 | new RoundedCornersEffect(), 48 | ); 49 | 50 | const shadow = createShadow(actor); 51 | 52 | // Bind properties of the window to the shadow actor. 53 | for (const prop of [ 54 | 'pivot-point', 55 | 'translation-x', 56 | 'translation-y', 57 | 'scale-x', 58 | 'scale-y', 59 | 'visible', 60 | ]) { 61 | actor.bind_property( 62 | prop, 63 | shadow, 64 | prop, 65 | GObject.BindingFlags.SYNC_CREATE, 66 | ); 67 | } 68 | 69 | // Store shadow, app type, visible binding, so that we can access them later 70 | actor.rwcCustomData = { 71 | shadow, 72 | unminimizedTimeoutId: 0, 73 | }; 74 | 75 | // Make sure the effect is applied correctly. 76 | refreshRoundedCorners(actor); 77 | } 78 | 79 | export function onRemoveEffect(actor: RoundedWindowActor): void { 80 | const name = ROUNDED_CORNERS_EFFECT; 81 | unwrapActor(actor)?.remove_effect_by_name(name); 82 | 83 | // Remove shadow actor 84 | const shadow = actor.rwcCustomData?.shadow; 85 | if (shadow) { 86 | global.windowGroup.remove_child(shadow); 87 | shadow.clear_effects(); 88 | shadow.destroy(); 89 | } 90 | 91 | // Remove all timeout handler 92 | const timeoutId = actor.rwcCustomData?.unminimizedTimeoutId; 93 | if (timeoutId) { 94 | GLib.source_remove(timeoutId); 95 | } 96 | delete actor.rwcCustomData; 97 | } 98 | 99 | export function onMinimize(actor: RoundedWindowActor): void { 100 | // Compatibility with "Compiz alike magic lamp effect". 101 | // When minimizing a window, disable the shadow to make the magic lamp effect 102 | // work. 103 | const magicLampEffect = actor.get_effect('minimize-magic-lamp-effect'); 104 | const shadow = actor.rwcCustomData?.shadow; 105 | const roundedCornersEffect = getRoundedCornersEffect(actor); 106 | if (magicLampEffect && shadow && roundedCornersEffect) { 107 | logDebug('Minimizing with magic lamp effect'); 108 | shadow.visible = false; 109 | roundedCornersEffect.enabled = false; 110 | } 111 | } 112 | 113 | export function onUnminimize(actor: RoundedWindowActor): void { 114 | // Compatibility with "Compiz alike magic lamp effect". 115 | // When unminimizing a window, wait until the effect is completed before 116 | // showing the shadow. 117 | const magicLampEffect = actor.get_effect('unminimize-magic-lamp-effect'); 118 | const shadow = actor.rwcCustomData?.shadow; 119 | const roundedCornersEffect = getRoundedCornersEffect(actor); 120 | if (magicLampEffect && shadow && roundedCornersEffect) { 121 | shadow.visible = false; 122 | type Effect = Clutter.Effect & {timerId: Clutter.Timeline}; 123 | const timer = (magicLampEffect as Effect).timerId; 124 | 125 | const id = timer.connect('new-frame', source => { 126 | // Wait until the effect is 98% completed 127 | if (source.get_progress() > 0.98) { 128 | logDebug('Unminimizing with magic lamp effect'); 129 | shadow.visible = true; 130 | roundedCornersEffect.enabled = true; 131 | source.disconnect(id); 132 | } 133 | }); 134 | 135 | return; 136 | } 137 | } 138 | 139 | export function onRestacked(): void { 140 | for (const actor of global.get_window_actors()) { 141 | const shadow = (actor as RoundedWindowActor).rwcCustomData?.shadow; 142 | 143 | if (!(actor.visible && shadow)) { 144 | continue; 145 | } 146 | 147 | global.windowGroup.set_child_below_sibling(shadow, actor); 148 | } 149 | } 150 | 151 | export const onSizeChanged = refreshRoundedCorners; 152 | 153 | export const onFocusChanged = refreshShadow; 154 | 155 | export const onSettingsChanged = refreshAllRoundedCorners; 156 | 157 | /** 158 | * Create the shadow actor for a window. 159 | * 160 | * @param actor - The window actor to create the shadow actor for. 161 | */ 162 | function createShadow(actor: Meta.WindowActor): St.Bin { 163 | const shadow = new St.Bin({ 164 | name: 'Shadow Actor', 165 | child: new St.Bin({ 166 | xExpand: true, 167 | yExpand: true, 168 | }), 169 | }); 170 | (shadow.firstChild as St.Bin).add_style_class_name('shadow'); 171 | 172 | refreshShadow(actor); 173 | 174 | // We have to clip the shadow because of this issue: 175 | // https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4474 176 | shadow.add_effect_with_name(CLIP_SHADOW_EFFECT, new ClipShadowEffect()); 177 | 178 | // Draw the shadow actor below the window actor. 179 | global.windowGroup.insert_child_below(shadow, actor); 180 | 181 | // Bind position and size between window and shadow 182 | for (let i = 0; i < 4; i++) { 183 | const constraint = new Clutter.BindConstraint({ 184 | source: actor, 185 | coordinate: i, 186 | offset: 0, 187 | }); 188 | shadow.add_constraint(constraint); 189 | } 190 | 191 | return shadow; 192 | } 193 | 194 | /** 195 | * Refresh the shadow actor for a window. 196 | * 197 | * @param actor - The window actor to refresh the shadow for. 198 | */ 199 | function refreshShadow(actor: RoundedWindowActor) { 200 | const win = actor.metaWindow; 201 | const shadow = actor.rwcCustomData?.shadow; 202 | if (!shadow) { 203 | return; 204 | } 205 | 206 | const shadowSettings = win.appears_focused 207 | ? getPref('focused-shadow') 208 | : getPref('unfocused-shadow'); 209 | 210 | const {borderRadius, padding} = getRoundedCornersCfg(win); 211 | 212 | updateShadowActorStyle(win, shadow, borderRadius, shadowSettings, padding); 213 | } 214 | 215 | /** 216 | * Refresh rounded corners state and settings for a window. 217 | * 218 | * @param actor - The window actor to refresh the rounded corners settings for. 219 | */ 220 | function refreshRoundedCorners(actor: RoundedWindowActor): void { 221 | const win = actor.metaWindow; 222 | 223 | const windowInfo = actor.rwcCustomData; 224 | const effect = getRoundedCornersEffect(actor); 225 | 226 | const hasEffect = effect && windowInfo; 227 | const shouldHaveEffect = shouldEnableEffect(win); 228 | 229 | if (!hasEffect) { 230 | // onAddEffect already skips windows that shouldn't have rounded corners. 231 | onAddEffect(actor); 232 | return; 233 | } 234 | 235 | if (!shouldHaveEffect) { 236 | onRemoveEffect(actor); 237 | return; 238 | } 239 | 240 | if (!effect.enabled) { 241 | effect.enabled = true; 242 | } 243 | 244 | // When window size is changed, update uniforms for corner rounding shader. 245 | const cfg = getRoundedCornersCfg(win); 246 | const windowContentOffset = computeWindowContentsOffset(win); 247 | effect.updateUniforms( 248 | windowScaleFactor(win), 249 | cfg, 250 | computeBounds(actor, windowContentOffset), 251 | ); 252 | 253 | // Update BindConstraint for the shadow 254 | const shadow = windowInfo.shadow; 255 | const offsets = computeShadowActorOffset(actor, windowContentOffset); 256 | const constraints = shadow.get_constraints(); 257 | constraints.forEach((constraint, i) => { 258 | if (constraint instanceof Clutter.BindConstraint) { 259 | constraint.offset = offsets[i]; 260 | } 261 | }); 262 | 263 | refreshShadow(actor); 264 | } 265 | 266 | /** Refresh rounded corners settings for all windows. */ 267 | function refreshAllRoundedCorners() { 268 | for (const actor of global.get_window_actors()) { 269 | refreshRoundedCorners(actor); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/manager/event_manager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Manages connections between gnome shell events and the rounded corners 3 | * effect. See {@link enableEffect} for more information. 4 | */ 5 | 6 | import {logDebug} from '../utils/log.js'; 7 | import {prefs} from '../utils/settings.js'; 8 | import * as handlers from './event_handlers.js'; 9 | 10 | import type GObject from 'gi://GObject'; 11 | import type Meta from 'gi://Meta'; 12 | import type Shell from 'gi://Shell'; 13 | import type {RoundedWindowActor} from '../utils/types.js'; 14 | 15 | /** 16 | * The rounded corners effect has to perform some actions when differen events 17 | * happen. For example, when a new window is opened, the effect has to detect 18 | * it and add rounded corners to it. 19 | * 20 | * The `enableEffect` method handles this by attaching the necessary signals 21 | * to matching handlers on each effect. 22 | */ 23 | export function enableEffect() { 24 | // Update the effect when settings are changed. 25 | connect(prefs, 'changed', handlers.onSettingsChanged); 26 | 27 | const wm = global.windowManager; 28 | 29 | // Add the effect to all windows when the extension is enabled. 30 | const windowActors = global.get_window_actors(); 31 | logDebug(`Initial window count: ${windowActors.length}`); 32 | for (const actor of windowActors) { 33 | applyEffectTo(actor); 34 | } 35 | 36 | // Add the effect to new windows when they are opened. 37 | connect( 38 | global.display, 39 | 'window-created', 40 | (_: Meta.Display, win: Meta.Window) => { 41 | const actor: Meta.WindowActor = win.get_compositor_private(); 42 | 43 | // If wm_class_instance of Meta.Window is null, wait for it to be 44 | // set before applying the effect. 45 | if (win?.get_wm_class_instance() == null) { 46 | const notifyId = win.connect('notify::wm-class', () => { 47 | applyEffectTo(actor); 48 | win.disconnect(notifyId); 49 | }); 50 | } else { 51 | applyEffectTo(actor); 52 | } 53 | }, 54 | ); 55 | 56 | // Window minimized. 57 | connect(wm, 'minimize', (_: Shell.WM, actor: Meta.WindowActor) => 58 | handlers.onMinimize(actor), 59 | ); 60 | 61 | // Window unminimized. 62 | connect(wm, 'unminimize', (_: Shell.WM, actor: Meta.WindowActor) => 63 | handlers.onUnminimize(actor), 64 | ); 65 | 66 | // When closing the window, remove the effect from it. 67 | connect(wm, 'destroy', (_: Shell.WM, actor: Meta.WindowActor) => 68 | removeEffectFrom(actor), 69 | ); 70 | 71 | // When windows are restacked, the order of shadow actors as well. 72 | connect(global.display, 'restacked', handlers.onRestacked); 73 | } 74 | 75 | /** Disable the effect for all windows. */ 76 | export function disableEffect() { 77 | for (const actor of global.get_window_actors()) { 78 | removeEffectFrom(actor); 79 | } 80 | 81 | disconnectAll(); 82 | } 83 | 84 | const connections: {object: GObject.Object; id: number}[] = []; 85 | 86 | /** 87 | * Connect a callback to an object signal and add it to the list of all 88 | * connections. This allows to easily disconnect all signals when removing 89 | * the effect. 90 | * 91 | * @param object - The object to connect the callback to. 92 | * @param signal - The name of the signal. 93 | * @param callback - The function to connect to the signal. 94 | */ 95 | function connect( 96 | object: GObject.Object, 97 | signal: string, 98 | // Signal callbacks can have any return args and return types. 99 | // biome-ignore lint/suspicious/noExplicitAny: 100 | callback: (...args: any[]) => any, 101 | ) { 102 | connections.push({ 103 | object: object, 104 | id: object.connect(signal, callback), 105 | }); 106 | } 107 | 108 | /** 109 | * Disconnect all connected signals from all actors or a specific object. 110 | * 111 | * @param object - If object is provided, only disconnect signals from it. 112 | */ 113 | function disconnectAll(object?: GObject.Object) { 114 | for (const connection of connections) { 115 | if (object === undefined || connection.object === object) { 116 | connection.object.disconnect(connection.id); 117 | } 118 | } 119 | } 120 | 121 | /** 122 | * Apply the effect to a window. 123 | * 124 | * While {@link enableEffect} handles global events such as window creation, 125 | * this function handles events that happen to a specific window, like changing 126 | * its size or workspace. 127 | * 128 | * @param actor - The window actor to apply the effect to. 129 | */ 130 | function applyEffectTo(actor: RoundedWindowActor) { 131 | // In wayland sessions, the surface actor of XWayland clients is sometimes 132 | // not ready when the window is created. In this case, we wait until it is 133 | // ready before applying the effect. 134 | if (!actor.firstChild) { 135 | const id = actor.connect('notify::first-child', () => { 136 | applyEffectTo(actor); 137 | actor.disconnect(id); 138 | }); 139 | 140 | return; 141 | } 142 | 143 | const texture = actor.get_texture(); 144 | if (!texture) { 145 | return; 146 | } 147 | 148 | // Window resized. 149 | // 150 | // The signal has to be connected both to the actor and the texture. Why is 151 | // that? I have no idea. But without that, weird bugs can happen. For 152 | // example, when using Dash to Dock, all opened windows will be invisible 153 | // *unless they are pinned in the dock*. So yeah, GNOME is magic. 154 | connect(actor, 'notify::size', () => handlers.onSizeChanged(actor)); 155 | connect(texture, 'size-changed', () => { 156 | handlers.onSizeChanged(actor); 157 | }); 158 | 159 | // Window focus changed. 160 | connect(actor.metaWindow, 'notify::appears-focused', () => 161 | handlers.onFocusChanged(actor), 162 | ); 163 | 164 | // Workspace or monitor of the window changed. 165 | connect(actor.metaWindow, 'workspace-changed', () => { 166 | handlers.onFocusChanged(actor); 167 | }); 168 | 169 | handlers.onAddEffect(actor); 170 | } 171 | 172 | /** 173 | * Remove the effect from a window. 174 | * 175 | * @param actor - The window actor to remove the effect from. 176 | */ 177 | function removeEffectFrom(actor: RoundedWindowActor) { 178 | disconnectAll(actor); 179 | disconnectAll(actor.metaWindow); 180 | 181 | handlers.onRemoveEffect(actor); 182 | } 183 | -------------------------------------------------------------------------------- /src/patch/README.md: -------------------------------------------------------------------------------- 1 | # `patch` 2 | 3 | This directory contains functions which are used to monkey patch GNOME Shell 4 | methods. See `extension.ts` for more information. 5 | 6 | ## `add_shadow_in_overview.ts` 7 | 8 | Contains a function that adds a shadow actor to a window preview in 9 | the overview. Used to patch the `WindowPreview._addWindow` method. 10 | 11 | ## `workspace_switch.ts` 12 | 13 | Contains functions for handling shadows during workspace switching. Used to 14 | patch the `WorkspaceAnimationController._prepareWorkspaceSwitch` and 15 | `WorkspaceAnimationController._finishWorkspaceSwitch` methods. 16 | -------------------------------------------------------------------------------- /src/patch/add_shadow_in_overview.ts: -------------------------------------------------------------------------------- 1 | /** @file Provides a function to add a shadow actor to a window preview in the overview. */ 2 | 3 | import Clutter from 'gi://Clutter'; 4 | import GObject from 'gi://GObject'; 5 | import Graphene from 'gi://Graphene'; 6 | 7 | import {overview} from 'resource:///org/gnome/shell/ui/main.js'; 8 | import {LinearFilterEffect} from '../effect/linear_filter_effect.js'; 9 | import {shouldEnableEffect, windowScaleFactor} from '../manager/utils.js'; 10 | import {OVERVIEW_SHADOW_ACTOR, SHADOW_PADDING} from '../utils/constants.js'; 11 | import {logDebug} from '../utils/log.js'; 12 | 13 | import type Meta from 'gi://Meta'; 14 | import type {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js'; 15 | import type {RoundedWindowActor} from '../utils/types.js'; 16 | 17 | /** 18 | * Add a shadow actor to a window preview in the overview. 19 | * @param window - The window that the preview is of. 20 | * @param self - The window preview that the shadow actor is added to. 21 | */ 22 | export function addShadowInOverview(window: Meta.Window, self: WindowPreview) { 23 | // Create a new error object and use it to get the call stack of 24 | // the function. 25 | // 26 | // Since the error is not actually being raised, it doesn't need 27 | // an error message. 28 | // biome-ignore lint/suspicious/useErrorMessage: 29 | const stack = new Error().stack?.trim(); 30 | if ( 31 | stack === undefined || 32 | stack.indexOf('_updateAttachedDialogs') !== -1 || 33 | stack.indexOf('addDialog') !== -1 34 | ) { 35 | // If the window is an attached dialog, skip it. 36 | return; 37 | } 38 | 39 | // If the original window doesn't have rounded corners or a shadow, 40 | // we don't need to do anything, so we can skip it. 41 | const hasRoundedCorners = shouldEnableEffect(window); 42 | const windowActor = window.get_compositor_private() as RoundedWindowActor; 43 | const shadow = windowActor.rwcCustomData?.shadow; 44 | if (!(hasRoundedCorners && shadow)) { 45 | return; 46 | } 47 | 48 | logDebug(`Adding shadow for ${window.title} in overview`); 49 | 50 | // windowContainer has the actual contents of the window preview 51 | const windowContainer = self.windowContainer; 52 | let firstChild: Clutter.Actor | null = windowContainer.firstChild; 53 | 54 | // Apply liear interpolation to the window preview to make it look 55 | // better (there's an upstream GNOME bug causing windows to be blurry, 56 | // this makes the effect less noticeable) 57 | firstChild?.add_effect(new LinearFilterEffect()); 58 | 59 | // Create a clone of the window's shadow actor and add it to the preview 60 | const shadowActorClone = new OverviewShadowActorClone(shadow, self); 61 | windowContainer.bind_property('scale-x', shadowActorClone, 'scale-x', 1); 62 | windowContainer.bind_property('scale-y', shadowActorClone, 'scale-y', 1); 63 | self.insert_child_below(shadowActorClone, windowContainer); 64 | 65 | // Disconnect all signals when the window preview is destroyed 66 | const connection = self.connect('destroy', () => { 67 | shadowActorClone.destroy(); 68 | firstChild?.clear_effects(); 69 | firstChild = null; 70 | 71 | self.disconnect(connection); 72 | }); 73 | } 74 | 75 | /** 76 | * A clone of a window's shadow actor that is shown in the overview. Binds the 77 | * size of the shadow to the size of the window preview in the overview. 78 | */ 79 | const OverviewShadowActorClone = GObject.registerClass( 80 | {}, 81 | class extends Clutter.Clone { 82 | windowPreview: WindowPreview; 83 | 84 | /** 85 | * Create the clone of the shadow actor. 86 | * @param source the shadow actor to clone. 87 | * @param windowPreview the window preview that the clone is applied to. 88 | */ 89 | constructor(source: Clutter.Actor, windowPreview: WindowPreview) { 90 | super({ 91 | source, // the source shadow actor shown in desktop 92 | name: OVERVIEW_SHADOW_ACTOR, 93 | pivotPoint: new Graphene.Point({x: 0.5, y: 0.5}), 94 | }); 95 | 96 | this.windowPreview = windowPreview; 97 | } 98 | 99 | /** 100 | * Recompute the position and size of shadow in overview 101 | * This virtual function will be called when we: 102 | * - entering/closing overview 103 | * - dragging window 104 | * - position and size of window preview in overview changed 105 | * @param box The bound box of shadow actor 106 | */ 107 | vfunc_allocate(box: Clutter.ActorBox): void { 108 | // The layout box of the window has to be obtained in a different 109 | // way when leaving the overview (eg. by pressing ESC). I have no 110 | // idea why this is the case, but oh well. GNOME. 111 | const leavingOverview = 112 | overview._overview.controls._workspacesDisplay._leavingOverview; 113 | const windowContainerBox = leavingOverview 114 | ? this.windowPreview.windowContainer.get_allocation_box() 115 | : this.windowPreview.get_allocation_box(); 116 | 117 | const metaWindow = 118 | this.windowPreview._windowActor.get_meta_window(); 119 | if (!metaWindow) { 120 | return; 121 | } 122 | 123 | // Scale the shadow by the same scale factor that the window preview 124 | // is scaled by. 125 | const containerScaleFactor = 126 | windowContainerBox.get_width() / 127 | metaWindow.get_frame_rect().width; 128 | const paddings = 129 | SHADOW_PADDING * 130 | containerScaleFactor * 131 | windowScaleFactor(metaWindow); 132 | 133 | // Setup the bounding box of the shadow actor. 134 | box.set_origin(-paddings, -paddings); 135 | box.set_size( 136 | windowContainerBox.get_width() + 2 * paddings, 137 | windowContainerBox.get_height() + 2 * paddings, 138 | ); 139 | 140 | // Apply the bounding box. 141 | super.vfunc_allocate(box); 142 | } 143 | }, 144 | ); 145 | -------------------------------------------------------------------------------- /src/patch/workspace_switch.ts: -------------------------------------------------------------------------------- 1 | /** @file Provides functions for handling shadows during workspace switching. */ 2 | 3 | import Clutter from 'gi://Clutter'; 4 | 5 | import {getRoundedCornersEffect, windowScaleFactor} from '../manager/utils.js'; 6 | import {SHADOW_PADDING} from '../utils/constants.js'; 7 | 8 | import type GObject from 'gi://GObject'; 9 | import type {WorkspaceAnimationController} from 'resource:///org/gnome/shell/ui/workspaceAnimation.js'; 10 | import type {RoundedWindowActor} from '../utils/types.js'; 11 | 12 | type WsAnimationActor = Clutter.Actor & {shadowClone?: Clutter.Actor}; 13 | 14 | /** 15 | * Add shadows to windows when switching workspaces. 16 | * @param self - The workspace animation controller. 17 | * @returns A set of connections to be disconnected on extension disable. 18 | */ 19 | export function addShadowsInWorkspaceSwitch( 20 | self: WorkspaceAnimationController, 21 | ) { 22 | const connections: {object: GObject.Object; id: number}[] = []; 23 | 24 | for (const monitor of self._switchData.monitors) { 25 | for (const workspace of monitor._workspaceGroups) { 26 | const windowRecords = workspace._windowRecords; 27 | 28 | // Switching workspaces messes with the window stacking order, 29 | // so the shadows need to be restacked in order to always be 30 | // behind the window. 31 | const restackedConnection = global.display.connect( 32 | 'restacked', 33 | () => { 34 | for (const {clone} of windowRecords) { 35 | const shadow = (clone as WsAnimationActor).shadowClone; 36 | if (shadow) { 37 | workspace.set_child_below_sibling(shadow, clone); 38 | } 39 | } 40 | }, 41 | ); 42 | const destroyConnection = workspace.connect('destroy', () => { 43 | global.display.disconnect(restackedConnection); 44 | workspace.disconnect(destroyConnection); 45 | }); 46 | 47 | connections.push({object: global.display, id: restackedConnection}); 48 | connections.push({object: workspace, id: destroyConnection}); 49 | 50 | for (const {windowActor: actor, clone} of windowRecords) { 51 | const win = actor.metaWindow; 52 | 53 | // Skip windows that don't have a shadow or an enabled RWC effect. 54 | const shadow = (actor as RoundedWindowActor).rwcCustomData 55 | ?.shadow; 56 | const enabled = getRoundedCornersEffect(actor)?.enabled; 57 | if (!(shadow && enabled)) { 58 | continue; 59 | } 60 | 61 | // Clone the shadow actor and set its dimensions to match the 62 | // window actor size. 63 | const shadowClone = new Clutter.Clone({ 64 | source: shadow, 65 | }); 66 | const paddings = SHADOW_PADDING * windowScaleFactor(win); 67 | 68 | const frameRect = win.get_frame_rect(); 69 | 70 | shadowClone.width = frameRect.width + paddings * 2; 71 | shadowClone.height = frameRect.height + paddings * 2; 72 | shadowClone.x = clone.x + frameRect.x - actor.x - paddings; 73 | shadowClone.y = clone.y + frameRect.y - actor.y - paddings; 74 | 75 | // Compatibility with Desktop Cube 76 | const notifyId = clone.connect('notify::translation-z', () => { 77 | shadowClone.translationZ = clone.translationZ - 0.05; 78 | }); 79 | const destroyConnection = clone.connect('destroy', () => { 80 | clone.disconnect(notifyId); 81 | clone.disconnect(destroyConnection); 82 | }); 83 | 84 | connections.push({object: clone, id: notifyId}); 85 | connections.push({object: clone, id: destroyConnection}); 86 | 87 | // Store the reference to the shadow clone. This allows restacking 88 | // them, as you can see at the top of this function. 89 | (clone as WsAnimationActor).shadowClone = shadowClone; 90 | clone.bind_property('visible', shadowClone, 'visible', 0); 91 | 92 | workspace.insert_child_below(shadowClone, clone); 93 | } 94 | } 95 | } 96 | 97 | return connections; 98 | } 99 | 100 | /** 101 | * Delete shadow clones after switching workspaces. 102 | * @param self - The workspace animation controller. 103 | */ 104 | export function removeShadowsAfterWorkspaceSwitch( 105 | self: WorkspaceAnimationController, 106 | ) { 107 | for (const monitor of self._switchData.monitors) { 108 | for (const workspace of monitor._workspaceGroups) { 109 | for (const {clone} of workspace._windowRecords) { 110 | (clone as WsAnimationActor).shadowClone?.destroy(); 111 | delete (clone as WsAnimationActor).shadowClone; 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/preferences/README.md: -------------------------------------------------------------------------------- 1 | # `preferences` 2 | 3 | This directory contains the code for the preferences page of the extension. 4 | 5 | Note that the actual implementation of the preferences page is in the 6 | `prefs.ts` file, which uses files from this directory. 7 | 8 | ## `index.ts` 9 | 10 | Contains a `prefsTabs` variable that lists all of the tabs to be initialized. 11 | 12 | ## `pages` 13 | 14 | This directory contains the code for each of the tabs, as well as the subpages 15 | for editing the shadow and resetting the settings. 16 | 17 | ## `widgets` 18 | 19 | This directory contains the code for the widgets used on the preferences page. 20 | -------------------------------------------------------------------------------- /src/preferences/index.ts: -------------------------------------------------------------------------------- 1 | /** @file Contains the list of top-level pages (tabs) in the preferences window. */ 2 | 3 | import type Adw from 'gi://Adw'; 4 | 5 | import {BlacklistPage} from '../preferences/pages/blacklist.js'; 6 | import {CustomPage} from '../preferences/pages/custom.js'; 7 | import {GeneralPage} from '../preferences/pages/general.js'; 8 | 9 | export const prefsTabs: (typeof Adw.PreferencesPage)[] = [ 10 | GeneralPage, 11 | BlacklistPage, 12 | CustomPage, 13 | ]; 14 | -------------------------------------------------------------------------------- /src/preferences/pages/blacklist.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Contains the implementation of the blacklist page. 3 | * Handles creating blacklist entries and binding them to settings. 4 | */ 5 | 6 | import Adw from 'gi://Adw'; 7 | import GLib from 'gi://GLib'; 8 | import GObject from 'gi://GObject'; 9 | 10 | import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; 11 | import {getPref, setPref} from '../../utils/settings.js'; 12 | import {AppRow, type AppRowClass} from '../widgets/app_row.js'; 13 | 14 | import type Gtk from 'gi://Gtk'; 15 | 16 | export const BlacklistPage = GObject.registerClass( 17 | { 18 | Template: GLib.uri_resolve_relative( 19 | import.meta.url, 20 | 'blacklist.ui', 21 | GLib.UriFlags.NONE, 22 | ), 23 | GTypeName: 'PrefsBlacklist', 24 | InternalChildren: ['blacklistGroup'], 25 | }, 26 | class extends Adw.PreferencesPage { 27 | private declare _blacklistGroup: Adw.PreferencesGroup; 28 | 29 | #blacklist = getPref('blacklist'); 30 | 31 | constructor() { 32 | super(); 33 | 34 | for (const title of this.#blacklist) { 35 | this.addWindow(undefined, title); 36 | } 37 | } 38 | 39 | /** 40 | * Add a new blacklist entry. 41 | * @param wmClass - The WM_CLASS of the window. 42 | */ 43 | addWindow(_?: Gtk.Button, wmClass?: string) { 44 | const row = new AppRow({ 45 | onDelete: row => this.#deleteWindow(row), 46 | onWindowChange: (_, oldWmClass, newWmClass) => 47 | this.#changeWindow(oldWmClass, newWmClass), 48 | }); 49 | row.set_subtitle(wmClass ?? ''); 50 | this._blacklistGroup.add(row); 51 | } 52 | 53 | /** 54 | * Delete a blacklist entry. 55 | * @param row - The row to delete. 56 | */ 57 | #deleteWindow(row: AppRowClass) { 58 | this.#blacklist.splice(this.#blacklist.indexOf(row.title), 1); 59 | setPref('blacklist', this.#blacklist); 60 | this._blacklistGroup.remove(row); 61 | } 62 | 63 | /** 64 | * Change the blacklist entry to a different window. 65 | * @param oldWmClass - Current WM_CLASS of the entry. 66 | * @param newWmClass - New WM_CLASS of the entry. 67 | * @returns Whether the entry was changed successfully. 68 | */ 69 | #changeWindow(oldWmClass: string, newWmClass: string): boolean { 70 | if (this.#blacklist.includes(newWmClass)) { 71 | // If the new window is already in the blacklist, show an error. 72 | const win = this.root as unknown as Adw.PreferencesDialog; 73 | win.add_toast( 74 | new Adw.Toast({ 75 | title: _( 76 | `Can't add ${newWmClass} to the list, because it already there`, 77 | ), 78 | }), 79 | ); 80 | return false; 81 | } 82 | 83 | if (oldWmClass === '') { 84 | // If the old WM_CLASS is empty, the entry was just created, 85 | // so we need to just add the new window to the blacklist. 86 | this.#blacklist.push(newWmClass); 87 | } else { 88 | // Otherwise, replace the old window with the new one. 89 | const oldId = this.#blacklist.indexOf(oldWmClass); 90 | this.#blacklist.splice(oldId, 1, newWmClass); 91 | } 92 | 93 | setPref('blacklist', this.#blacklist); 94 | 95 | return true; 96 | } 97 | }, 98 | ); 99 | -------------------------------------------------------------------------------- /src/preferences/pages/blacklist.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 29 | 30 | -------------------------------------------------------------------------------- /src/preferences/pages/custom.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 29 | 30 | -------------------------------------------------------------------------------- /src/preferences/pages/edit_shadow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Contains the code for the shadow settings page. 3 | * It implements the dynamic preview widget that uses GTK CSS Provider to 4 | * show what the shadow will look like. 5 | */ 6 | 7 | import Adw from 'gi://Adw'; 8 | import GLib from 'gi://GLib'; 9 | import GObject from 'gi://GObject'; 10 | import Gio from 'gi://Gio'; 11 | import Gtk from 'gi://Gtk'; 12 | 13 | import {boxShadowCss} from '../../utils/box_shadow.js'; 14 | import {getPref, setPref} from '../../utils/settings.js'; 15 | 16 | import type {BoxShadow} from '../../utils/types.js'; 17 | 18 | export const EditShadowPage = GObject.registerClass( 19 | { 20 | Template: GLib.uri_resolve_relative( 21 | import.meta.url, 22 | 'edit-shadow.ui', 23 | GLib.UriFlags.NONE, 24 | ), 25 | GTypeName: 'EditShadowPage', 26 | InternalChildren: [ 27 | 'focusedShadowPreview', 28 | 'unfocusedShadowPreview', 29 | 'previewRow', 30 | 31 | 'focusedHorizontalOffset', 32 | 'focusedVerticalOffset', 33 | 'focusedBlurRadius', 34 | 'focusedSpreadRadius', 35 | 'focusedOpacity', 36 | 37 | 'unfocusedHorizontalOffset', 38 | 'unfocusedVerticalOffset', 39 | 'unfocusedBlurRadius', 40 | 'unfocusedSpreadRadius', 41 | 'unfocusedOpacity', 42 | ], 43 | }, 44 | class extends Adw.NavigationPage { 45 | private declare _focusedShadowPreview: Gtk.Widget; 46 | private declare _unfocusedShadowPreview: Gtk.Widget; 47 | private declare _previewRow: Gtk.Widget; 48 | 49 | private declare _focusedHorizontalOffset: Gtk.Adjustment; 50 | private declare _focusedVerticalOffset: Gtk.Adjustment; 51 | private declare _focusedBlurRadius: Gtk.Adjustment; 52 | private declare _focusedSpreadRadius: Gtk.Adjustment; 53 | private declare _focusedOpacity: Gtk.Adjustment; 54 | 55 | private declare _unfocusedHorizontalOffset: Gtk.Adjustment; 56 | private declare _unfocusedVerticalOffset: Gtk.Adjustment; 57 | private declare _unfocusedBlurRadius: Gtk.Adjustment; 58 | private declare _unfocusedSpreadRadius: Gtk.Adjustment; 59 | private declare _unfocusedOpacity: Gtk.Adjustment; 60 | 61 | // CSS Providers allow to dynamically apply a CSS style string to 62 | // the preview widgets. 63 | #unfocusCssProvider = new Gtk.CssProvider(); 64 | #focusCssProvider = new Gtk.CssProvider(); 65 | #backgroundCssProvider = new Gtk.CssProvider(); 66 | 67 | #focusedShadowSettings = getPref('focused-shadow'); 68 | #unfocusedShadowSettings = getPref('unfocused-shadow'); 69 | 70 | #isInitialized = false; 71 | 72 | constructor() { 73 | super(); 74 | 75 | // Update the desktop wallpaper in the preview when switching 76 | // between light and dark mode, since the wallpaper can change 77 | // when that happens. 78 | const styleManager = new Adw.StyleManager(); 79 | styleManager.connect('notify::dark', manager => { 80 | this.#refreshWallpaper(manager); 81 | }); 82 | 83 | // Initialize the styles of preview widgets. 84 | this._focusedShadowPreview 85 | .get_style_context() 86 | .add_provider( 87 | this.#focusCssProvider, 88 | Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, 89 | ); 90 | this._unfocusedShadowPreview 91 | .get_style_context() 92 | .add_provider( 93 | this.#unfocusCssProvider, 94 | Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, 95 | ); 96 | this._previewRow 97 | .get_style_context() 98 | .add_provider( 99 | this.#backgroundCssProvider, 100 | Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, 101 | ); 102 | 103 | this.#refreshWallpaper(styleManager); 104 | this.#syncWidgetState(); 105 | this.#updatePreviewStyle(); 106 | this.#isInitialized = true; 107 | } 108 | 109 | onValueChanged() { 110 | if (!this.#isInitialized) { 111 | return; 112 | } 113 | this.#setPrefs(); 114 | this.#updatePreviewStyle(); 115 | } 116 | 117 | /** Synchronize the widget state with actual setting values. */ 118 | #syncWidgetState() { 119 | this._focusedHorizontalOffset.set_value( 120 | this.#focusedShadowSettings.horizontalOffset, 121 | ); 122 | this._focusedVerticalOffset.set_value( 123 | this.#focusedShadowSettings.verticalOffset, 124 | ); 125 | this._focusedBlurRadius.set_value( 126 | this.#focusedShadowSettings.blurOffset, 127 | ); 128 | this._focusedSpreadRadius.set_value( 129 | this.#focusedShadowSettings.spreadRadius, 130 | ); 131 | this._focusedOpacity.set_value(this.#focusedShadowSettings.opacity); 132 | 133 | this._unfocusedHorizontalOffset.set_value( 134 | this.#unfocusedShadowSettings.horizontalOffset, 135 | ); 136 | this._unfocusedVerticalOffset.set_value( 137 | this.#unfocusedShadowSettings.verticalOffset, 138 | ); 139 | this._unfocusedBlurRadius.set_value( 140 | this.#unfocusedShadowSettings.blurOffset, 141 | ); 142 | this._unfocusedSpreadRadius.set_value( 143 | this.#unfocusedShadowSettings.spreadRadius, 144 | ); 145 | this._unfocusedOpacity.set_value( 146 | this.#unfocusedShadowSettings.opacity, 147 | ); 148 | } 149 | 150 | /** Update the desktop wallpaper in the preview. */ 151 | #refreshWallpaper(manager: Adw.StyleManager) { 152 | const backgrounds = Gio.Settings.new( 153 | 'org.gnome.desktop.background', 154 | ); 155 | const path = manager.get_dark() 156 | ? backgrounds.get_string('picture-uri-dark') 157 | : backgrounds.get_string('picture-uri'); 158 | this.#backgroundCssProvider.load_from_string(`.desktop-background { 159 | background: url("${path}"); 160 | background-size: cover; 161 | }`); 162 | } 163 | 164 | /** Update the CSS style of preview widgets to match shadow settings. */ 165 | #updatePreviewStyle() { 166 | this.#unfocusCssProvider.load_from_string( 167 | `.preview { 168 | transition: box-shadow 200ms; 169 | ${boxShadowCss(this.#unfocusedShadowSettings)}; 170 | border-radius: 12px; 171 | } 172 | .preview:hover { 173 | ${boxShadowCss(this.#focusedShadowSettings)}; 174 | }`, 175 | ); 176 | this.#focusCssProvider.load_from_string( 177 | `.preview { 178 | transition: box-shadow 200ms; 179 | ${boxShadowCss(this.#focusedShadowSettings)}; 180 | border-radius: 12px; 181 | } 182 | .preview:hover { 183 | ${boxShadowCss(this.#unfocusedShadowSettings)}; 184 | }`, 185 | ); 186 | } 187 | 188 | /** Update extension preferences based on widget state. */ 189 | #setPrefs() { 190 | const focusedShadow: BoxShadow = { 191 | verticalOffset: this._focusedVerticalOffset.get_value(), 192 | horizontalOffset: this._focusedHorizontalOffset.get_value(), 193 | blurOffset: this._focusedBlurRadius.get_value(), 194 | spreadRadius: this._focusedSpreadRadius.get_value(), 195 | opacity: this._focusedOpacity.get_value(), 196 | }; 197 | this.#focusedShadowSettings = focusedShadow; 198 | const unfocusedShadow: BoxShadow = { 199 | verticalOffset: this._unfocusedVerticalOffset.get_value(), 200 | horizontalOffset: this._unfocusedHorizontalOffset.get_value(), 201 | blurOffset: this._unfocusedBlurRadius.get_value(), 202 | spreadRadius: this._unfocusedSpreadRadius.get_value(), 203 | opacity: this._unfocusedOpacity.get_value(), 204 | }; 205 | this.#unfocusedShadowSettings = unfocusedShadow; 206 | 207 | setPref('unfocused-shadow', this.#unfocusedShadowSettings); 208 | setPref('focused-shadow', this.#focusedShadowSettings); 209 | } 210 | }, 211 | ); 212 | -------------------------------------------------------------------------------- /src/preferences/pages/general.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Contains the implementation of the main preferences page. 3 | * There isn't much logic in this file. 4 | */ 5 | 6 | import Adw from 'gi://Adw'; 7 | import GLib from 'gi://GLib'; 8 | import GObject from 'gi://GObject'; 9 | import Gdk from 'gi://Gdk'; 10 | import Gio from 'gi://Gio'; 11 | 12 | import {bindPref, getPref, setPref} from '../../utils/settings.js'; 13 | import {EditShadowPage} from './edit_shadow.js'; 14 | import {ResetPage} from './reset.js'; 15 | 16 | import type Gtk from 'gi://Gtk'; 17 | import type {PaddingsRowClass} from '../widgets/paddings_row.js'; 18 | 19 | export const GeneralPage = GObject.registerClass( 20 | { 21 | Template: GLib.uri_resolve_relative( 22 | import.meta.url, 23 | 'general.ui', 24 | GLib.UriFlags.NONE, 25 | ), 26 | GTypeName: 'PrefsGeneral', 27 | 28 | // Those variables are declared inside of the `general.ui` file and 29 | // passed into the JS module prefixed with an underscore. 30 | // (skipLibadwaita -> _skipLibadwaita) 31 | InternalChildren: [ 32 | 'skipLibadwaita', 33 | 'skipLibhandy', 34 | 'borderWidth', 35 | 'borderColor', 36 | 'cornerRadius', 37 | 'cornerSmoothing', 38 | 'keepForMaximized', 39 | 'keepForFullscreen', 40 | 'paddings', 41 | 'tweakKitty', 42 | 'rightClickMenu', 43 | 'enableDebugLogs', 44 | ], 45 | }, 46 | class extends Adw.PreferencesPage { 47 | private declare _skipLibadwaita: Adw.SwitchRow; 48 | private declare _skipLibhandy: Adw.SwitchRow; 49 | private declare _borderWidth: Gtk.Adjustment; 50 | private declare _borderColor: Gtk.ColorDialogButton; 51 | private declare _cornerRadius: Gtk.Adjustment; 52 | private declare _cornerSmoothing: Gtk.Adjustment; 53 | private declare _keepForMaximized: Adw.SwitchRow; 54 | private declare _keepForFullscreen: Adw.SwitchRow; 55 | private declare _paddings: PaddingsRowClass; 56 | private declare _tweakKitty: Adw.SwitchRow; 57 | private declare _rightClickMenu: Adw.SwitchRow; 58 | private declare _enableDebugLogs: Adw.SwitchRow; 59 | 60 | #settings = getPref('global-rounded-corner-settings'); 61 | 62 | // Bind all buttons to respective prefs. 63 | constructor() { 64 | super(); 65 | 66 | bindPref( 67 | 'skip-libadwaita-app', 68 | this._skipLibadwaita, 69 | 'active', 70 | Gio.SettingsBindFlags.DEFAULT, 71 | ); 72 | bindPref( 73 | 'skip-libhandy-app', 74 | this._skipLibhandy, 75 | 'active', 76 | Gio.SettingsBindFlags.DEFAULT, 77 | ); 78 | 79 | bindPref( 80 | 'border-width', 81 | this._borderWidth, 82 | 'value', 83 | Gio.SettingsBindFlags.DEFAULT, 84 | ); 85 | 86 | const color = new Gdk.RGBA(); 87 | [color.red, color.green, color.blue, color.alpha] = 88 | this.#settings.borderColor; 89 | this._borderColor.set_rgba(color); 90 | this._borderColor.connect( 91 | 'notify::rgba', 92 | (button: Gtk.ColorDialogButton) => { 93 | const color = button.get_rgba(); 94 | this.#settings.borderColor = [ 95 | color.red, 96 | color.green, 97 | color.blue, 98 | color.alpha, 99 | ]; 100 | this.#updateGlobalConfig(); 101 | }, 102 | ); 103 | 104 | this._cornerRadius.set_value(this.#settings.borderRadius); 105 | this._cornerRadius.connect( 106 | 'value-changed', 107 | (adj: Gtk.Adjustment) => { 108 | this.#settings.borderRadius = adj.get_value(); 109 | this.#updateGlobalConfig(); 110 | }, 111 | ); 112 | 113 | this._cornerSmoothing.set_value(this.#settings.smoothing); 114 | this._cornerSmoothing.connect( 115 | 'value-changed', 116 | (adj: Gtk.Adjustment) => { 117 | this.#settings.smoothing = adj.get_value(); 118 | this.#updateGlobalConfig(); 119 | }, 120 | ); 121 | 122 | this._keepForMaximized.set_active( 123 | this.#settings.keepRoundedCorners.maximized, 124 | ); 125 | this._keepForMaximized.connect( 126 | 'notify::active', 127 | (swtch: Adw.SwitchRow) => { 128 | this.#settings.keepRoundedCorners.maximized = 129 | swtch.get_active(); 130 | this.#updateGlobalConfig(); 131 | }, 132 | ); 133 | 134 | this._keepForFullscreen.set_active( 135 | this.#settings.keepRoundedCorners.fullscreen, 136 | ); 137 | this._keepForFullscreen.connect( 138 | 'notify::active', 139 | (swtch: Adw.SwitchRow) => { 140 | this.#settings.keepRoundedCorners.fullscreen = 141 | swtch.get_active(); 142 | this.#updateGlobalConfig(); 143 | }, 144 | ); 145 | 146 | this._paddings.paddingTop = this.#settings.padding.top; 147 | this._paddings.connect( 148 | 'notify::padding-top', 149 | (row: PaddingsRowClass) => { 150 | this.#settings.padding.top = row.paddingTop; 151 | this.#updateGlobalConfig(); 152 | }, 153 | ); 154 | 155 | this._paddings.paddingBottom = this.#settings.padding.bottom; 156 | this._paddings.connect( 157 | 'notify::padding-bottom', 158 | (row: PaddingsRowClass) => { 159 | this.#settings.padding.bottom = row.paddingBottom; 160 | this.#updateGlobalConfig(); 161 | }, 162 | ); 163 | 164 | this._paddings.paddingStart = this.#settings.padding.left; 165 | this._paddings.connect( 166 | 'notify::padding-start', 167 | (row: PaddingsRowClass) => { 168 | this.#settings.padding.left = row.paddingStart; 169 | this.#updateGlobalConfig(); 170 | }, 171 | ); 172 | 173 | this._paddings.paddingEnd = this.#settings.padding.right; 174 | this._paddings.connect( 175 | 'notify::padding-end', 176 | (row: PaddingsRowClass) => { 177 | this.#settings.padding.right = row.paddingEnd; 178 | this.#updateGlobalConfig(); 179 | }, 180 | ); 181 | 182 | bindPref( 183 | 'tweak-kitty-terminal', 184 | this._tweakKitty, 185 | 'active', 186 | Gio.SettingsBindFlags.DEFAULT, 187 | ); 188 | 189 | bindPref( 190 | 'enable-preferences-entry', 191 | this._rightClickMenu, 192 | 'active', 193 | Gio.SettingsBindFlags.DEFAULT, 194 | ); 195 | 196 | bindPref( 197 | 'debug-mode', 198 | this._enableDebugLogs, 199 | 'active', 200 | Gio.SettingsBindFlags.DEFAULT, 201 | ); 202 | } 203 | 204 | showResetPage(_: Gtk.Button) { 205 | const root = this.root as unknown as Adw.PreferencesDialog; 206 | root.push_subpage(new ResetPage()); 207 | } 208 | 209 | showShadowPage(_: Adw.ActionRow) { 210 | const root = this.root as unknown as Adw.PreferencesDialog; 211 | root.push_subpage(new EditShadowPage()); 212 | } 213 | 214 | #updateGlobalConfig() { 215 | setPref('global-rounded-corner-settings', this.#settings); 216 | } 217 | }, 218 | ); 219 | -------------------------------------------------------------------------------- /src/preferences/pages/general.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 196 | 197 | -------------------------------------------------------------------------------- /src/preferences/pages/reset.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Contains the implementation of the reset page. 3 | * The list of prefs that can be reset is stored in the `#resetPrefs` variable, 4 | * and the UI is automatically generated from that. 5 | */ 6 | 7 | import Adw from 'gi://Adw'; 8 | import GLib from 'gi://GLib'; 9 | import GObject from 'gi://GObject'; 10 | 11 | import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; 12 | import { 13 | Schema, 14 | type SchemaKey, 15 | getPref, 16 | prefs, 17 | setPref, 18 | } from '../../utils/settings.js'; 19 | 20 | import type Gtk from 'gi://Gtk'; 21 | import type {RoundedCornerSettings} from '../../utils/types.js'; 22 | 23 | type RoundedCorerSettingsKey = keyof RoundedCornerSettings; 24 | type ResetKey = SchemaKey | RoundedCorerSettingsKey; 25 | 26 | export const ResetPage = GObject.registerClass( 27 | { 28 | Template: GLib.uri_resolve_relative( 29 | import.meta.url, 30 | 'reset.ui', 31 | GLib.UriFlags.NONE, 32 | ), 33 | GTypeName: 'ResetPage', 34 | InternalChildren: ['resetGroup', 'resetButton', 'dialog'], 35 | }, 36 | class extends Adw.NavigationPage { 37 | private declare _resetGroup: Adw.PreferencesGroup; 38 | private declare _resetButton: Gtk.Button; 39 | private declare _dialog: Adw.AlertDialog; 40 | 41 | // Store all switches in a list to enable the "select all" button. 42 | #rows: Adw.SwitchRow[] = []; 43 | 44 | // List of prefs that should be reset. 45 | #resetPrefs: ResetKey[] = []; 46 | 47 | // Map prefs to their labels in the UI. 48 | #resetLabels: {[Key in ResetKey]?: string} = { 49 | 'skip-libadwaita-app': 'Skip LibAdwaita Applications', 50 | 'skip-libhandy-app': 'Skip LibHandy Applications', 51 | 'focused-shadow': 'Focus Window Shadow Style', 52 | 'unfocused-shadow': 'Unfocus Window Shadow Style', 53 | 'border-width': 'Border Width', 54 | 'debug-mode': 'Enable Log', 55 | 56 | borderRadius: 'Border Radius', 57 | borderColor: 'Border Color', 58 | padding: 'Padding', 59 | keepRoundedCorners: 60 | 'Keep Rounded Corners when Maximized or Fullscreen', 61 | smoothing: 'Corner Smoothing', 62 | }; 63 | 64 | constructor() { 65 | super(); 66 | 67 | this.#buildUi(); 68 | } 69 | 70 | selectAll() { 71 | for (const row of this.#rows) { 72 | row.set_active(true); 73 | } 74 | } 75 | 76 | askForReset() { 77 | this._dialog.choose(this, null, null); 78 | } 79 | 80 | reset(_: Adw.MessageDialog, response: string) { 81 | if (response === 'cancel') { 82 | return; 83 | } 84 | 85 | const defaultRoundedCornerSettings = prefs 86 | .get_default_value('global-rounded-corner-settings') 87 | ?.recursiveUnpack() as RoundedCornerSettings; 88 | 89 | const currentRoundedCornerSettings = getPref( 90 | 'global-rounded-corner-settings', 91 | ); 92 | 93 | for (const key of this.#resetPrefs) { 94 | if (key in Schema) { 95 | // If the key is a top-level prefs schema key, reset it directly. 96 | prefs.reset(key); 97 | } else { 98 | // Otherwise, it's a key inside of `global-rounded-corner-settings`, 99 | // and should be reset accordingly. 100 | const settingsKey = key as RoundedCorerSettingsKey; 101 | currentRoundedCornerSettings[settingsKey] = 102 | defaultRoundedCornerSettings[settingsKey] as never; 103 | } 104 | } 105 | 106 | setPref( 107 | 'global-rounded-corner-settings', 108 | currentRoundedCornerSettings, 109 | ); 110 | 111 | const root = this.root as unknown as Adw.PreferencesDialog; 112 | root.pop_subpage(); 113 | } 114 | 115 | /** Generate the UI from {@link #resetLabels}. */ 116 | #buildUi() { 117 | for (const [key, label] of Object.entries(this.#resetLabels)) { 118 | const row = new Adw.SwitchRow({ 119 | active: false, 120 | name: key, 121 | title: _(label), 122 | }); 123 | row.connect('notify::active', source => 124 | this.#onToggled(source), 125 | ); 126 | this._resetGroup.add(row); 127 | this.#rows.push(row); 128 | } 129 | } 130 | 131 | /** Callback to add or remove a pref from the list of prefs to reset. */ 132 | #onToggled(source: Adw.SwitchRow): void { 133 | if (source.active) { 134 | this.#resetPrefs.push(source.name as ResetKey); 135 | } else { 136 | this.#resetPrefs = this.#resetPrefs.filter( 137 | k => k !== (source.name as ResetKey), 138 | ); 139 | } 140 | } 141 | }, 142 | ); 143 | -------------------------------------------------------------------------------- /src/preferences/pages/reset.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 53 | 54 | 55 | Reset these settings? 56 | reset 57 | cancel 58 | 59 | 60 | _Cancel 61 | _Reset 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/preferences/widgets/app_row.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Generic widget for choosing a window via a picker or a text entry. 3 | * Used in the blacklist and custom settings pages. 4 | */ 5 | 6 | import Adw from 'gi://Adw'; 7 | import GObject from 'gi://GObject'; 8 | import Gtk from 'gi://Gtk'; 9 | 10 | import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; 11 | import {onPicked, pick} from '../../window_picker/client.js'; 12 | 13 | export class AppRowClass extends Adw.ExpanderRow { 14 | #callbacks: AppRowCallbacks; 15 | 16 | #removeButton = new Gtk.Button({ 17 | icon_name: 'window-close-symbolic', 18 | css_classes: ['flat', 'circular'], 19 | valign: Gtk.Align.CENTER, 20 | }); 21 | #applyButton = new Gtk.Button({ 22 | icon_name: 'object-select-symbolic', 23 | css_classes: ['flat', 'circular'], 24 | valign: Gtk.Align.CENTER, 25 | }); 26 | #pickButton = new Gtk.Button({ 27 | icon_name: 'find-location-symbolic', 28 | css_classes: ['flat', 'circular'], 29 | valign: Gtk.Align.CENTER, 30 | }); 31 | 32 | #wmClassEntry = new Adw.EntryRow({ 33 | title: _('Window class'), 34 | }); 35 | 36 | constructor(cb: AppRowCallbacks) { 37 | super(); 38 | 39 | this.#callbacks = cb; 40 | 41 | this.#wmClassEntry.add_prefix(this.#applyButton); 42 | this.#wmClassEntry.add_prefix(this.#pickButton); 43 | this.add_row(this.#wmClassEntry); 44 | this.add_suffix(this.#removeButton); 45 | 46 | this.bind_property( 47 | 'subtitle', 48 | this.#wmClassEntry, 49 | 'text', 50 | GObject.BindingFlags.DEFAULT, 51 | ); 52 | 53 | this.add_css_class('property'); 54 | this.set_title(_('Expand this row, to pick a window')); 55 | 56 | this.#removeButton.connect('clicked', () => { 57 | this.onDelete(); 58 | }); 59 | this.#pickButton.connect('clicked', () => { 60 | this.pickWindow(this.#wmClassEntry); 61 | }); 62 | this.#applyButton.connect('clicked', () => { 63 | this.onTitleChange(this.#wmClassEntry); 64 | }); 65 | } 66 | 67 | onTitleChange(entry: Adw.EntryRow) { 68 | // Skip if the title hasn't changed 69 | if (this.subtitle === entry.text || entry.text === '') { 70 | return; 71 | } 72 | 73 | if ( 74 | this.#callbacks.onWindowChange( 75 | this, 76 | this.subtitle || '', 77 | entry.text || '', 78 | ) 79 | ) { 80 | this.set_subtitle(entry.text || ''); 81 | } 82 | } 83 | 84 | onDelete() { 85 | this.#callbacks?.onDelete(this); 86 | } 87 | 88 | pickWindow(entry: Adw.EntryRow) { 89 | onPicked(wmInstanceClass => { 90 | if (wmInstanceClass === 'window-not-found') { 91 | const win = this.root as unknown as Adw.PreferencesDialog; 92 | win.add_toast( 93 | new Adw.Toast({ 94 | title: _("Can't pick window from this position"), 95 | }), 96 | ); 97 | return; 98 | } 99 | entry.text = wmInstanceClass; 100 | }); 101 | pick(); 102 | } 103 | } 104 | 105 | export const AppRow = GObject.registerClass( 106 | { 107 | GTypeName: 'AppRow', 108 | }, 109 | AppRowClass, 110 | ); 111 | 112 | export type AppRowCallbacks = { 113 | onDelete: (row: AppRowClass) => void; 114 | onWindowChange: ( 115 | row: AppRowClass, 116 | oldWmClass: string, 117 | newWmClass: string, 118 | ) => boolean; 119 | }; 120 | -------------------------------------------------------------------------------- /src/preferences/widgets/custom_settings_row.ts: -------------------------------------------------------------------------------- 1 | /** @file An extension of {@link AppRowClass} that adds widgets for setting config overrides. */ 2 | 3 | import Adw from 'gi://Adw'; 4 | import GObject from 'gi://GObject'; 5 | import Gtk from 'gi://Gtk'; 6 | 7 | import {PaddingsRow} from './paddings_row.js'; 8 | import './app_row.js'; 9 | import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; 10 | 11 | import {type AppRowCallbacks, AppRowClass} from './app_row.js'; 12 | 13 | export class CustomSettingsRowClass extends AppRowClass { 14 | enabledRow = new Adw.SwitchRow({ 15 | title: _('Enabled'), 16 | }); 17 | 18 | #borderColorRow = new Adw.ActionRow({ 19 | title: _('Border color'), 20 | }); 21 | borderColorButton = new Gtk.ColorDialogButton({ 22 | valign: Gtk.Align.CENTER, 23 | }); 24 | borderColorDialog = new Gtk.ColorDialog(); 25 | 26 | #cornerRadiusRow = new Adw.ActionRow({ 27 | title: _('Corner radius'), 28 | }); 29 | cornerRadius = new Gtk.Adjustment({ 30 | lower: 0, 31 | upper: 40, 32 | stepIncrement: 1, 33 | pageIncrement: 1, 34 | }); 35 | 36 | #cornerSmoothingRow = new Adw.ActionRow({ 37 | title: _('Corner smoothing'), 38 | }); 39 | cornerSmoothing = new Gtk.Adjustment({ 40 | lower: 0, 41 | upper: 1, 42 | stepIncrement: 0.1, 43 | pageIncrement: 0.1, 44 | }); 45 | 46 | keepForMaximized = new Adw.SwitchRow({ 47 | title: _('Keep rounded corners when maximized'), 48 | subtitle: _( 49 | 'Always clip rounded corners even if window is maximized or tiled', 50 | ), 51 | }); 52 | keepForFullscreen = new Adw.SwitchRow({ 53 | title: _('Keep rounded corners when in fullscreen'), 54 | subtitle: _('Always clip rounded corners even for fullscreen window'), 55 | }); 56 | paddings = new PaddingsRow(); 57 | 58 | constructor(cb: AppRowCallbacks) { 59 | super(cb); 60 | 61 | this.#borderColorRow.add_suffix(this.borderColorButton); 62 | this.borderColorButton.set_dialog(this.borderColorDialog); 63 | 64 | this.#cornerRadiusRow.add_suffix( 65 | new Gtk.Scale({ 66 | valign: Gtk.Align.CENTER, 67 | hexpand: true, 68 | draw_value: true, 69 | value_pos: Gtk.PositionType.LEFT, 70 | round_digits: 0, 71 | digits: 0, 72 | orientation: Gtk.Orientation.HORIZONTAL, 73 | adjustment: this.cornerRadius, 74 | }), 75 | ); 76 | this.#cornerSmoothingRow.add_suffix( 77 | new Gtk.Scale({ 78 | valign: Gtk.Align.CENTER, 79 | hexpand: true, 80 | draw_value: true, 81 | value_pos: Gtk.PositionType.LEFT, 82 | round_digits: 1, 83 | orientation: Gtk.Orientation.HORIZONTAL, 84 | adjustment: this.cornerSmoothing, 85 | }), 86 | ); 87 | 88 | this.add_row(this.enabledRow); 89 | this.add_row(this.#borderColorRow); 90 | this.add_row(this.#cornerRadiusRow); 91 | this.add_row(this.#cornerSmoothingRow); 92 | this.add_row(this.keepForMaximized); 93 | this.add_row(this.keepForFullscreen); 94 | this.add_row(this.paddings); 95 | 96 | this.checkState(); 97 | } 98 | 99 | public checkState() { 100 | if (!this.enabledRow.get_active()) { 101 | this.toggleSensitivity(false); 102 | return; 103 | } 104 | 105 | if (this.subtitle === '') { 106 | this.toggleSensitivity(false); 107 | return; 108 | } 109 | 110 | this.toggleSensitivity(true); 111 | } 112 | 113 | private toggleSensitivity(state: boolean) { 114 | this.#borderColorRow.set_sensitive(state); 115 | this.#cornerRadiusRow.set_sensitive(state); 116 | this.#cornerSmoothingRow.set_sensitive(state); 117 | this.keepForMaximized.set_sensitive(state); 118 | this.keepForFullscreen.set_sensitive(state); 119 | this.paddings.set_sensitive(state); 120 | } 121 | } 122 | 123 | export const CustomSettingsRow = GObject.registerClass( 124 | { 125 | GTypeName: 'CustomSettingsRow', 126 | }, 127 | CustomSettingsRowClass, 128 | ); 129 | -------------------------------------------------------------------------------- /src/preferences/widgets/paddings-row.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 91 | -------------------------------------------------------------------------------- /src/preferences/widgets/paddings_row.ts: -------------------------------------------------------------------------------- 1 | /** @file A widget for setting paddings for windows, used inside of {@link CustomSettingsRow}. */ 2 | 3 | import Adw from 'gi://Adw'; 4 | import GLib from 'gi://GLib'; 5 | import GObject from 'gi://GObject'; 6 | 7 | export class PaddingsRowClass extends Adw.PreferencesRow { 8 | public declare paddingTop: number; 9 | public declare paddingBottom: number; 10 | public declare paddingStart: number; 11 | public declare paddingEnd: number; 12 | } 13 | 14 | export const PaddingsRow = GObject.registerClass( 15 | { 16 | Template: GLib.uri_resolve_relative( 17 | import.meta.url, 18 | 'paddings-row.ui', 19 | GLib.UriFlags.NONE, 20 | ), 21 | GTypeName: 'PaddingsRow', 22 | Properties: { 23 | PaddingTop: GObject.ParamSpec.int( 24 | 'padding-top', 25 | 'Padding top', 26 | 'Padding from the top', 27 | GObject.ParamFlags.READWRITE, 28 | 0, 29 | 100, 30 | 0, 31 | ), 32 | PaddingBottom: GObject.ParamSpec.int( 33 | 'padding-bottom', 34 | 'Padding bottom', 35 | 'Padding from the bottom', 36 | GObject.ParamFlags.READWRITE, 37 | 0, 38 | 100, 39 | 0, 40 | ), 41 | PaddingStart: GObject.ParamSpec.int( 42 | 'padding-start', 43 | 'Padding start', 44 | 'Padding from the start', 45 | GObject.ParamFlags.READWRITE, 46 | 0, 47 | 100, 48 | 0, 49 | ), 50 | PaddingEnd: GObject.ParamSpec.int( 51 | 'padding-end', 52 | 'Padding end', 53 | 'Padding from the end', 54 | GObject.ParamFlags.READWRITE, 55 | 0, 56 | 100, 57 | 0, 58 | ), 59 | }, 60 | }, 61 | PaddingsRowClass, 62 | ); 63 | -------------------------------------------------------------------------------- /src/prefs.ts: -------------------------------------------------------------------------------- 1 | /** @file Contains the implementation of the preferences page. */ 2 | 3 | import type Adw from 'gi://Adw'; 4 | import GLib from 'gi://GLib'; 5 | import Gdk from 'gi://Gdk'; 6 | import Gtk from 'gi://Gtk'; 7 | 8 | import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; 9 | import {prefsTabs} from './preferences/index.js'; 10 | import {logDebug} from './utils/log.js'; 11 | import {initPrefs, uninitPrefs} from './utils/settings.js'; 12 | 13 | export default class RoundedWindowCornersRebornPrefs extends ExtensionPreferences { 14 | async fillPreferencesWindow(win: Adw.PreferencesWindow) { 15 | initPrefs(this.getSettings()); 16 | 17 | for (const page of prefsTabs) { 18 | win.add(new page()); 19 | } 20 | 21 | // Disconnect all signals when closing the preferences 22 | win.connect('close-request', () => { 23 | logDebug('Disconnect Signals'); 24 | uninitPrefs(); 25 | }); 26 | 27 | this.#loadCss(); 28 | } 29 | 30 | #loadCss() { 31 | const display = Gdk.Display.get_default(); 32 | if (display) { 33 | const css = new Gtk.CssProvider(); 34 | const path = GLib.build_filenamev([ 35 | import.meta.url, 36 | 'stylesheet-prefs.css', 37 | ]); 38 | css.load_from_path(path); 39 | Gtk.StyleContext.add_provider_for_display(display, css, 0); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/README.md: -------------------------------------------------------------------------------- 1 | # `utils` 2 | 3 | This directory contains functions which are used in different places throughout 4 | the codebase, so they can't be put anywhere else. 5 | 6 | ## `background_menu.ts` 7 | 8 | Handles adding and removing the RWC settings item in the desktop context menu. 9 | 10 | ## `box_shadow.ts` 11 | 12 | Contains a function for converting box shadow JS objects into CSS styles for 13 | those shadows. 14 | 15 | ## `constants.ts` 16 | 17 | Defines the constants used in the codebase. 18 | 19 | ## `file.ts` 20 | 21 | Contains utility functions for reading file contents. 22 | 23 | ## `log.ts` 24 | 25 | Provides wrapper functions for printing out debug messages. 26 | 27 | ## `settings.ts` 28 | 29 | Provides wrappers around the GSettings object that add type safety and 30 | automatically convert values between JS types and GLib Variant types that are 31 | used for storing GSettings. 32 | 33 | ## `types.ts` 34 | 35 | Provides types used throughout the codebase, mostly for storing settings. 36 | -------------------------------------------------------------------------------- /src/utils/background_menu.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Handles adding and removing the RWC settings item in the desktop 3 | * context menu. 4 | * 5 | * XXX: It seems like this relies on GNOME Shell methods which aren't supposed 6 | * to be public. Perhaps this would be removed in the future. 7 | */ 8 | 9 | import { 10 | Extension, 11 | gettext as _, 12 | } from 'resource:///org/gnome/shell/extensions/extension.js'; 13 | import {PopupMenuItem} from 'resource:///org/gnome/shell/ui/popupMenu.js'; 14 | 15 | import type Clutter from 'gi://Clutter'; 16 | import type {PopupMenu} from 'resource:///org/gnome/shell/ui/popupMenu.js'; 17 | 18 | /** 19 | * Clutter Actor of the desktop background. 20 | * 21 | * https://gjs-docs.gnome.org/meta15~15/meta.backgroundactor 22 | */ 23 | type BackgroundActor = Clutter.Actor & { 24 | _backgroundMenu: PopupMenu; 25 | }; 26 | 27 | /** Enable the "rounded corner settings" item in desktop context menu. */ 28 | export function enableBackgroundMenuItem() { 29 | for (const background of global.windowGroup.firstChild.get_children()) { 30 | const menu = (background as BackgroundActor)._backgroundMenu; 31 | addItemToMenu(menu); 32 | } 33 | } 34 | 35 | /** Disable the "rounded corner settings" item in desktop context menu. */ 36 | export function disableBackgroundMenuItem() { 37 | for (const background of global.windowGroup.firstChild.get_children()) { 38 | const menu = (background as BackgroundActor)._backgroundMenu; 39 | removeItemFromMenu(menu); 40 | } 41 | } 42 | 43 | /** 44 | * Add the menu item to the background menu. 45 | * 46 | * @param menu - BackgroundMenu to add the item to. 47 | */ 48 | function addItemToMenu(menu: PopupMenu) { 49 | const rwcMenuItemName = _('Rounded Corners Settings...'); 50 | 51 | // Check if the item already exists 52 | for (const item of menu._getMenuItems()) { 53 | if ( 54 | item instanceof PopupMenuItem && 55 | item.label.text === rwcMenuItemName 56 | ) { 57 | return; 58 | } 59 | } 60 | 61 | menu.addAction(rwcMenuItemName, () => { 62 | const extension = Extension.lookupByURL(import.meta.url) as Extension; 63 | extension.openPreferences(); 64 | }); 65 | } 66 | 67 | /** 68 | * Remove the menu item from the background menu. 69 | * 70 | * @param menu - BackgroundMenu to remove the item from. 71 | */ 72 | function removeItemFromMenu(menu: PopupMenu) { 73 | const items = menu._getMenuItems(); 74 | const rwcMenuItemName = _('Rounded Corners Settings...'); 75 | for (const item of items) { 76 | if ( 77 | item instanceof PopupMenuItem && 78 | item.label.text === rwcMenuItemName 79 | ) { 80 | item.destroy(); 81 | break; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/utils/box_shadow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Contains a single function - {@link boxShadowCss}, which converts 3 | * {@link BoxShadow} objects into CSS code for the shadow. 4 | */ 5 | 6 | import type {BoxShadow} from './types.js'; 7 | 8 | /** 9 | * Generate a CSS style for a box shadow from the provided {@link BoxShadow} 10 | * object. 11 | * 12 | * @param shadow - The settings for the box shadow. 13 | * @param scale - The scale of the window, 1 by default. 14 | * @returns The box-shadow CSS string. 15 | */ 16 | export function boxShadowCss(shadow: BoxShadow, scale = 1) { 17 | return `box-shadow: ${shadow.horizontalOffset * scale}px 18 | ${shadow.verticalOffset * scale}px 19 | ${shadow.blurOffset * scale}px 20 | ${shadow.spreadRadius * scale}px 21 | rgba(0,0,0, ${shadow.opacity / 100})`; 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | /** @file Defines the constants used in this extension. */ 2 | 3 | /** Name of the rounded corners effect */ 4 | export const ROUNDED_CORNERS_EFFECT = 'Rounded Corners Effect'; 5 | 6 | /** Name of the shadow clipping effect */ 7 | export const CLIP_SHADOW_EFFECT = 'Clip Shadow Effect'; 8 | 9 | /** Padding of shadow actors */ 10 | export const SHADOW_PADDING = 80; 11 | 12 | /** 13 | * Hardcoded shadow size for certain applications that have to be 14 | * manually clipped 15 | */ 16 | export const APP_SHADOWS = { 17 | kitty: [11, 35, 11, 11], 18 | }; 19 | 20 | /** Name of shadow actor to be added in overview */ 21 | export const OVERVIEW_SHADOW_ACTOR = 'Shadow Actor (Overview)'; 22 | -------------------------------------------------------------------------------- /src/utils/file.ts: -------------------------------------------------------------------------------- 1 | /** @file Contains utility functions for reading file contents. */ 2 | 3 | import GLib from 'gi://GLib'; 4 | import Gio from 'gi://Gio'; 5 | 6 | /** 7 | * Read file contents as a string. 8 | * 9 | * @param path - The path to the file to be read. 10 | * @returns Contents of the file as a UTF-8 string. 11 | */ 12 | export function readFile(path: string) { 13 | const file = Gio.File.new_for_path(path); 14 | 15 | const contents = file.load_contents(null)[1]; 16 | 17 | const decoder = new TextDecoder('utf-8'); 18 | return decoder.decode(contents); 19 | } 20 | 21 | /** 22 | * Read a file relative to the current module. 23 | * 24 | * @param module - `import.meta.url` of the current module. 25 | * @param path - File path relative to the current module. 26 | * @returns Contents of the file as a UTF-8 string. 27 | */ 28 | export function readRelativeFile(module: string, path: string) { 29 | const basedir = GLib.path_get_dirname(module); 30 | const fileUri = GLib.build_filenamev([basedir, path]); 31 | const filePath = GLib.filename_from_uri(fileUri)[0]; 32 | return readFile(filePath ?? ''); 33 | } 34 | 35 | /** 36 | * Read a shader file and split it into declarations and main code, since 37 | * GNOME's `add_glsl_snippet` function takes those parts as two separate 38 | * arguments. 39 | * 40 | * @param module - `import.meta.url` of the current module. 41 | * @param path - File path relative to the current module. 42 | * @returns A list containing the declarations as the first element and 43 | * contents of the main function as the second. 44 | */ 45 | export function readShader(module: string, path: string) { 46 | const shader = readRelativeFile(module, path); 47 | // This function isn't called very often, so creating the regex at the top 48 | // level doesn't really make sense. 49 | // biome-ignore lint/performance/useTopLevelRegex: 50 | let [declarations, code] = shader.split(/^.*?main\(\s?\)\s?/m); 51 | declarations = declarations.trim(); 52 | code = code.trim().replace(/^[{}]/gm, '').trim(); 53 | return [declarations, code]; 54 | } 55 | -------------------------------------------------------------------------------- /src/utils/log.ts: -------------------------------------------------------------------------------- 1 | /** @file Provides wrapper functions for printing out debug messages. */ 2 | 3 | import {getPref} from './settings.js'; 4 | 5 | /** 6 | * Log a message with a [Rounded Window Corners] prefix, but only 7 | * when debug mode is enabled. 8 | */ 9 | export function logDebug(...args: unknown[]) { 10 | if (getPref('debug-mode')) { 11 | console.log(`[Rounded Window Corners] ${args}`); 12 | } 13 | } 14 | 15 | /** 16 | * Log an error with a [Rounded Window Corners] prefix. 17 | */ 18 | export function logError(...args: unknown[]) { 19 | console.error(`[Rounded Window Corners] ${args}`); 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/settings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Provides wrappers around the GSettings object that add type safety and 3 | * automatically convert values between JS types and GLib Variant types that 4 | * are used for storing GSettings. 5 | */ 6 | 7 | import GLib from 'gi://GLib'; 8 | 9 | import {logDebug} from './log.js'; 10 | 11 | import type GObject from 'gi://GObject'; 12 | import type Gio from 'gi://Gio'; 13 | import type { 14 | BoxShadow, 15 | CustomRoundedCornerSettings, 16 | RoundedCornerSettings, 17 | } from './types.js'; 18 | 19 | /** Mapping of schema keys to the JS representation of their type. */ 20 | type Schema = { 21 | 'settings-version': number; 22 | blacklist: string[]; 23 | 'skip-libadwaita-app': boolean; 24 | 'skip-libhandy-app': boolean; 25 | 'border-width': number; 26 | 'global-rounded-corner-settings': RoundedCornerSettings; 27 | 'custom-rounded-corner-settings': CustomRoundedCornerSettings; 28 | 'focused-shadow': BoxShadow; 29 | 'unfocused-shadow': BoxShadow; 30 | 'debug-mode': boolean; 31 | 'tweak-kitty-terminal': boolean; 32 | 'enable-preferences-entry': boolean; 33 | }; 34 | 35 | /** All existing schema keys. */ 36 | export type SchemaKey = keyof Schema; 37 | 38 | /** Mapping of schema keys to their GLib Variant type string */ 39 | export const Schema = { 40 | 'settings-version': 'u', 41 | blacklist: 'as', 42 | 'skip-libadwaita-app': 'b', 43 | 'skip-libhandy-app': 'b', 44 | 'border-width': 'i', 45 | 'global-rounded-corner-settings': 'a{sv}', 46 | 'custom-rounded-corner-settings': 'a{sv}', 47 | 'focused-shadow': 'a{si}', 48 | 'unfocused-shadow': 'a{si}', 49 | 'debug-mode': 'b', 50 | 'tweak-kitty-terminal': 'b', 51 | 'enable-preferences-entry': 'b', 52 | }; 53 | 54 | /** The raw GSettings object for direct manipulation. */ 55 | export let prefs: Gio.Settings; 56 | 57 | /** 58 | * Initialize the {@link prefs} object with existing GSettings. 59 | * 60 | * @param gSettings - GSettings to initialize the prefs with. 61 | */ 62 | export function initPrefs(gSettings: Gio.Settings) { 63 | resetOutdated(gSettings); 64 | prefs = gSettings; 65 | } 66 | 67 | /** Delete the {@link prefs} object for garbage collection. */ 68 | export function uninitPrefs() { 69 | (prefs as Gio.Settings | null) = null; 70 | } 71 | 72 | /** 73 | * Get a preference from GSettings and convert it from a GLib Variant to a 74 | * JavaScript type. 75 | * 76 | * @param key - The key of the preference to get. 77 | * @returns The value of the preference. 78 | */ 79 | export function getPref(key: K): Schema[K] { 80 | return prefs.get_value(key).recursiveUnpack(); 81 | } 82 | 83 | /** 84 | * Pack a value into a GLib Variant type and store it in GSettings. 85 | * 86 | * @param key - The key of the preference to set. 87 | * @param value - The value to set the preference to. 88 | */ 89 | export function setPref(key: K, value: Schema[K]) { 90 | logDebug(`Settings pref: ${key}, ${value}`); 91 | let variant: GLib.Variant; 92 | 93 | if (key === 'global-rounded-corner-settings') { 94 | variant = packRoundedCornerSettings(value as RoundedCornerSettings); 95 | } else if (key === 'custom-rounded-corner-settings') { 96 | variant = packCustomRoundedCornerSettings( 97 | value as CustomRoundedCornerSettings, 98 | ); 99 | } else { 100 | variant = new GLib.Variant(Schema[key], value); 101 | } 102 | 103 | prefs.set_value(key, variant); 104 | } 105 | 106 | /** A simple type-checked wrapper around {@link prefs.bind} */ 107 | export function bindPref( 108 | key: SchemaKey, 109 | object: GObject.Object, 110 | property: string, 111 | flags: Gio.SettingsBindFlags, 112 | ) { 113 | prefs.bind(key, object, property, flags); 114 | } 115 | 116 | /** 117 | * Reset setting keys that changed their type between releases 118 | * to avoid conflicts. 119 | * 120 | * @param prefs the GSettings object to clean. 121 | */ 122 | function resetOutdated(prefs: Gio.Settings) { 123 | const lastVersion = 7; 124 | const currentVersion = prefs 125 | .get_user_value('settings-version') 126 | ?.recursiveUnpack(); 127 | 128 | if (!currentVersion || currentVersion < lastVersion) { 129 | if (prefs.list_keys().includes('black-list')) { 130 | prefs.reset('black-list'); 131 | } 132 | prefs.reset('global-rounded-corner-settings'); 133 | prefs.reset('custom-rounded-corner-settings'); 134 | if (prefs.list_keys().includes('border-color')) { 135 | prefs.reset('border-color'); 136 | } 137 | prefs.reset('focused-shadow'); 138 | prefs.reset('unfocused-shadow'); 139 | prefs.set_uint('settings-version', lastVersion); 140 | } 141 | } 142 | 143 | /** 144 | * Pack rounded corner settings into a GLib Variant object. 145 | * 146 | * Since rounded corner settings are stored as a dictionary where the values 147 | * are of different types, it can't be automatically packed into a variant. 148 | * Instead, we need to pack each of the values into the correct variant 149 | * type, and only then pack the entire dictionary into a variant with type 150 | * "a{sv}" (dictionary with string keys and arbitrary variant values). 151 | * 152 | * @param settings - The rounded corner settings to pack. 153 | * @returns The packed GLib Variant object. 154 | */ 155 | function packRoundedCornerSettings(settings: RoundedCornerSettings) { 156 | const padding = new GLib.Variant('a{su}', settings.padding); 157 | const keepRoundedCorners = new GLib.Variant( 158 | 'a{sb}', 159 | settings.keepRoundedCorners, 160 | ); 161 | const borderRadius = GLib.Variant.new_uint32(settings.borderRadius); 162 | const smoothing = GLib.Variant.new_double(settings.smoothing); 163 | const borderColor = new GLib.Variant('(dddd)', settings.borderColor); 164 | const enabled = GLib.Variant.new_boolean(settings.enabled); 165 | 166 | const variantObject = { 167 | padding: padding, 168 | keepRoundedCorners: keepRoundedCorners, 169 | borderRadius: borderRadius, 170 | smoothing: smoothing, 171 | borderColor: borderColor, 172 | enabled: enabled, 173 | }; 174 | 175 | return new GLib.Variant('a{sv}', variantObject); 176 | } 177 | 178 | /** 179 | * Pack custom rounded corner overrides into a GLib Variant object. 180 | * 181 | * Custom rounded corner settings are stored as a dictionary from window 182 | * wm_class to {@link RoundedCornerSettings} objects. See the documentation for 183 | * {@link packRoundedCornerSettings} for more information on why manual packing 184 | * is needed here. 185 | * 186 | * @param settings - The custom rounded corner setting overrides to pack. 187 | * @returns The packed GLib Variant object. 188 | */ 189 | function packCustomRoundedCornerSettings( 190 | settings: CustomRoundedCornerSettings, 191 | ) { 192 | const packedSettings: Record> = {}; 193 | for (const [wmClass, windowSettings] of Object.entries(settings)) { 194 | packedSettings[wmClass] = packRoundedCornerSettings(windowSettings); 195 | } 196 | 197 | const variant = new GLib.Variant('a{sv}', packedSettings); 198 | return variant; 199 | } 200 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | /** @file Provides types used throughout the codebase, mostly for storing settings. */ 2 | 3 | import type Meta from 'gi://Meta'; 4 | import type St from 'gi://St'; 5 | 6 | /** Bounds of rounded corners */ 7 | export type Bounds = { 8 | x1: number; 9 | y1: number; 10 | x2: number; 11 | y2: number; 12 | }; 13 | 14 | /** Settings for corner rounding. */ 15 | export type RoundedCornerSettings = { 16 | keepRoundedCorners: { 17 | maximized: boolean; 18 | fullscreen: boolean; 19 | }; 20 | borderRadius: number; 21 | smoothing: number; 22 | padding: { 23 | left: number; 24 | right: number; 25 | top: number; 26 | bottom: number; 27 | }; 28 | borderColor: [number, number, number, number]; 29 | enabled: boolean; 30 | }; 31 | 32 | /** Rounded corner settings exceptions for specific windows. */ 33 | export type CustomRoundedCornerSettings = { 34 | [wmClass: string]: RoundedCornerSettings; 35 | }; 36 | 37 | /** Window shadow properties. */ 38 | export type BoxShadow = { 39 | opacity: number; 40 | spreadRadius: number; 41 | blurOffset: number; 42 | verticalOffset: number; 43 | horizontalOffset: number; 44 | }; 45 | 46 | /** 47 | * A window actor with rounded corners. 48 | * 49 | * This type is needed to store extra custom properties on a window actor. 50 | */ 51 | export type RoundedWindowActor = Meta.WindowActor & { 52 | rwcCustomData?: { 53 | shadow: St.Bin; 54 | unminimizedTimeoutId: number; 55 | }; 56 | }; 57 | -------------------------------------------------------------------------------- /src/window_picker/README.md: -------------------------------------------------------------------------------- 1 | # `window_picker` 2 | 3 | The extensions preferences window runs in a separate isolated process which has 4 | no access to GNOME Shell methods. However, the window picking functionality is 5 | based on GNOME Shell's Looking Glass. 6 | 7 | To allow the preferences window to communicate with the main process, the code 8 | in this directory creates a DBus service, which has a method to open the window 9 | picker and a signal to transmit the class of the selected window. 10 | 11 | ## `iface.xml` 12 | 13 | Defines the DBus interface for the window picker. 14 | 15 | ## `service.ts` 16 | 17 | Contains the implementation of the DBus interface. 18 | 19 | ## `client.ts` 20 | 21 | Provides wrapper JavaScript functions around the DBus method calls. 22 | -------------------------------------------------------------------------------- /src/window_picker/client.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file This file provides wrapper functions around the DBus window picker 3 | * interface. 4 | */ 5 | 6 | import Gio from 'gi://Gio'; 7 | 8 | const connection = Gio.DBus.session; 9 | const busName = 'org.gnome.Shell'; 10 | const interfaceName = 'org.gnome.Shell.Extensions.RoundedWindowCorners'; 11 | const objectPath = '/org/gnome/shell/extensions/RoundedWindowCorners'; 12 | 13 | /** Open the window picker and select a window. */ 14 | export function pick() { 15 | connection.call( 16 | busName, 17 | objectPath, 18 | interfaceName, 19 | 'pick', 20 | null, 21 | null, 22 | Gio.DBusCallFlags.NO_AUTO_START, 23 | -1, 24 | null, 25 | null, 26 | ); 27 | } 28 | 29 | /** 30 | * Connect a callback to the `picked` signal, which is emitted when a window 31 | * is picked. 32 | * 33 | * @param callback - The function to execute when the window is picked. 34 | */ 35 | export function onPicked(callback: (wmInstanceClass: string) => void) { 36 | const id = connection.signal_subscribe( 37 | busName, 38 | interfaceName, 39 | 'picked', 40 | objectPath, 41 | null, 42 | Gio.DBusSignalFlags.NONE, 43 | (_conn, _sender, _objectPath, _iface, _signal, params) => { 44 | const val = params.get_child_value(0); 45 | callback(val.get_string()[0]); 46 | connection.signal_unsubscribe(id); 47 | }, 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/window_picker/iface.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/window_picker/service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file This file contains the implementation of the DBus interface for the 3 | * window picker. See the {@link WindowPicker} class for more information. 4 | */ 5 | 6 | import GLib from 'gi://GLib'; 7 | import Gio from 'gi://Gio'; 8 | import Meta from 'gi://Meta'; 9 | 10 | import {Inspector} from 'resource:///org/gnome/shell/ui/lookingGlass.js'; 11 | import * as Main from 'resource:///org/gnome/shell/ui/main.js'; 12 | 13 | import {readRelativeFile} from '../utils/file.js'; 14 | import {logDebug} from '../utils/log.js'; 15 | 16 | /** 17 | * This class provides the implementation of the DBus interface for the window 18 | * picker. It implements a single method - `pick` - which opens the window picker 19 | * and allows the user to select a window. 20 | */ 21 | export class WindowPicker { 22 | #iface = readRelativeFile(import.meta.url, 'iface.xml'); 23 | #dbus = Gio.DBusExportedObject.wrapJSObject(this.#iface, this); 24 | 25 | /** Emit the wm_class of the picked window to the `picked` signal. */ 26 | #sendPickedWindow(wmClass: string) { 27 | this.#dbus.emit_signal('picked', new GLib.Variant('(s)', [wmClass])); 28 | } 29 | 30 | /** 31 | * Open the window picker and select a window. 32 | * 33 | * This uses the window picker from GNOME's Looking Glass. This is the 34 | * easiest way to pick a window, and this is also what's used by other 35 | * extensions such as Blur my Shell. 36 | */ 37 | pick() { 38 | const lookingGlass = Main.createLookingGlass(); 39 | const inspector = new Inspector(lookingGlass); 40 | 41 | inspector.connect('target', (me, target, x, y) => { 42 | logDebug(`${me}: pick ${target} in ${x}, ${y}`); 43 | 44 | // Remove the red border effect when the window is picked. 45 | const effectName = 'lookingGlass_RedBorderEffect'; 46 | for (const effect of target.get_effects()) { 47 | if (effect.toString().includes(effectName)) { 48 | target.remove_effect(effect); 49 | } 50 | } 51 | 52 | let actor = target; 53 | 54 | // If the picked actor is not a Meta.WindowActor, which happens 55 | // often since it's usually a Meta.SurfaceActor, try to find its 56 | // parent which is a Meta.WindowActor. 57 | for (let i = 0; i < 2; i++) { 58 | if (actor == null || actor instanceof Meta.WindowActor) { 59 | break; 60 | } 61 | actor = actor.get_parent(); 62 | } 63 | 64 | if (!(actor instanceof Meta.WindowActor)) { 65 | this.#sendPickedWindow('window-not-found'); 66 | return; 67 | } 68 | 69 | this.#sendPickedWindow( 70 | actor.metaWindow.get_wm_class_instance() ?? 'window-not-found', 71 | ); 72 | }); 73 | 74 | inspector.connect('closed', () => { 75 | lookingGlass.close(); 76 | }); 77 | } 78 | 79 | export() { 80 | this.#dbus.export( 81 | Gio.DBus.session, 82 | '/org/gnome/shell/extensions/RoundedWindowCorners', 83 | ); 84 | logDebug('DBus Service exported'); 85 | } 86 | 87 | unexport() { 88 | this.#dbus.unexport(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "esnext", 5 | "moduleResolution": "bundler", 6 | "sourceMap": false, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "outDir": "./_build/" 10 | }, 11 | "include": ["src/**/*.ts"] 12 | } 13 | --------------------------------------------------------------------------------