Compare commits

...

31 commits

Author SHA1 Message Date
5af952d285 chore: refresh lockfile post-upstream rebase
Leyoda 2026 – GPLv3
2026-07-04 11:24:49 +02:00
d5eb4b8308 fix: absorb libcosmic theme-v2 API + reqwest stream feature
Leyoda 2026 – GPLv3
2026-07-04 11:23:11 +02:00
9de2d66f85 chore: use local cosmic-text checkout 2026-07-04 11:21:51 +02:00
f9f329d85f feat: align terminal with local cosmic stack 2026-07-04 11:21:51 +02:00
Frederic Laing
1e49511fcf fix: accumulate fractional wheel-line scroll deltas 2026-06-30 17:20:17 -06:00
Jeremy Soller
1d3d8837df Epoch 1.2.0 version update
Generated by cosmic-epoch scripts/version-update.sh
2026-06-30 13:20:35 -06:00
Igor Matuszewski
18d45bc88f fix: report effective OSC 11 background 2026-06-26 09:44:58 -04:00
Michael Murphy
fde0f34137
Merge pull request #852 from weblate/weblate-pop-os-cosmic-term
i18n: translation update from Hosted Weblate
2026-06-25 17:04:51 +02:00
Igor Matuszewski
042c915990 feat: report focus in and out events 2026-06-25 14:06:02 +02:00
Hosted Weblate
aa30e551e2
i18n: translation updates from weblate
Co-authored-by: Arve Eriksson <arve.eriksson@startmail.com>
Co-authored-by: CYAXXX <85353920+CYAXXX@users.noreply.github.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Geeson Wan <wang14240@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jim Spentzos <jimspentzos2000@gmail.com>
Co-authored-by: Julien Brouillard <julienbrouillard1@gmail.com>
Co-authored-by: VandaL <vandalhj@gmail.com>
Co-authored-by: Walter William Beckerleg Bruckman <spayk.99@protonmail.com>
Co-authored-by: Zahid Rizky Fakhri <zahidrizkyfakhri@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: lorduskordus <lorduskordus@gmail.com>
Co-authored-by: therealmate <hellogaming91@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/ar/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/cs/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/de/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/el/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/fr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/id/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/kmr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/pl/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/sv/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/zh_Hant/
Translation: Pop OS/COSMIC Term
2026-06-24 14:01:30 +00:00
Jeremy Soller
a5acc057af Epoch 1.1.0 version update
Generated by cosmic-epoch scripts/version-update.sh
2026-06-23 10:51:56 -06:00
James Feister
18b4450c0f
fix: avoid re-reading system theme from disk on every menu bar render 2026-06-12 14:47:00 -06:00
Jeremy Soller
8c042673e7 Epoch 1.0.16 version update
Generated by cosmic-epoch scripts/version-update.sh
2026-06-09 12:04:37 -06:00
Levi Portenier
3832971d04
Merge pull request #828 from phlax/fix-undo
keybindings: Fix terminal undo
2026-06-02 14:33:52 -06:00
Jeremy Soller
7cf99dff31 Epoch 1.0.15 version update
Generated by cosmic-epoch scripts/version-update.sh
2026-06-02 12:12:26 -06:00
Levi Portenier
31d97dcadb
Merge pull request #840 from pop-os/shortcut-fix
chore: update libcosmic
2026-06-01 14:20:51 -06:00
Ryan Northey
b5dca36359 keybindings: Fix terminal undo
Signed-off-by: Ryan Northey <ryan@synca.io>
2026-06-01 20:16:33 +01:00
Ashley Wulber
5f922f0fd1 chore: update libcosmic
should fix #837
2026-06-01 14:45:01 -04:00
Jeremy Soller
9eafd070bb
Merge pull request #836 from gene1wood/increase-scrollback-history
Set scrolling_history to 100,000
2026-06-01 07:42:39 -06:00
Gene Wood
ee25528ad9
Set scrolling_history to 100,000
Relates to #684
2026-05-29 11:02:10 -07:00
Hojjat
67938814b6 fix: avoid unnecessary shape-until-cursor at start-up 2026-05-29 18:56:59 +02:00
Hojjat
02d977ac4d fix: send the correct up/down code in APP_CURSOR mode
Fixes scrolling in `less`
2026-05-29 17:14:22 +02:00
Hojjat
124931faff fix: do not drop scrolling events from smooth scrolling mice 2026-05-29 17:14:22 +02:00
Hojjat
3d8ca65b75 chore: support more named keys for keyboard shortcuts 2026-05-28 21:18:28 +02:00
Hojjat Abdollahi
f5cbed08d6
fix(libcosmic): fix item highlight in wayland context menu 2026-05-28 21:10:17 +02:00
Hojjat
91fd140ae3 feat: option to open new windows in the current directory 2026-05-27 23:58:24 -04:00
Jeremy Soller
fa611ea07d
Merge pull request #830 from pop-os/epoch-update
Epoch 1.0.14 version update
2026-05-26 13:45:39 -06:00
Ashley Wulber
512cc0d250 fix: libcosmic updates 2026-05-26 15:40:59 -04:00
Jeremy Soller
e484ae5305
Merge pull request #812 from weblate/weblate-pop-os-cosmic-term
i18n: translation update from Hosted Weblate
2026-05-26 09:11:36 -06:00
Jeremy Soller
7f41fd19ac Epoch 1.0.14 version update
Generated by cosmic-epoch scripts/version-update.sh
2026-05-26 08:32:02 -06:00
Hosted Weblate
d650868cdf
i18n: translation updates from weblate
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hugo Carvalho <hugokarvalho@hotmail.com>
Co-authored-by: oddib <60390653+oddib@users.noreply.github.com>
Co-authored-by: therealmate <hellogaming91@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/ga/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/hu/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/pt/
Translation: Pop OS/COSMIC Term
2026-05-25 21:11:34 +00:00
27 changed files with 1520 additions and 867 deletions

1585
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-term"
version = "1.0.13"
version = "1.2.0"
authors = ["Jeremy Soller <jeremy@system76.com>"]
edition = "2024"
license = "GPL-3.0-only"
@ -17,7 +17,10 @@ regex = "1"
ron = "0.11"
serde = { version = "1", features = ["serde_derive"] }
shlex = "1"
tokio = { version = "1", features = ["sync"] }
tokio = { version = "1", features = ["sync", "rt", "macros", "time"] }
# HTTP client for Modjo AI integration
reqwest = { version = "0.12", features = ["json", "stream"], optional = true }
serde_json = { version = "1", optional = true }
# CLI arguments
clap_lex = "0.7"
# Internationalization
@ -36,15 +39,16 @@ thiserror = { version = "2.0", optional = true }
secstr = { version = "0.5", optional = true }
[dependencies.cosmic-files]
git = "https://github.com/pop-os/cosmic-files.git"
path = "../cosmic-files"
default-features = false
[dependencies.cosmic-text]
version = "0.19"
path = "../cosmic-text"
features = ["monospace_fallback", "shape-run-cache"]
[dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic.git"
[dependencies.cosmic]
package = "libcosmic-yoda"
path = "../libcosmic"
default-features = false
#TODO: a11y feature crashes file chooser dialog
features = ["about", "autosize", "multi-window", "tokio", "winit", "surface-message"]
@ -57,17 +61,40 @@ xdgen = "0.1"
[features]
default = ["dbus-config", "wgpu", "wayland", "password_manager"]
dbus-config = ["libcosmic/dbus-config"]
wgpu = ["libcosmic/wgpu", "cosmic-files/wgpu"]
wayland = ["libcosmic/wayland", "cosmic-files/wayland"]
dbus-config = ["cosmic/dbus-config"]
wgpu = ["cosmic/wgpu", "cosmic-files/wgpu"]
wayland = ["cosmic/wayland", "cosmic-files/wayland"]
password_manager = ["secret-service", "thiserror", "secstr"]
modjo = ["dep:reqwest", "dep:serde_json"]
[profile.release-with-debug]
inherits = "release"
debug = true
#[patch.'https://github.com/pop-os/libcosmic']
#libcosmic = { path = "../libcosmic" }
#cosmic-config = { path = "../libcosmic/cosmic-config" }
#cosmic-theme = { path = "../libcosmic/cosmic-theme" }
[patch.'https://github.com/pop-os/libcosmic']
cosmic-config = { path = "../libcosmic/cosmic-config" }
cosmic-theme = { path = "../libcosmic/cosmic-theme" }
iced = { path = "../libcosmic/iced" }
iced_accessibility = { path = "../libcosmic/iced/accessibility" }
iced_core = { path = "../libcosmic/iced/core" }
iced_futures = { path = "../libcosmic/iced/futures" }
iced_graphics = { path = "../libcosmic/iced/graphics" }
iced_renderer = { path = "../libcosmic/iced/renderer" }
iced_runtime = { path = "../libcosmic/iced/runtime" }
iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
iced_wgpu = { path = "../libcosmic/iced/wgpu" }
iced_widget = { path = "../libcosmic/iced/widget" }
iced_winit = { path = "../libcosmic/iced/winit" }
[patch.'https://github.com/pop-os/cosmic-protocols']
cosmic-protocols = { path = "../cosmic-protocols" }
cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" }
[patch.'https://github.com/pop-os/cosmic-text.git']
cosmic-text = { path = "../cosmic-text" }
[patch.'https://forge.aditua.com/leyoda/window_clipboard.git']
window_clipboard = { path = "../window_clipboard" }
dnd = { path = "../window_clipboard/dnd" }
mime = { path = "../window_clipboard/mime" }

30
debian/changelog vendored
View file

@ -1,3 +1,33 @@
cosmic-term (1.2.0) noble; urgency=medium
* Epoch 1.2.0 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 30 Jun 2026 12:21:56 -0600
cosmic-term (1.1.0) noble; urgency=medium
* Epoch 1.1.0 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 23 Jun 2026 08:49:37 -0600
cosmic-term (1.0.16) noble; urgency=medium
* Epoch 1.0.16 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 09 Jun 2026 10:26:45 -0600
cosmic-term (1.0.15) noble; urgency=medium
* Epoch 1.0.15 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 02 Jun 2026 11:03:25 -0600
cosmic-term (1.0.14) noble; urgency=medium
* Epoch 1.0.14 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 26 May 2026 08:31:52 -0600
cosmic-term (1.0.13) noble; urgency=medium
* Epoch 1.0.13 version update

View file

@ -145,5 +145,5 @@ tab-activate = نشّط لسان { $number }
toggle-fullscreen = بدّل ملء الشاشة
type-to-search = اكتب للبحث...
copy-link = انسخ الرابط
tab-new-inherit-working-directory = الألسنة الجديدة تستخدم المجلد الحالي
tab-new-inherit-working-directory-description = افتح ألسنة جديدة في مجلد العمل الخاص باللسان النشط
tab-new-inherit-working-directory = الألسنة الجديدة والنوافذ تستخدم المجلد الحالي
tab-new-inherit-working-directory-description = افتح الألسنة الجديدة والنوافذ في مجلد العمل الخاص باللسان النشط

View file

@ -107,5 +107,5 @@ shortcut-replace-body = { $binding } je již přiřazena k „{ $existing }“.
shortcut-capture-hint = Stiskněte kombinaci kláves
paste-primary = Vložit primární
copy-link = Kopírovat odkaz
tab-new-inherit-working-directory = Nové karty používají aktuální adresář
tab-new-inherit-working-directory-description = Otevírat nové karty v pracovním adresáři aktivní karty
tab-new-inherit-working-directory = Nové karty a okna používají aktuální adresář
tab-new-inherit-working-directory-description = Otevírat nové karty a okna v pracovním adresáři aktivní karty

View file

@ -147,5 +147,5 @@ paste-primary = Primär einfügen
add-another-keybinding = Weitere Tastenbelegung hinzufügen
shortcut-replace-body = { $binding } ist bereits { $existing } zugeordnet. Durch { $new_action } ersetzen?
keywords = Befehl;Shell;Terminal;CLI;
tab-new-inherit-working-directory = Neue Tabs verwenden das aktuelle Verzeichnis
tab-new-inherit-working-directory-description = Neue Tabs im Arbeitsverzeichnis des aktiven Tabs öffnen
tab-new-inherit-working-directory = Neue Tabs und Fenster verwenden das aktuelle Verzeichnis
tab-new-inherit-working-directory-description = Neue Tabs und Fenster im Arbeitsverzeichnis des aktiven Tabs öffnen

View file

@ -150,5 +150,5 @@ paste-primary = Πρωτεύουσα επικόλληση
shortcut-group-other = Άλλες
shortcut-group-zoom = Ζουμ
shortcut-replace-body = Ο συνδυασμός { $binding } έχει ήδη αντιστοιχιστεί στην ενέργεια «{ $existing }». Θέλετε να αντικατασταθεί με την ενέργεια «{ $new_action }»;
tab-new-inherit-working-directory = Οι νέες καρτέλες χρησιμοποιούν τον τρέχοντα κατάλογο
tab-new-inherit-working-directory-description = Άνοιγμα νέων καρτελών στον τρέχοντα κατάλογο της ενεργής καρτέλας
tab-new-inherit-working-directory = Οι νέες καρτέλες και παράθυρα χρησιμοποιούν τον τρέχοντα κατάλογο
tab-new-inherit-working-directory-description = Άνοιγμα νέων καρτελών και παραθύρων στον τρέχοντα κατάλογο της ενεργής καρτέλας

View file

@ -63,13 +63,14 @@ focus-follow-mouse = Typing focus follows mouse
advanced = Advanced
show-headerbar = Show header
show-header-description = Reveal the header from the right-click menu
tab-new-inherit-working-directory = New tabs use current directory
tab-new-inherit-working-directory-description = Open new tabs in the active tab's working directory
tab-new-inherit-working-directory = New tabs and windows use current directory
tab-new-inherit-working-directory-description = Open new tabs and windows in the active tab's working directory
### Keyboard shortcuts
add-another-keybinding = Add another keybinding
cancel = Cancel
close-window = Close window
confirm = Confirm
copy-or-sigint = Copy or SIGINT
disable = Disable
focus-pane-down = Focus pane down
@ -83,6 +84,7 @@ password-manager = Password manager
paste-primary = Paste primary
replace = Replace
reset-to-default = Reset to default
reset = Reset
shortcut-capture-hint = Press the key combination
shortcut-group-clipboard = Clipboard
shortcut-group-other = Other
@ -92,6 +94,9 @@ shortcut-group-zoom = Zoom
shortcut-replace-body = { $binding } is already assigned to { $existing }. Replace it with { $new_action }?
shortcut-replace-title = Replace shortcut?
tab-activate = Activate tab { $number }
rename-tab = Rename tab
tab-name-placeholder = Tab name
tab-rename-title = Rename tab
toggle-fullscreen = Toggle fullscreen
type-to-search = Type to search...

View file

@ -121,6 +121,7 @@ copy-link = Copier le lien
add-another-keybinding = Ajouter un autre raccourci clavier
cancel = Annuler
close-window = Fermer la fenêtre
confirm = Confirmer
copy-or-sigint = Copier ou SIGINT
disable = Désactiver
keyboard-shortcuts = Raccourcis clavier
@ -144,8 +145,12 @@ focus-pane-left = Passer au panneau de gauche
focus-pane-right = Passer au panneau de droite
focus-pane-up = Passer au panneau du dessus
reset-to-default = Rétablir les paramètres par défaut
reset = Réinitialiser
toggle-fullscreen = Basculer en plein écran
rename-tab = Renommer l'onglet
tab-name-placeholder = Nom de l'onglet
tab-rename-title = Renommer l'onglet
comment = Émulateur de terminal pour le bureau COSMIC
keywords = Commande;Shell;Terminal;CLI;
tab-new-inherit-working-directory = Les nouveaux onglets utilisent le répertoire actuel
tab-new-inherit-working-directory-description = Ouvrir les nouveaux onglets dans le répertoire de travail de l'onglet actif
tab-new-inherit-working-directory = Les nouveaux onglets et fenêtres utilisent le répertoire actuel
tab-new-inherit-working-directory-description = Ouvrir les nouveaux onglets et fenêtres dans le répertoire de travail de l'onglet actif

View file

@ -147,3 +147,5 @@ type-to-search = Clóscríobh le cuardach a dhéanamh...
comment = Aithriseoir teirminéal don deasc COSMIC
keywords = Ordú;Blaosc;Teirminéal;CLI;
copy-link = Cóipeáil Nasc
tab-new-inherit-working-directory = Úsáideann cluaisíní nua an eolaire reatha
tab-new-inherit-working-directory-description = Oscail cluaisíní nua in eolaire oibre an chluaisín ghníomhaigh

View file

@ -1,6 +1,6 @@
cosmic-terminal = COSMIC Terminál
comment = Terminálemulátor a COSMIC asztali környezethez
keywords = parancssor;terminál;
keywords = parancssor;héj;terminál;CLI;
new-terminal = Új terminál
# Context Pages
@ -32,7 +32,7 @@ new-profile = Új profil
make-default = Beállítás alapértelmezettként
working-directory = Munkakönyvtár
hold = Maradjon nyitva
remain-open = Maradjon nyitva a gyermekfolyamat kilépése után
remain-open = Maradjon nyitva a gyermekfolyamat kilépése után.
## Settings
@ -147,5 +147,5 @@ tab-activate = { $number }. lap aktiválása
toggle-fullscreen = Teljes képernyő váltása
type-to-search = Gépelj a kereséshez…
copy-link = Hivatkozás másolása
tab-new-inherit-working-directory = Az új lapok az aktuális könyvtárat használják
tab-new-inherit-working-directory-description = Az új lapok az aktív lap munkakönyvtárában nyílnak meg
tab-new-inherit-working-directory = Az új lapok és ablakok az aktuális könyvtárat használják
tab-new-inherit-working-directory-description = Az új lapok és ablakok az aktív lap munkakönyvtárában nyílnak meg

View file

@ -107,5 +107,5 @@ focus-pane-up = Fokuskan panel ke atas
comment = Emulator terminal untuk desktop COSMIC
keywords = Perintah;Shell;Terminal;CLI;
copy-link = Salin Tautan
tab-new-inherit-working-directory = Tab baru menggunakan direktori saat ini
tab-new-inherit-working-directory-description = Buka tab baru di direktori pekerjaan tab aktif
tab-new-inherit-working-directory = Tab dan jendela baru menggunakan direktori saat ini
tab-new-inherit-working-directory-description = Buka tab dan jendela baru di direktori pekerjaan tab aktif

View file

@ -0,0 +1,111 @@
rename = Navê biguhêrîne
cancel = Têk bibe
name = Nav
delete = Jê bibe
add-profile = Profîlê tevlî bike
find-next = Yê pêş bibîne
splits = Beşkirin
import-errors = Şaşetiyan têxîne
select-all = Hemûyan hilbijêre
new-window = Çarçoveya nû
menu-profiles = Profîl...
new-terminal = Termînala nû
tab-title-description = Sernavê rûgerê yê berdest biguhêrîne
remain-open = Piştî derketina pêvajoyê vekirî bimîne.
working-directory = Rêgeha xebatê
import = Têxîne
profiles = Profîl
tab-title = Sernavê rûgerê
quit = Biqedîne
edit = Biguhêrîne
copy = Jê bigire
export = Derxîne
theme = Rûkar
appearance = Xuyang
close-tab = Rûgerê bigire
light = Ronî
advanced = Pêşketî
support = Piştgirî
default-font = Cûrenivîs
paste = Pêve bike
menu-settings = Sazkarî...
command-line = Rêza fermanê
view = Dîtin
dark = Tarî
hold = Ragire
match-desktop = Li gorî sermaseyê
find-previous = Yê paş bibîne
cosmic-terminal = Termînalê COSMIC
find-placeholder = Bibîne...
advanced-font-settings = Sazkariyên pêşketî yên cûrenivîsê
settings = Sazkarî
font = Cûrenivîs
default-zoom-step = Gavên nêzîkbûnê
clear-scrollback = Dîroka şemitandinê pak bike
open-link = Girêdanê veke
file = Pel
make-default = Bike berdest
new-profile = Profîla nû
profile = Profîl
new-tab = Rûgera nû
comment = Emûlatorê termînalê ji bo sermaseya COSMIC
keywords = Ferman;Shell;Termînal;CLI;
close-window = Çarçoveyê bigire
keyboard-shortcuts = Kurteriyên kilîtdankê
menu-keyboard-shortcuts = Kurteriyên kilîtdankê...
no-shortcuts = Kurterê tune ne
shortcut-group-clipboard = Jêgirtdank
shortcut-group-other = Ên din
shortcut-group-tabs = Rûger
shortcut-group-window = Çarçove
shortcut-group-zoom = Nêzîkbûn
repository = Embar
find = Bibîne
replace = Biguhêrîne
syntax-light = Şêwaza rengê ronî
default-font-weight = Giraniya cûrenivîsê ya normal
zoom-in = Nivîsa mezintir
previous-tab = Rûgera paş
show-headerbar = Sernavê nîşan bide
zoom-out = Nivîsa biçûktir
split-vertical = Stûnî dabeş bike
syntax-dark = Şêwaza rengê tarî
menu-about = Derbarê termînala COSMIC...
menu-color-schemes = Şêwazên rengê...
opacity = Zelaliya paşrûyê
zoom-reset = Mezinahiya nivîsê ya berdest
passwords-title = Borînpeyv
pane-toggle-maximize = Mezinkirinê biguhêrîne
password-input = Borînpeyv
color-schemes = Şêwazên rengê
default-dim-font-weight = Giraniya cûrenivîsê ya sivik
use-bright-bold = Nivîsa qalind ronîtir bike
default-bold-font-weight = Giraniya cûrenivîsê ya qalind
password-input-description = Danasîn
add-password = Borînpeyvê tevlî bike
default-font-stretch = Dirêjahiya cûrenivîsê
menu-password-manager = Borînpeyv...
default-font-size = Mezinahiya cûrenivîsê
split-horizontal = Asoyî dabeş bike
focus-follow-mouse = Balkişandinê nivîsê mişkê dişopîne
next-tab = Rûgera pêş
show-header-description = Sernavê ji menuya rast-tikandin nîşan bide
tab-new-inherit-working-directory = Rûger û çarçoveyên nû peldanka heyî bi kar bîne
tab-new-inherit-working-directory-description = Rûger û çarçoveyên nû di peldanka xebatê ya çalak de veke
add-another-keybinding = Kurteriyeke din tevlî bike
copy-or-sigint = Jê bigire yan jî SIGINT
disable = Neçalak bike
focus-pane-down = Balê bikişîne ser destgeha jêrîn
focus-pane-left = Balê bikişîne ser destgeha çepê
focus-pane-right = Balê bikişîne ser destgeha rastê
focus-pane-up = Balê bikişîne ser destgeha jorîn
password-manager = Rêveberê borînpeyvê
paste-primary = A sereke pêve bike
reset-to-default = Vegere rewşa berdest
shortcut-capture-hint = Kombînasyona kilîtan bitikîne
shortcut-replace-body = { $binding } berê ji bo { $existing } hatiye destnîşankirin. Tu dixwazî bi { $new_action } re biguherînî?
shortcut-replace-title = Kurterê biguhêrînî?
tab-activate = Rûgera { $number } çalak bike
toggle-fullscreen = Dîmendera tijî biguhêrîne
type-to-search = Ji bo lêgerînê binivîse...
copy-link = Girêdanê jê bigire

View file

@ -72,3 +72,4 @@ default-zoom-step = Zoom steg
focus-follow-mouse = Skrivefokus følger musen
hold = Hold
cancel = Avbryt
replace = Erstatt

View file

@ -147,5 +147,5 @@ focus-pane-left = Aktywuj lewy panel
focus-pane-right = Aktywuj prawy panel
focus-pane-up = Aktywuj panel wyżej
copy-link = Skopiuj Odnośnik
tab-new-inherit-working-directory = Nowe karty użyją obecnego katalogu
tab-new-inherit-working-directory-description = Otwieraj nowe karty w katalogu roboczym aktywnej karty
tab-new-inherit-working-directory = Nowe karty i okna użyją obecnego katalogu
tab-new-inherit-working-directory-description = Otwieraj nowe karty i okna w katalogu roboczym aktywnej karty

View file

@ -147,5 +147,5 @@ tab-activate = Ativar aba { $number }
toggle-fullscreen = Alternar para tela cheia
type-to-search = Digite para pesquisar...
copy-link = Copiar link
tab-new-inherit-working-directory = Novas abas usam o diretório atual
tab-new-inherit-working-directory-description = Abrir novas abas no diretório de trabalho da aba ativa
tab-new-inherit-working-directory = Novas abas e janelas usam o diretório atual
tab-new-inherit-working-directory-description = Abrir novas abas e janelas no diretório de trabalho da aba ativa

View file

@ -113,3 +113,4 @@ menu-about = Acerca do Terminal COSMIC...
repository = Repositório
support = Suporte
clear-scrollback = Limpar scrollback
cancel = Cancelar

View file

@ -151,5 +151,5 @@ shortcut-group-other = Andra
shortcut-group-tabs = Flikar
shortcut-group-zoom = Zooma
copy-link = Kopiera länk
tab-new-inherit-working-directory-description = Öppna nya flikar i den aktiva flikens arbetskatalog
tab-new-inherit-working-directory = Nya flikar använder aktuell katalog
tab-new-inherit-working-directory-description = Öppna nya flikar och fönster i den aktiva flikens arbetskatalog
tab-new-inherit-working-directory = Nya flikar och fönster använder aktuell katalog

View file

@ -147,5 +147,5 @@ toggle-fullscreen = 切换全屏
comment = COSMIC 桌面的终端模拟器
keywords = 指令;外壳;终端;命令行界面;
copy-link = 复制链接
tab-new-inherit-working-directory = 新标签使用当前目录
tab-new-inherit-working-directory-description = 在活动标签的工作目录中打开新标签
tab-new-inherit-working-directory = 新标签和窗口使用当前目录
tab-new-inherit-working-directory-description = 在活动标签的工作目录中打开新标签和窗口

View file

@ -147,5 +147,5 @@ copy-link = 複製連結
shortcut-replace-body = { $binding } 已經分配至 { $existing }。 要取代它為 { $new_action }?
clear-scrollback = 清除捲動回朔
comment = COSMIC 桌面終端機模擬器
tab-new-inherit-working-directory = 新分頁使用目前目錄
tab-new-inherit-working-directory-description = 在作用中分頁的工作目錄開啟新分頁
tab-new-inherit-working-directory = 新分頁和視窗使用目前目錄
tab-new-inherit-working-directory-description = 在作用中分頁的工作目錄開啟新分頁和視窗

View file

@ -290,13 +290,19 @@ impl Config {
}
}
pub fn color_scheme_kind(&self) -> ColorSchemeKind {
if self.app_theme.theme().theme_type.is_dark() {
pub fn color_scheme_kind(&self, system_theme: &theme::Theme) -> ColorSchemeKind {
match self.app_theme {
AppTheme::Dark => ColorSchemeKind::Dark,
AppTheme::Light => ColorSchemeKind::Light,
AppTheme::System => {
if system_theme.theme_type.is_dark() {
ColorSchemeKind::Dark
} else {
ColorSchemeKind::Light
}
}
}
}
// Get a sorted and adjusted for duplicates list of color scheme names and ids
pub fn color_scheme_names(
@ -358,8 +364,11 @@ impl Config {
}
// Get current syntax theme based on dark mode
pub fn syntax_theme(&self, profile_id_opt: Option<ProfileId>) -> (String, ColorSchemeKind) {
let color_scheme_kind = self.color_scheme_kind();
pub fn syntax_theme(
&self,
color_scheme_kind: ColorSchemeKind,
profile_id_opt: Option<ProfileId>,
) -> (String, ColorSchemeKind) {
let theme_name = match profile_id_opt.and_then(|profile_id| self.profiles.get(&profile_id))
{
Some(profile) => match color_scheme_kind {

View file

@ -4,6 +4,7 @@
use alacritty_terminal::{event::Event as TermEvent, term, term::color::Colors as TermColors, tty};
use cosmic::iced::clipboard::dnd::DndAction;
use cosmic::iced::core::keyboard::key::Named;
use cosmic::iced::keyboard::key::Physical;
use cosmic::widget::menu::action::MenuAction;
use cosmic::widget::menu::key_bind::KeyBind;
use cosmic::widget::pane_grid::Pane;
@ -185,7 +186,10 @@ fn main() -> Result<(), Box<dyn Error>> {
});
// Terminal config setup
let term_config = term::Config::default();
let term_config = term::Config {
scrolling_history: 100_000,
..term::Config::default()
};
// Set up environmental variables for terminal
tty::setup_env();
// Override TERM for better compatibility
@ -277,6 +281,7 @@ pub enum Action {
TabNewNoProfile,
TabNext,
TabPrev,
TabRename,
ToggleFullscreen,
WindowClose,
WindowNew,
@ -330,6 +335,7 @@ impl Action {
Self::TabNewNoProfile => Message::TabNewNoProfile,
Self::TabNext => Message::TabNext,
Self::TabPrev => Message::TabPrev,
Self::TabRename => Message::TabRenameOpen(entity_opt),
Self::ToggleFullscreen => Message::ToggleFullscreen,
Self::WindowClose => Message::WindowClose,
Self::WindowNew => Message::WindowNew,
@ -383,7 +389,7 @@ pub enum Message {
FindSearchValueChanged(String),
MiddleClick(pane_grid::Pane, Option<segmented_button::Entity>),
FocusFollowMouse(bool),
Key(Modifiers, Key),
Key(Modifiers, Physical, Key),
LaunchUrl(String),
LaunchUrlByMenu,
Modifiers(Modifiers),
@ -437,6 +443,12 @@ pub enum Message {
TabNewNoProfile,
TabNext,
TabPrev,
TabRenameOpenForPane(pane_grid::Pane, segmented_button::Entity),
TabRenameOpen(Option<segmented_button::Entity>),
TabRenameInput(String),
TabRenameCommit,
TabRenameCancel,
TabRenameResetToDefault,
TermEvent(pane_grid::Pane, segmented_button::Entity, TermEvent),
TermEventTx(mpsc::UnboundedSender<(pane_grid::Pane, segmented_button::Entity, TermEvent)>),
ToggleFullscreen,
@ -510,6 +522,8 @@ pub struct App {
color_scheme_expanded: Option<(ColorSchemeKind, Option<ColorSchemeId>)>,
color_scheme_renaming: Option<(ColorSchemeKind, ColorSchemeId, String)>,
color_scheme_rename_id: widget::Id,
rename_tab: Option<(segmented_button::Entity, String)>,
rename_tab_id: widget::Id,
color_scheme_tab_model: widget::segmented_button::SingleSelectModel,
profile_expanded: Option<ProfileId>,
show_advanced_font_settings: bool,
@ -674,7 +688,7 @@ impl App {
// Update terminal window background color
{
let color = Color::from(theme.cosmic().background.base);
let color = Color::from(theme.cosmic().background(false).base);
let bytes = color.into_rgba8();
let data = u32::from(bytes[2])
| (u32::from(bytes[1]) << 8)
@ -684,11 +698,12 @@ impl App {
}
// Set config of all tabs
let color_scheme_kind = self.config.color_scheme_kind(&theme);
for (_pane, tab_model) in self.pane_model.panes.iter() {
for entity in tab_model.iter() {
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
let mut terminal = terminal.lock().unwrap();
terminal.set_config(&self.config, &self.themes);
terminal.set_config(&self.config, color_scheme_kind, &self.themes);
}
}
}
@ -704,6 +719,7 @@ impl App {
// skip writing config to fs when zoom in/ out
// recalculate the pane due to the changes of zoom_adj value
// but only for the active pane/tab
let color_scheme_kind = self.config.color_scheme_kind(self.core.system_theme());
if let Some(tab_model) = self.pane_model.active() {
for entity in tab_model.iter() {
if tab_model.is_active(entity)
@ -720,7 +736,7 @@ impl App {
}
_ => {}
}
terminal.set_config(&self.config, &self.themes);
terminal.set_config(&self.config, color_scheme_kind, &self.themes);
}
}
}
@ -758,7 +774,9 @@ impl App {
}
fn update_focus(&self) -> Task<Message> {
if self.find {
if self.rename_tab.is_some() {
widget::text_input::focus(self.rename_tab_id.clone())
} else if self.find {
widget::text_input::focus(self.find_search_id.clone())
} else if self.core.window.show_context {
// Right now we only care about the KeyboardShortcuts context page, so we use a simple if.
@ -990,6 +1008,7 @@ impl App {
//TODO: re-export in libcosmic
iced::widget::text::Style {
color: Some(cosmic.destructive_text_color().into()),
..Default::default()
}
}))
.into(),
@ -1547,10 +1566,11 @@ impl App {
self.pane_model.set_focus(pane);
match &self.term_event_tx_opt {
Some(term_event_tx) => {
let color_scheme_kind = self.config.color_scheme_kind(self.core.system_theme());
let colors = self
.themes
.get(&self.config.syntax_theme(profile_id_opt))
.or_else(|| match self.config.color_scheme_kind() {
.get(&self.config.syntax_theme(color_scheme_kind, profile_id_opt))
.or_else(|| match color_scheme_kind {
ColorSchemeKind::Dark => self
.themes
.get(&(config::COSMIC_THEME_DARK.to_string(), ColorSchemeKind::Dark)),
@ -1627,7 +1647,11 @@ impl App {
tab_title_override,
) {
Ok(mut terminal) => {
terminal.set_config(&self.config, &self.themes);
terminal.set_config(
&self.config,
color_scheme_kind,
&self.themes,
);
tab_model
.data_set::<Mutex<Terminal>>(entity, Mutex::new(terminal));
}
@ -1666,7 +1690,7 @@ impl App {
None => {
log::error!(
"failed to find terminal theme {:?}",
self.config.syntax_theme(profile_id_opt)
self.config.syntax_theme(color_scheme_kind, profile_id_opt)
);
//TODO: fall back to known good theme
}
@ -1872,6 +1896,8 @@ impl Application for App {
color_scheme_expanded: None,
color_scheme_renaming: None,
color_scheme_rename_id: widget::Id::unique(),
rename_tab: None,
rename_tab_id: widget::Id::unique(),
color_scheme_tab_model: widget::segmented_button::Model::default(),
profile_expanded: None,
show_advanced_font_settings: false,
@ -2419,7 +2445,7 @@ impl Application for App {
Message::FocusFollowMouse(focus_follow_mouse) => {
config_set!(focus_follow_mouse, focus_follow_mouse);
}
Message::Key(modifiers, key) => {
Message::Key(modifiers, physical, key) => {
// Hard-coded keys
match key {
Key::Named(Named::Copy) => {
@ -2459,7 +2485,7 @@ impl Application for App {
// Handle configurable keys
for (key_bind, action) in &self.key_binds {
if key_bind.matches(modifiers, &key) {
if key_bind.matches(modifiers, &key, Some(&physical)) {
return self.update(action.message(None));
}
}
@ -2942,7 +2968,9 @@ impl Application for App {
let pos_y = _position.y as i32;
tasks.push(cosmic::task::message(Message::Surface(
cosmic::surface::action::app_popup(move |_app: &mut Self| {
cosmic::surface::action::app_popup(
|_: &Self| cosmic::surface::action::LiveSettings::default(),
move |_app: &mut Self| {
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
use cosmic::iced::runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
@ -3019,6 +3047,64 @@ impl Application for App {
}
}
}
Message::TabRenameOpenForPane(pane, entity) => {
self.pane_model.set_focus(pane);
return self.update(Message::TabRenameOpen(Some(entity)));
}
Message::TabRenameOpen(entity_opt) => {
if let Some(tab_model) = self.pane_model.active() {
let entity = entity_opt.unwrap_or_else(|| tab_model.active());
let initial = tab_model
.text(entity)
.map(|s| s.to_string())
.unwrap_or_default();
self.rename_tab = Some((entity, initial));
self.pane_model.unfocus_all_terminals();
return self.update_focus();
}
}
Message::TabRenameInput(value) => {
if let Some((_, buffer)) = self.rename_tab.as_mut() {
*buffer = value;
}
}
Message::TabRenameCommit => {
if let Some((entity, value)) = self.rename_tab.take() {
let trimmed = value.trim().to_string();
if let Some(tab_model) = self.pane_model.active_mut() {
if trimmed.is_empty() {
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
terminal.lock().unwrap().tab_title_override = None;
}
tab_model.text_set(entity, fl!("new-terminal"));
} else {
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
terminal.lock().unwrap().tab_title_override = Some(trimmed.clone());
}
tab_model.text_set(entity, trimmed);
}
}
self.pane_model.update_terminal_focus();
return self.update_title(None);
}
}
Message::TabRenameCancel => {
self.rename_tab = None;
self.pane_model.update_terminal_focus();
}
Message::TabRenameResetToDefault => {
if let Some((entity, _)) = self.rename_tab.take()
&& let Some(tab_model) = self.pane_model.active_mut()
{
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
terminal.lock().unwrap().tab_title_override = None;
}
tab_model.text_set(entity, fl!("new-terminal"));
self.pane_model.update_terminal_focus();
return self.update_title(None);
}
self.pane_model.update_terminal_focus();
}
Message::TermEvent(pane, entity, event) => {
match event {
TermEvent::Bell => {
@ -3054,7 +3140,7 @@ impl Application for App {
&& let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity)
{
let terminal = terminal.lock().unwrap();
let rgb = terminal.colors()[index].unwrap_or_default();
let rgb = terminal.effective_color(index);
let text = f(rgb);
terminal.input_no_scroll(text.into_bytes());
}
@ -3225,12 +3311,21 @@ impl Application for App {
}
}
Message::WindowNew => match env::current_exe() {
Ok(exe) => match process::Command::new(&exe).spawn() {
Ok(exe) => {
let mut command = process::Command::new(&exe);
if self.config.tab_new_inherit_working_directory
&& let Some(dir) = self.active_terminal_working_directory()
{
command.arg("--working-directory");
command.arg(dir);
}
match command.spawn() {
Ok(_child) => {}
Err(err) => {
log::error!("failed to execute {:?}: {}", exe, err);
}
},
}
}
Err(err) => {
log::error!("failed to get current executable path: {}", err);
}
@ -3335,6 +3430,31 @@ impl Application for App {
}
fn dialog(&self) -> Option<Element<'_, Message>> {
if let Some((_, buffer)) = self.rename_tab.as_ref() {
let input = widget::text_input(fl!("tab-name-placeholder"), buffer)
.id(self.rename_tab_id.clone())
.on_input(Message::TabRenameInput)
.on_submit(|_| Message::TabRenameCommit);
return Some(
widget::dialog()
.title(fl!("tab-rename-title"))
.control(input)
.primary_action(
widget::button::suggested(fl!("confirm"))
.on_press(Message::TabRenameCommit),
)
.secondary_action(
widget::button::standard(fl!("cancel")).on_press(Message::TabRenameCancel),
)
.tertiary_action(
widget::button::text(fl!("reset"))
.on_press(Message::TabRenameResetToDefault),
)
.into(),
);
}
let conflict = self.shortcut_conflict.as_ref()?;
let binding = shortcuts::binding_display(&conflict.binding);
let existing = shortcuts::action_label(conflict.existing_action);
@ -3409,7 +3529,6 @@ impl Application for App {
let pane_grid = PaneGrid::new(&self.pane_model.panes, |pane, tab_model, _is_maximized| {
let mut tab_column = widget::column::with_capacity(1);
if tab_model.iter().count() > 1 {
tab_column = tab_column.push(
widget::container(
widget::tab_bar::horizontal(tab_model)
@ -3419,16 +3538,15 @@ impl Application for App {
.button_height(32)
.button_spacing(space_xxs)
.on_activate(Message::TabActivate)
.on_close(|entity| Message::TabClose(Some(entity))),
.on_close(|entity| Message::TabClose(Some(entity)))
.on_context(move |entity| Message::TabRenameOpenForPane(pane, entity)),
)
.class(style::Container::Custom(Box::new(|theme| {
let cosmic = theme.cosmic();
cosmic::iced::widget::container::Style {
icon_color: Some(Color::from(cosmic.background.on)),
text_color: Some(Color::from(cosmic.background.on)),
background: Some(iced::Background::Color(
cosmic.background.base.into(),
)),
icon_color: Some(Color::from(cosmic.background(false).on)),
text_color: Some(Color::from(cosmic.background(false).on)),
background: Some(iced::Background::Color(cosmic.background(false).base.into())),
border: iced::Border::default(),
shadow: iced::Shadow::default(),
snap: true,
@ -3436,7 +3554,6 @@ impl Application for App {
})))
.width(Length::Fill),
);
}
let entity = tab_model.active();
let entity_middle_click = tab_model.active();
@ -3448,7 +3565,7 @@ impl Application for App {
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
let mut terminal_box = terminal_box(terminal, &self.key_binds)
.id(terminal_id)
.disabled(self.core.window.show_context)
.disabled(self.core.window.show_context || self.rename_tab.is_some())
.on_context_menu(move |menu_state| Message::TabContextMenu(pane, menu_state))
.on_middle_click(move || Message::MiddleClick(pane, Some(entity_middle_click)))
.on_open_hyperlink(Some(Box::new(Message::LaunchUrl)))
@ -3608,9 +3725,12 @@ impl Application for App {
Subscription::batch([
event::listen_with(|event, _status, _window_id| match event {
Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => {
Some(Message::Key(modifiers, key))
}
Event::Keyboard(KeyEvent::KeyPressed {
key,
physical_key,
modifiers,
..
}) => Some(Message::Key(modifiers, physical_key, key)),
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
Some(Message::Modifiers(modifiers))
}

View file

@ -45,10 +45,11 @@ pub fn context_menu<'a>(
String::new()
};
fn key_style(theme: &cosmic::Theme) -> TextStyle {
let mut color = theme.cosmic().background.component.on;
let mut color = theme.cosmic().background(false).component.on;
color.alpha *= 0.75;
TextStyle {
color: Some(color.into()),
..Default::default()
}
}
@ -94,7 +95,9 @@ pub fn context_menu<'a>(
)),
Element::from(divider::horizontal::light()),
Element::from(menu_item(fl!("new-tab"), Action::TabNew)),
Element::from(menu_item(fl!("rename-tab"), Action::TabRename)),
Element::from(menu_item(fl!("menu-settings"), Action::Settings)),
Element::from(menu_item(fl!("menu-about"), Action::About)),
];
#[cfg(feature = "password_manager")]
{
@ -128,7 +131,7 @@ pub fn context_menu<'a>(
//TODO: move style to libcosmic
.style(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background.component;
let component = &cosmic.background(false).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
@ -176,7 +179,7 @@ pub fn color_scheme_menu<'a>(
//TODO: move style to libcosmic
.style(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background.component;
let component = &cosmic.background(false).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
@ -205,6 +208,8 @@ pub fn menu_bar<'a>(
//TODO: what to do if there are no profiles?
let color_scheme_kind = config.color_scheme_kind(core.system_theme());
responsive_menu_bar()
.item_height(ItemHeight::Dynamic(40))
.item_width(ItemWidth::Uniform(320))
@ -224,6 +229,7 @@ pub fn menu_bar<'a>(
MenuItem::Folder(fl!("profile"), profile_items),
MenuItem::Button(fl!("menu-profiles"), None, Action::Profiles),
MenuItem::Divider,
MenuItem::Button(fl!("rename-tab"), None, Action::TabRename),
MenuItem::Button(fl!("close-tab"), None, Action::TabClose),
MenuItem::Divider,
MenuItem::Button(fl!("quit"), None, Action::WindowClose),
@ -266,7 +272,7 @@ pub fn menu_bar<'a>(
MenuItem::Button(
fl!("menu-color-schemes"),
None,
Action::ColorSchemes(config.color_scheme_kind()),
Action::ColorSchemes(color_scheme_kind),
),
MenuItem::Button(
fl!("menu-keyboard-shortcuts"),

View file

@ -3,10 +3,6 @@ use cosmic::{
iced::{Event, keyboard::Modifiers, mouse::Button},
};
use crate::terminal::Terminal;
const SCROLL_SPEED: u32 = 3;
#[derive(Default)]
pub struct MouseReporter {
last_movment_x: Option<u32>,
@ -17,6 +13,30 @@ pub struct MouseReporter {
}
impl MouseReporter {
pub fn accumulate_scroll(
&mut self,
delta: ScrollDelta,
cell_width: f32,
cell_height: f32,
) -> (i32, i32) {
match delta {
ScrollDelta::Lines { x, y } => {
self.accumulated_scroll_x += x;
self.accumulated_scroll_y += y;
}
ScrollDelta::Pixels { x, y } => {
self.accumulated_scroll_x += x / cell_width;
self.accumulated_scroll_y += y / cell_height;
}
}
let lines_x = self.accumulated_scroll_x as i32;
let lines_y = self.accumulated_scroll_y as i32;
self.accumulated_scroll_x -= lines_x as f32;
self.accumulated_scroll_y -= lines_y as f32;
(lines_x, lines_y)
}
fn button_number(button: Button) -> Option<u8> {
match button {
Button::Left => Some(0),
@ -178,24 +198,7 @@ impl MouseReporter {
x: u32,
y: u32,
) -> impl Iterator<Item = Vec<u8>> {
let (lines_x, lines_y) = match delta {
ScrollDelta::Lines { x, y } => (x as i32, y as i32),
ScrollDelta::Pixels { x, y } => {
//Accumulate change
self.accumulated_scroll_x += x / term_cell_width;
self.accumulated_scroll_y += y / term_cell_height;
//Resolve lines crossed
let lines_x = self.accumulated_scroll_x as i32;
let lines_y = self.accumulated_scroll_y as i32;
//Subtract accounted lines from accumulators
self.accumulated_scroll_x -= lines_x as f32;
self.accumulated_scroll_y -= lines_y as f32;
(lines_x, lines_y)
}
};
let (lines_x, lines_y) = self.accumulate_scroll(delta, term_cell_width, term_cell_height);
//Resolve modifier flags
let mut modifier_flags = 0;
@ -235,26 +238,4 @@ impl MouseReporter {
term_code.as_bytes().to_vec()
})
}
//Emulate mouse wheel scroll with up/down arrows. Using mouse spec uses
//scroll-back and scroll-forw actions, which moves whole windows like page up/page down.
pub fn report_mouse_wheel_as_arrows(
terminal: &Terminal,
term_cell_width: f32,
term_cell_height: f32,
delta: ScrollDelta,
) {
let (_delta_x, delta_y) = match delta {
ScrollDelta::Lines { x, y } => (x, y),
ScrollDelta::Pixels { x, y } => (x / term_cell_width, y / term_cell_height),
};
//Send delta_y * SCROLL_SPEED number of Up/Down arrows
for _ in 0..(delta_y.abs() as u32 * SCROLL_SPEED) {
if delta_y > 0.0 {
terminal.input_no_scroll(b"\x1B[A".as_slice())
} else if delta_y < 0.0 {
terminal.input_no_scroll(b"\x1B[B".as_slice())
}
}
}
}

View file

@ -87,6 +87,7 @@ pub enum KeyBindAction {
TabNew,
TabNext,
TabPrev,
TabRename,
ToggleFullscreen,
WindowClose,
WindowNew,
@ -127,6 +128,7 @@ impl KeyBindAction {
Self::TabNew => Some(Action::TabNew),
Self::TabNext => Some(Action::TabNext),
Self::TabPrev => Some(Action::TabPrev),
Self::TabRename => Some(Action::TabRename),
Self::ToggleFullscreen => Some(Action::ToggleFullscreen),
Self::WindowClose => Some(Action::WindowClose),
Self::WindowNew => Some(Action::WindowNew),
@ -288,6 +290,7 @@ pub fn action_label(action: KeyBindAction) -> String {
KeyBindAction::TabNew => fl!("new-tab"),
KeyBindAction::TabNext => fl!("next-tab"),
KeyBindAction::TabPrev => fl!("previous-tab"),
KeyBindAction::TabRename => fl!("rename-tab"),
KeyBindAction::ToggleFullscreen => fl!("toggle-fullscreen"),
KeyBindAction::WindowClose => fl!("close-window"),
KeyBindAction::WindowNew => fl!("new-window"),
@ -320,6 +323,7 @@ pub fn shortcut_groups() -> Vec<ShortcutGroup> {
actions: vec![
KeyBindAction::TabNew,
KeyBindAction::TabClose,
KeyBindAction::TabRename,
KeyBindAction::TabNext,
KeyBindAction::TabPrev,
KeyBindAction::TabActivate0,
@ -454,6 +458,7 @@ fn fallback_shortcuts() -> Shortcuts {
bind!([Ctrl, Shift], "V", Paste);
bind!([Shift], "Insert", PastePrimary);
bind!([Ctrl, Shift], "W", TabClose);
bind!([Ctrl, Shift], "R", TabRename);
bind!([Ctrl], ",", Settings);
bind!([], "F11", ToggleFullscreen);
@ -505,9 +510,26 @@ fn fallback_shortcuts() -> Shortcuts {
fn key_from_string(value: &str) -> Option<Key> {
match value {
"Backspace" => Some(Key::Named(Named::Backspace)),
"Enter" => Some(Key::Named(Named::Enter)),
"Escape" => Some(Key::Named(Named::Escape)),
"Insert" => Some(Key::Named(Named::Insert)),
"Delete" => Some(Key::Named(Named::Delete)),
"Tab" => Some(Key::Named(Named::Tab)),
"F1" => Some(Key::Named(Named::F1)),
"F2" => Some(Key::Named(Named::F2)),
"F3" => Some(Key::Named(Named::F3)),
"F4" => Some(Key::Named(Named::F4)),
"F5" => Some(Key::Named(Named::F5)),
"F6" => Some(Key::Named(Named::F6)),
"F7" => Some(Key::Named(Named::F7)),
"F8" => Some(Key::Named(Named::F8)),
"F9" => Some(Key::Named(Named::F9)),
"F10" => Some(Key::Named(Named::F10)),
"F11" => Some(Key::Named(Named::F11)),
"F12" => Some(Key::Named(Named::F12)),
"Home" => Some(Key::Named(Named::Home)),
"End" => Some(Key::Named(Named::End)),
"ArrowLeft" | "Left" => Some(Key::Named(Named::ArrowLeft)),
"ArrowRight" | "Right" => Some(Key::Named(Named::ArrowRight)),
"ArrowUp" | "Up" => Some(Key::Named(Named::ArrowUp)),

View file

@ -21,8 +21,7 @@ use cosmic::{
widget::{pane_grid, segmented_button},
};
use cosmic_text::{
Attrs, AttrsList, Buffer, BufferLine, CacheKeyFlags, Family, LineEnding, Metrics, Shaping,
Weight, Wrap,
Attrs, AttrsList, Buffer, BufferLine, CacheKeyFlags, Family, LineEnding, Shaping, Weight, Wrap,
};
use indexmap::IndexSet;
use std::{
@ -191,21 +190,22 @@ impl TerminalPaneGrid {
let entity = tab_model.active();
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
let mut terminal = terminal.lock().unwrap();
terminal.is_focused = self.focus == *pane;
terminal.set_focused(self.focus == *pane);
terminal.update();
}
}
}
pub fn unfocus_all_terminals(&self) {
for (_pane, tab_model) in self.panes.panes.iter() {
let entity = tab_model.active();
for entity in tab_model.iter() {
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
let mut terminal = terminal.lock().unwrap();
terminal.is_focused = false;
terminal.set_focused(false);
terminal.update();
}
}
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
@ -284,7 +284,7 @@ impl Terminal {
let bold_font_weight = app_config.bold_font_weight;
let use_bright_bold = app_config.use_bright_bold;
let metrics = Metrics::new(14.0, 21.0);
let metrics = app_config.metrics(0);
let default_bg = convert_color(&colors, Color::Named(NamedColor::Background));
let default_fg = convert_color(&colors, Color::Named(NamedColor::Foreground));
@ -387,6 +387,19 @@ impl Terminal {
&self.colors
}
pub fn effective_color(&self, index: usize) -> Rgb {
if index == NamedColor::Background as usize {
self.colors[index].unwrap_or_else(|| {
// Allow using an unset background
let [r, g, b, _] =
cosmic_text::Color(WINDOW_BG_COLOR.load(Ordering::SeqCst)).as_rgba();
Rgb { r, g, b }
})
} else {
self.colors[index].unwrap_or_default()
}
}
pub fn default_attrs(&self) -> &Attrs<'static> {
&self.default_attrs
}
@ -403,6 +416,22 @@ impl Terminal {
self.zoom_adj = value;
}
fn set_focused(&mut self, is_focused: bool) {
let focus_changed = self.is_focused != is_focused;
self.is_focused = is_focused;
if focus_changed {
let report_focus = self.term.lock().mode().contains(TermMode::FOCUS_IN_OUT);
if report_focus {
const FOCUS_IN: &[u8] = b"\x1b[I";
const FOCUS_OUT: &[u8] = b"\x1b[O";
let input = if is_focused { FOCUS_IN } else { FOCUS_OUT };
self.input_no_scroll(input);
}
}
}
pub fn redraw(&self) -> bool {
self.buffer.redraw()
}
@ -614,6 +643,7 @@ impl Terminal {
pub fn set_config(
&mut self,
config: &AppConfig,
color_scheme_kind: ColorSchemeKind,
themes: &HashMap<(String, ColorSchemeKind), Colors>,
) {
let mut update_cell_size = false;
@ -640,7 +670,7 @@ impl Terminal {
update_cell_size = true;
}
if self.bold_font_weight.0 != config.font_weight {
if self.bold_font_weight.0 != config.bold_font_weight {
self.bold_font_weight = Weight(config.bold_font_weight);
update_cell_size = true;
}
@ -656,7 +686,9 @@ impl Terminal {
update_cell_size = true;
}
if let Some(colors) = themes.get(&config.syntax_theme(self.profile_id_opt)) {
if let Some(colors) =
themes.get(&config.syntax_theme(color_scheme_kind, self.profile_id_opt))
{
let mut changed = false;
for i in 0..color::COUNT {
if self.colors[i] != colors[i] {
@ -1025,10 +1057,9 @@ impl Terminal {
x: u32,
y: u32,
) {
let term_lock = self.term.lock();
let mode = term_lock.mode();
let is_sgr = self.term.lock().mode().contains(TermMode::SGR_MOUSE);
if mode.contains(TermMode::SGR_MOUSE) {
if is_sgr {
let codes = self.mouse_reporter.sgr_mouse_wheel_scroll(
self.size().cell_width,
self.size().cell_height,
@ -1042,12 +1073,29 @@ impl Terminal {
self.notifier.notify(code);
}
} else {
MouseReporter::report_mouse_wheel_as_arrows(
self,
self.size().cell_width,
self.size().cell_height,
delta,
);
self.scroll_as_arrows(delta);
}
}
pub fn scroll_as_arrows(&mut self, delta: ScrollDelta) {
let cell_width = self.size().cell_width;
let cell_height = self.size().cell_height;
let (_, lines_y) = self
.mouse_reporter
.accumulate_scroll(delta, cell_width, cell_height);
let is_app_cursor = self.term.lock().mode().contains(TermMode::APP_CURSOR);
let (up, down) = if is_app_cursor {
(&b"\x1BOA"[..], &b"\x1BOB"[..])
} else {
(&b"\x1B[A"[..], &b"\x1B[B"[..])
};
const SCROLL_SPEED: u32 = 3;
for _ in 0..(lines_y.unsigned_abs() * SCROLL_SPEED) {
if lines_y > 0 {
self.input_no_scroll(up)
} else if lines_y < 0 {
self.input_no_scroll(down)
}
}
}
}

View file

@ -45,10 +45,7 @@ use std::{
time::{Duration, Instant},
};
use crate::{
Action, Terminal, TerminalScroll, menu::MenuState, mouse_reporter::MouseReporter,
terminal::Metadata,
};
use crate::{Action, Terminal, TerminalScroll, menu::MenuState, terminal::Metadata};
const AUTOSCROLL_INTERVAL: Duration = Duration::from_millis(100);
@ -759,7 +756,7 @@ where
let scrollbar_color: Color = if pressed {
// pressed_state_color, 0.5
cosmic_theme
.background
.background(false)
.component
.pressed
.without_alpha()
@ -769,7 +766,7 @@ where
} else if hover {
// hover_state_color, 0.2
cosmic_theme
.background
.background(false)
.component
.hover
.without_alpha()
@ -936,13 +933,14 @@ where
},
Event::Keyboard(KeyEvent::KeyPressed {
key: Key::Named(named),
physical_key,
modified_key: Key::Named(modified_named),
modifiers,
text,
..
}) if state.is_focused && named == modified_named => {
for key_bind in self.key_binds.keys() {
if key_bind.matches(*modifiers, &Key::Named(*named)) {
if key_bind.matches(*modifiers, &Key::Named(*named), Some(physical_key)) {
shell.capture_event();
return;
}
@ -1102,6 +1100,7 @@ where
text,
modifiers,
key,
physical_key,
..
}) if state.is_focused && *key == Key::Character(SmolStr::new(" ")) => {
//Special handle Enter, Escape, Backspace and Tab as described in
@ -1127,10 +1126,12 @@ where
text,
modifiers,
key,
physical_key,
modified_key,
..
}) if state.is_focused => {
for key_bind in self.key_binds.keys() {
if key_bind.matches(*modifiers, key) {
if key_bind.matches(*modifiers, key, Some(physical_key)) {
shell.capture_event();
return;
@ -1173,11 +1174,10 @@ where
}
}
(false, true, _, true) => {
//This is normally Ctrl+Minus, but since that
//is taken by zoom, we send that code for
//Ctrl+Underline instead, like xterm and
//gnome-terminal
if *key == Key::Character("_".into()) {
// Ctrl+Shift+<key>: send 0x1F (C-_) on '_', matching xterm/gnome-terminal.
// Use `modified_key` so this works regardless of which physical key
// produces '_' on the user's layout.
if *modified_key == Key::Character("_".into()) {
terminal.input_scroll(b"\x1F".as_slice());
shell.capture_event();
}
@ -1548,26 +1548,27 @@ where
let row = y / terminal.size().cell_height;
terminal.scroll_mouse(*delta, &state.modifiers, col as u32, row as u32);
} else if terminal.term.lock().mode().contains(TermMode::ALT_SCREEN) {
MouseReporter::report_mouse_wheel_as_arrows(
&terminal,
terminal.size().cell_width,
terminal.size().cell_height,
*delta,
);
terminal.scroll_as_arrows(*delta);
shell.capture_event();
} else {
match delta {
ScrollDelta::Lines { x: _, y } => {
//TODO: this adjustment is just a guess!
// High-resolution wheels deliver a notch as many
// fractional deltas; accumulate them so nothing
// below one whole line is lost (see
// accumulate_wheel_lines).
state.scroll_pixels = 0.0;
let lines = (-y * 6.0) as i32;
let (lines, remainder) =
accumulate_wheel_lines(*y, state.scroll_lines);
state.scroll_lines = remainder;
if lines != 0 {
terminal.scroll(TerminalScroll::Delta(-lines));
terminal.scroll(TerminalScroll::Delta(lines));
}
shell.capture_event();
}
ScrollDelta::Pixels { x: _, y } => {
//TODO: this adjustment is just a guess!
state.scroll_lines = 0.0;
state.scroll_pixels -= y * 6.0;
let mut lines = 0;
let metrics = terminal.with_buffer(|buffer| buffer.metrics());
@ -1733,6 +1734,26 @@ fn update_active_regex_match(
}
}
/// Whole lines scrolled per unit of wheel `Lines` delta.
//TODO: this adjustment is just a guess!
const SCROLL_LINE_MULTIPLIER: f32 = 6.0;
/// Convert a wheel `Lines` delta into whole lines to scroll, accumulating the
/// fractional remainder across events.
///
/// High-resolution wheels deliver a single physical notch as many fractional
/// deltas (e.g. `y = 0.125`). Scaling by [`SCROLL_LINE_MULTIPLIER`] and
/// truncating per event discards anything below one whole line, so most of
/// those events would scroll nothing. Carrying the remainder forward means the
/// fractions add up and a full notch scrolls the intended amount.
///
/// Returns `(whole_lines, new_accumulator)`.
fn accumulate_wheel_lines(y: f32, accumulator: f32) -> (i32, f32) {
let total = accumulator + y * SCROLL_LINE_MULTIPLIER;
let lines = total.trunc() as i32;
(lines, total - lines as f32)
}
fn shade(color: cosmic_text::Color, is_focused: bool) -> cosmic_text::Color {
if is_focused {
color
@ -1934,6 +1955,7 @@ pub struct State {
dragging: Option<Dragging>,
is_focused: bool,
scroll_pixels: f32,
scroll_lines: f32,
scrollbar_rect: Cell<Rectangle<f32>>,
autoscroll: DragAutoscroll,
preedit: Option<input_method::Preedit>,
@ -1948,6 +1970,7 @@ impl State {
dragging: None,
is_focused: false,
scroll_pixels: 0.0,
scroll_lines: 0.0,
scrollbar_rect: Cell::new(Rectangle::default()),
autoscroll: DragAutoscroll::new(AUTOSCROLL_INTERVAL),
preedit: None,
@ -2027,7 +2050,38 @@ fn ss3(code: &str, modifiers: u8) -> Option<Vec<u8>> {
#[cfg(test)]
mod tests {
use super::{EdgeScrollDirection, edge_scroll_adjustment};
use super::{EdgeScrollDirection, accumulate_wheel_lines, edge_scroll_adjustment};
#[test]
fn wheel_lines_single_fractional_event_keeps_remainder() {
// A high-res wheel notch arrives as fractional deltas. A lone 0.125
// event is less than one whole line, but must not be discarded.
let (lines, remainder) = accumulate_wheel_lines(0.125, 0.0);
assert_eq!(lines, 0);
assert!((remainder - 0.75).abs() < f32::EPSILON);
}
#[test]
fn wheel_lines_accumulate_into_whole_lines() {
// Eight 0.125 deltas make up one physical notch and must scroll the
// full SCROLL_LINE_MULTIPLIER (6) lines, not be lost to truncation.
let mut accumulator = 0.0;
let mut total = 0;
for _ in 0..8 {
let (lines, remainder) = accumulate_wheel_lines(0.125, accumulator);
accumulator = remainder;
total += lines;
}
assert_eq!(total, 6);
}
#[test]
fn wheel_lines_preserve_direction() {
let (down, _) = accumulate_wheel_lines(0.25, 0.0);
let (up, _) = accumulate_wheel_lines(-0.25, 0.0);
assert_eq!(down, 1);
assert_eq!(up, -1);
}
const BUFFER_HEIGHT: f32 = 200.0;
const CELL_HEIGHT: f32 = 20.0;