Merge branch 'master' into primary
This commit is contained in:
commit
5b2bbe84cb
21 changed files with 2064 additions and 1382 deletions
1291
Cargo.lock
generated
1291
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -11,8 +11,10 @@ alacritty_terminal = "0.20"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
indexmap = "2"
|
indexmap = "2"
|
||||||
|
lexical-sort = "0.3.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
serde = { version = "1", features = ["serde_derive"] }
|
||||||
|
shlex = "1"
|
||||||
tokio = { version = "1", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
# Internationalization
|
# Internationalization
|
||||||
i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester"] }
|
i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester"] }
|
||||||
|
|
@ -21,8 +23,13 @@ rust-embed = "6"
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
palette = "0.7"
|
palette = "0.7"
|
||||||
|
|
||||||
|
[dependencies.smol_str]
|
||||||
|
version = "0.2.1"
|
||||||
|
features = ["serde"]
|
||||||
|
|
||||||
[dependencies.cosmic-text]
|
[dependencies.cosmic-text]
|
||||||
git = "https://github.com/pop-os/cosmic-text.git"
|
git = "https://github.com/pop-os/cosmic-text.git"
|
||||||
|
features = ["shape-run-cache"]
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic]
|
||||||
git = "https://github.com/pop-os/libcosmic.git"
|
git = "https://github.com/pop-os/libcosmic.git"
|
||||||
|
|
@ -36,7 +43,6 @@ wgpu = ["libcosmic/wgpu"]
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# https://github.com/rust-lang/libc/pull/3512
|
# https://github.com/rust-lang/libc/pull/3512
|
||||||
libc = { git = "https://gitlab.redox-os.org/redox-os/liblibc.git", branch = "redox_0.2.151" }
|
libc = { git = "https://gitlab.redox-os.org/redox-os/liblibc.git", branch = "redox_0.2.151" }
|
||||||
smithay-client-toolkit = { git = "https://github.com/pop-os/client-toolkit", branch = "wayland-resize" }
|
|
||||||
|
|
||||||
[profile.release-with-debug]
|
[profile.release-with-debug]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|
|
||||||
1
debian/control
vendored
1
debian/control
vendored
|
|
@ -13,4 +13,5 @@ Homepage: https://github.com/pop-os/cosmic-term
|
||||||
Package: cosmic-term
|
Package: cosmic-term
|
||||||
Architecture: amd64 arm64
|
Architecture: amd64 arm64
|
||||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||||
|
Provides: x-terminal-emulator
|
||||||
Description: Cosmic Terminal
|
Description: Cosmic Terminal
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
|
new-terminal = New terminal
|
||||||
|
|
||||||
# Context Pages
|
# Context Pages
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
profiles = Profiles
|
||||||
|
name = Name
|
||||||
|
command-line = Command line
|
||||||
|
tab-title = Tab title
|
||||||
|
tab-title-description = Override the default tab title
|
||||||
|
add-profile = Add profile
|
||||||
|
new-profile = New profile
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
settings = Settings
|
settings = Settings
|
||||||
|
|
||||||
|
|
@ -12,6 +23,7 @@ light = Light
|
||||||
syntax-dark = Color scheme dark
|
syntax-dark = Color scheme dark
|
||||||
syntax-light = Color scheme light
|
syntax-light = Color scheme light
|
||||||
default-zoom-step = Zoom steps
|
default-zoom-step = Zoom steps
|
||||||
|
opacity = Background opacity
|
||||||
|
|
||||||
### Font
|
### Font
|
||||||
font = Font
|
font = Font
|
||||||
|
|
@ -44,6 +56,8 @@ find-next = Find next
|
||||||
file = File
|
file = File
|
||||||
new-tab = New tab
|
new-tab = New tab
|
||||||
new-window = New window
|
new-window = New window
|
||||||
|
profile = Profile
|
||||||
|
menu-profiles = Profiles...
|
||||||
close-tab = Close tab
|
close-tab = Close tab
|
||||||
quit = Quit
|
quit = Quit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,47 @@
|
||||||
# Context Pages
|
# Context Pages
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
profiles = プロファイル
|
||||||
|
name = 名前
|
||||||
|
command-line = コマンドライン
|
||||||
|
tab-title = タブのイトル
|
||||||
|
tab-title-description = デフォルトのタブタイトルを無効にします
|
||||||
|
add-profile = プロファイルを追加
|
||||||
|
new-profile = 新しいプロファイル
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
settings = 設定
|
settings = 設定
|
||||||
|
|
||||||
### Appearance
|
### Appearance
|
||||||
appearance = 外観
|
appearance = 外観
|
||||||
theme = テーマ
|
theme = テーマ
|
||||||
match-desktop = デスクトップに合わす
|
match-desktop = システム設定に従う
|
||||||
dark = 暗い
|
dark = ダーク
|
||||||
light = 明かり
|
light = ライト
|
||||||
syntax-dark = 暗いシンタックス
|
syntax-dark = ダークシンタックスハイライト
|
||||||
syntax-light = 明かりシンタックス
|
syntax-light = ライトシンタックスハイライト
|
||||||
advanced-font-settings = 詳細なフォント設定
|
default-zoom-step = 拡大縮小の間隔
|
||||||
default-font = デフォルトフォント
|
opacity = 不透明度
|
||||||
default-font-stretch = デフォルトのフォント幅
|
|
||||||
default-font-weight = デフォルトのフォントの太さ
|
### Font
|
||||||
default-dim-font-weight = デフォルトの暗いフォントの太さ
|
font = フォント
|
||||||
default-bold-font-weight = デフォルトの太字の太さ
|
advanced-font-settings = フォントの詳細設定
|
||||||
|
default-font = フォント名
|
||||||
|
default-font-size = サイズ
|
||||||
|
default-font-stretch = 幅
|
||||||
|
default-font-weight = スタイル
|
||||||
|
default-dim-font-weight = 細字のスタイル
|
||||||
|
default-bold-font-weight = 太字のスタイル
|
||||||
use-bright-bold = 太字を明るい色で表示する
|
use-bright-bold = 太字を明るい色で表示する
|
||||||
default-font-size = デフォルトのフォントサイズ
|
|
||||||
default-zoom-step =ズームの間隔
|
### Splits
|
||||||
|
splits = ウィンドウの分割
|
||||||
|
focus-follow-mouse = フォーカスをマウスに追従
|
||||||
|
|
||||||
|
### Advanced
|
||||||
|
advanced = 高度な設定
|
||||||
|
show-headerbar = ヘッダーを表示する
|
||||||
|
show-header-description = 右クリックでヘッダーを表示する
|
||||||
|
|
||||||
# Find
|
# Find
|
||||||
find-placeholder = 検索...
|
find-placeholder = 検索...
|
||||||
|
|
@ -32,6 +54,8 @@ find-next = 次を検索
|
||||||
file = ファイル
|
file = ファイル
|
||||||
new-tab = 新しいタブ
|
new-tab = 新しいタブ
|
||||||
new-window = 新しいウィンドウ
|
new-window = 新しいウィンドウ
|
||||||
|
profile = プロファイル
|
||||||
|
menu-profiles = プロファイル...
|
||||||
close-tab = タブを閉じる
|
close-tab = タブを閉じる
|
||||||
quit = 終了
|
quit = 終了
|
||||||
|
|
||||||
|
|
@ -44,7 +68,12 @@ find = 検索
|
||||||
|
|
||||||
## View
|
## View
|
||||||
view = 表示
|
view = 表示
|
||||||
|
zoom-in = 拡大
|
||||||
|
zoom-reset = デフォルトに戻す
|
||||||
|
zoom-out = 縮小
|
||||||
|
next-tab = 次のタブ
|
||||||
|
previous-tab = 前のタブ
|
||||||
|
split-horizontal = 上下に分割
|
||||||
|
split-vertical = 左右に分割
|
||||||
|
pane-toggle-maximize = ペインの最大化を切替
|
||||||
menu-settings = 設定...
|
menu-settings = 設定...
|
||||||
|
|
||||||
# Context menu
|
|
||||||
show-headerbar = ヘッダーバーを表す
|
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,32 @@ dark = Ciemny
|
||||||
light = Jasny
|
light = Jasny
|
||||||
syntax-dark = Ciemna składnia
|
syntax-dark = Ciemna składnia
|
||||||
syntax-light = Jasna składnia
|
syntax-light = Jasna składnia
|
||||||
advanced-font-settings = Advanced Font Settings
|
default-zoom-step = Domyślny poziom przybliżenia
|
||||||
|
|
||||||
|
### Font
|
||||||
|
font = Czcionka
|
||||||
|
advanced-font-settings = Zaawansowane Ustawienia Czcionek
|
||||||
default-font = Domyślna czcionka
|
default-font = Domyślna czcionka
|
||||||
|
default-font-size = Domyślny rozmiar czcionki
|
||||||
default-font-stretch = Domyślne rozciągnięcie czcionki
|
default-font-stretch = Domyślne rozciągnięcie czcionki
|
||||||
default-font-weight = Domyślna grubość czcionki
|
default-font-weight = Domyślna grubość czcionki
|
||||||
default-dim-font-weight = Domyślna grubość przyciemnionej czcionki
|
default-dim-font-weight = Domyślna grubość przyciemnionej czcionki
|
||||||
default-bold-font-weight = Domyślna grubość pogrubionej czcionki
|
default-bold-font-weight = Domyślna grubość pogrubionej czcionki
|
||||||
use-bright-bold = Użyj jasnych kolorów przy pogrubionym tekście
|
use-bright-bold = Rozjaśnij pogrubiony tekst
|
||||||
default-font-size = Domyślny rozmiar czcionki
|
|
||||||
default-zoom-step = Domyślny poziom przybliżenia
|
### Splits
|
||||||
|
splits = Podziel
|
||||||
|
focus-follow-mouse = Aktywne okno pisania odpowiada położeniu myszki
|
||||||
|
|
||||||
|
### Advanced
|
||||||
|
advanced = Zaawansowane
|
||||||
|
show-headerbar = Pokaż pasek nagłówka
|
||||||
|
show-header-description = Ukazuje pasek nagłówka z menu wybieranego prawym przyciskiem myszki.
|
||||||
|
|
||||||
# Find
|
# Find
|
||||||
find-placeholder = Find...
|
find-placeholder = Szukaj...
|
||||||
find-previous = Find previous
|
find-previous = Szukaj poprzedni
|
||||||
find-next = Find next
|
find-next = Szukaj następny
|
||||||
|
|
||||||
# Menu
|
# Menu
|
||||||
|
|
||||||
|
|
@ -44,7 +56,12 @@ find = Szukaj
|
||||||
|
|
||||||
## View
|
## View
|
||||||
view = Widok
|
view = Widok
|
||||||
|
zoom-in = Większy tekst
|
||||||
|
zoom-reset = Domyślny rozmiar tekstu
|
||||||
|
zoom-out = Mniejszy tekst
|
||||||
|
next-tab = Natępna karta
|
||||||
|
previous-tab = Poprzednia karta
|
||||||
|
split-horizontal = Podziel w poziomie
|
||||||
|
split-vertical = Podziel w pionie
|
||||||
|
pane-toggle-maximize = Przełącznik maksymalizacji
|
||||||
menu-settings = Ustawienia...
|
menu-settings = Ustawienia...
|
||||||
|
|
||||||
# Context menu
|
|
||||||
show-headerbar = Pokaż pasek nagłówka
|
|
||||||
|
|
|
||||||
|
|
@ -9,17 +9,29 @@ theme = Тема
|
||||||
match-desktop = Как в системе
|
match-desktop = Как в системе
|
||||||
dark = Темная
|
dark = Темная
|
||||||
light = Светлая
|
light = Светлая
|
||||||
syntax-dark = Синтаксис темный
|
syntax-dark = Цветовая схема темная
|
||||||
syntax-light = Синтаксис светлый
|
syntax-light = Цветовая схема светлая
|
||||||
|
default-zoom-step = Шаги масштабирования
|
||||||
|
|
||||||
|
### Font
|
||||||
|
font = Шрифт
|
||||||
advanced-font-settings = Дополнительные настройки шрифта
|
advanced-font-settings = Дополнительные настройки шрифта
|
||||||
default-font = Шрифт по умолчанию
|
default-font = Шрифт по умолчанию
|
||||||
default-font-stretch = Растяжение шрифта по умолчанию
|
default-font-size = Размер шрифта
|
||||||
default-font-weight = Масса шрифта по умолчанию
|
default-font-stretch = Растяжение шрифта
|
||||||
default-dim-font-weight = Масса dim шрифта по умолчанию
|
default-font-weight = Масса шрифта
|
||||||
default-bold-font-weight = Масса жирного шрифта по умолчанию
|
default-dim-font-weight = Масса dim шрифта
|
||||||
use-bright-bold = Использовать яркие цвета и жирный текст
|
default-bold-font-weight = Масса жирного шрифта
|
||||||
default-font-size = Размер шрифта по умолчанию
|
use-bright-bold = Сделать жирный текст ярче
|
||||||
default-zoom-step = Шаг масштабирования по умолчанию
|
|
||||||
|
### Splits
|
||||||
|
splits = Деления
|
||||||
|
focus-follow-mouse = Фокус при наборе следует за мышью
|
||||||
|
|
||||||
|
### Advanced
|
||||||
|
advanced = Дополнительно
|
||||||
|
show-headerbar = Отображать заголовок
|
||||||
|
show-header-description = Раскройте заголовок из меню правой кнопки мыши.
|
||||||
|
|
||||||
# Find
|
# Find
|
||||||
find-placeholder = Найти...
|
find-placeholder = Найти...
|
||||||
|
|
@ -44,7 +56,12 @@ find = Найти
|
||||||
|
|
||||||
## View
|
## View
|
||||||
view = Вид
|
view = Вид
|
||||||
|
zoom-in = Текст крупнее
|
||||||
|
zoom-reset = Размер текста по умолчанию
|
||||||
|
zoom-out = Текст меньше
|
||||||
|
next-tab = Следующая вкладка
|
||||||
|
previous-tab = Предыдущая вкладка
|
||||||
|
split-horizontal = Разделение по горизонтали
|
||||||
|
split-vertical = Разделение по вертикали
|
||||||
|
pane-toggle-maximize = Переключить на весь экран
|
||||||
menu-settings = Параметры...
|
menu-settings = Параметры...
|
||||||
|
|
||||||
# Context menu
|
|
||||||
show-headerbar = Отображать заголовок
|
|
||||||
67
i18n/sv-SE/cosmic_term.ftl
Normal file
67
i18n/sv-SE/cosmic_term.ftl
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Context sidor
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
settings = Inställningar
|
||||||
|
|
||||||
|
### Appearance
|
||||||
|
appearance = Utseende
|
||||||
|
theme = Tema
|
||||||
|
match-desktop = Matcha skrivbordet
|
||||||
|
dark = Mörkt
|
||||||
|
light = Ljust
|
||||||
|
syntax-dark = Färgschema mörkt
|
||||||
|
syntax-light = Färgschema ljust
|
||||||
|
default-zoom-step = Zoom steg
|
||||||
|
|
||||||
|
### Teckensnitt
|
||||||
|
font = Teckensnitt
|
||||||
|
advanced-font-settings = Avancerade teckensnittsinställningar
|
||||||
|
default-font = Teckensnitt
|
||||||
|
default-font-size = Teckenstorlek
|
||||||
|
default-font-stretch = Teckenstretch
|
||||||
|
default-font-weight = Normal teckensnittsvikt
|
||||||
|
default-dim-font-weight = Dämpad teckensnittsvikt
|
||||||
|
default-bold-font-weight = Fet teckensnittsvikt
|
||||||
|
use-bright-bold = Gör fet text ljusare
|
||||||
|
|
||||||
|
### Delar
|
||||||
|
splits = Delar
|
||||||
|
focus-follow-mouse = Skrivfokus följer mus
|
||||||
|
|
||||||
|
### Avancerat
|
||||||
|
advanced = Avancerat
|
||||||
|
show-headerbar = Visa rubrikrad
|
||||||
|
show-header-description = Visa rubrikrad från högerklicksmenyn.
|
||||||
|
|
||||||
|
# Sök
|
||||||
|
find-placeholder = Sök…
|
||||||
|
find-previous = Hitta föregående
|
||||||
|
find-next = Hitta nästa
|
||||||
|
|
||||||
|
# Meny
|
||||||
|
|
||||||
|
## Fil
|
||||||
|
file = Fil
|
||||||
|
new-tab = Ny flik
|
||||||
|
new-window = Nytt fönster
|
||||||
|
close-tab = Stäng flik
|
||||||
|
quit = Avsluta
|
||||||
|
|
||||||
|
## Redigera
|
||||||
|
edit = Redigera
|
||||||
|
copy = Kopiera
|
||||||
|
paste = Klistra in
|
||||||
|
select-all = Välj alla
|
||||||
|
find = Sök
|
||||||
|
|
||||||
|
## Visa
|
||||||
|
view = Visa
|
||||||
|
zoom-in = Zooma in
|
||||||
|
zoom-reset = Återställ zoom
|
||||||
|
zoom-out = Zooma ut
|
||||||
|
next-tab = Nästa flik
|
||||||
|
previous-tab = Föregående flik
|
||||||
|
split-horizontal = Dela horisontellt
|
||||||
|
split-vertical = Dela vertikalt
|
||||||
|
pane-toggle-maximize = Växla maximerad
|
||||||
|
menu-settings = Inställningar…
|
||||||
78
i18n/tr/cosmic-term.ftl
Normal file
78
i18n/tr/cosmic-term.ftl
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Context Pages
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
profiles = Profiller
|
||||||
|
name = İsim
|
||||||
|
command-line = Komut satırı
|
||||||
|
tab-title = Sekme başlığı
|
||||||
|
tab-title-description = Varsayılan sekme başlığını geçersiz kılar
|
||||||
|
add-profile = Profil ekle
|
||||||
|
new-profile = Yeni profil
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
settings = Ayarlar
|
||||||
|
|
||||||
|
### Appearance
|
||||||
|
appearance = Görünüm
|
||||||
|
theme = Tema
|
||||||
|
match-desktop = Masaüstüyle eşle
|
||||||
|
dark = Karanlık
|
||||||
|
light = Aydınlık
|
||||||
|
syntax-dark = Karanlık renk şeması
|
||||||
|
syntax-light = Aydınlık renk şeması
|
||||||
|
default-zoom-step = Yakınlaştırma basamakları
|
||||||
|
|
||||||
|
### Font
|
||||||
|
font = Yazı tipi
|
||||||
|
advanced-font-settings = Gelişmiş Yazı tipi Seçenekleri
|
||||||
|
default-font = Yazı tipi
|
||||||
|
default-font-size = Yazı tipi boyutu
|
||||||
|
default-font-stretch = Yazı tipi esnekliği
|
||||||
|
default-font-weight = Normal yazı tipi ağırlığı
|
||||||
|
default-dim-font-weight = Soluk yazı tipi ağırlığı
|
||||||
|
default-bold-font-weight = Kalın yazı tipi ağırlığı
|
||||||
|
use-bright-bold = Kalın metni daha parlak yap
|
||||||
|
|
||||||
|
### Splits
|
||||||
|
splits = Bölmeler
|
||||||
|
focus-follow-mouse = Yazma odağı fareyi takip etsin
|
||||||
|
|
||||||
|
### Advanced
|
||||||
|
advanced = Gelişmiş
|
||||||
|
show-headerbar = Başlığı göster
|
||||||
|
show-header-description = Sağ tıklama menüsünden başlığı gösterin.
|
||||||
|
|
||||||
|
# Find
|
||||||
|
find-placeholder = Bul...
|
||||||
|
find-previous = Öncekini bul
|
||||||
|
find-next = Sonrakini bul
|
||||||
|
|
||||||
|
# Menu
|
||||||
|
|
||||||
|
## File
|
||||||
|
file = Dosyal
|
||||||
|
new-tab = Yeni sekme
|
||||||
|
new-window = Yeni pencere
|
||||||
|
profile = Profil
|
||||||
|
menu-profiles = Profiller...
|
||||||
|
close-tab = Sekmeyi kapat
|
||||||
|
quit = Çık
|
||||||
|
|
||||||
|
## Edit
|
||||||
|
edit = Düzenle
|
||||||
|
copy = Kopyala
|
||||||
|
paste = Yapıştır
|
||||||
|
select-all = Hepsini seç
|
||||||
|
find = Bul
|
||||||
|
|
||||||
|
## View
|
||||||
|
view = Görünüş
|
||||||
|
zoom-in = Daha büyük metin
|
||||||
|
zoom-reset = Varsayılan metin boyutu
|
||||||
|
zoom-out = Daha küçük metin
|
||||||
|
next-tab = Sonraki sekme
|
||||||
|
previous-tab = Önceki sekme
|
||||||
|
split-horizontal = Yatay böl
|
||||||
|
split-vertical = Dikey böl
|
||||||
|
pane-toggle-maximize = En üste geç
|
||||||
|
menu-settings = Ayarlar...
|
||||||
6
justfile
6
justfile
|
|
@ -46,9 +46,13 @@ check *args:
|
||||||
# Runs a clippy check with JSON message format
|
# Runs a clippy check with JSON message format
|
||||||
check-json: (check '--message-format=json')
|
check-json: (check '--message-format=json')
|
||||||
|
|
||||||
|
dev *args:
|
||||||
|
cargo fmt
|
||||||
|
just run {{args}}
|
||||||
|
|
||||||
# Run with debug logs
|
# Run with debug logs
|
||||||
run *args:
|
run *args:
|
||||||
env RUST_LOG=debug RUST_BACKTRACE=full cargo run --release {{args}}
|
env RUST_LOG=cosmic_term=debug RUST_BACKTRACE=full cargo run --release {{args}}
|
||||||
|
|
||||||
# Installs files
|
# Installs files
|
||||||
install:
|
install:
|
||||||
|
|
|
||||||
4
res/icons/edit-delete-symbolic.svg
Normal file
4
res/icons/edit-delete-symbolic.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2 4.98999V13.99C2 14.514 2.476 14.99 3 14.99H12C12.524 14.99 13 14.514 13 13.99V4.98999H2Z" fill="#232323"/>
|
||||||
|
<path d="M1 2.99001V3.99001L14 3.98701V2.99001C14 1.99001 13 1.98701 13 1.98701H10C10 1.98701 10 0.987 9 0.987H6C5 0.987 5 1.98701 5 1.98701H2C2 1.98701 1 1.99001 1 2.99001Z" fill="#232323"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 414 B |
3
res/icons/list-add-symbolic.svg
Normal file
3
res/icons/list-add-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8 3C7.73478 3 7.48043 3.10536 7.29289 3.29289C7.10536 3.48043 7 3.73478 7 4V6.996L4 7C3.73478 7 3.48043 7.10536 3.29289 7.29289C3.10536 7.48043 3 7.73478 3 8C3 8.26522 3.10536 8.51957 3.29289 8.70711C3.48043 8.89464 3.73478 9 4 9L7 8.996V12C7 12.2652 7.10536 12.5196 7.29289 12.7071C7.48043 12.8946 7.73478 13 8 13C8.26522 13 8.51957 12.8946 8.70711 12.7071C8.89464 12.5196 9 12.2652 9 12V8.996L12 9C12.2652 9 12.5196 8.89464 12.7071 8.70711C12.8946 8.51957 13 8.26522 13 8C13 7.73478 12.8946 7.48043 12.7071 7.29289C12.5196 7.10536 12.2652 7 12 7L9 6.996V4C9 3.73478 8.89464 3.48043 8.70711 3.29289C8.51957 3.10536 8.26522 3 8 3Z" fill="#232323"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 762 B |
|
|
@ -10,6 +10,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use crate::fl;
|
||||||
|
|
||||||
pub const CONFIG_VERSION: u64 = 1;
|
pub const CONFIG_VERSION: u64 = 1;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
|
@ -29,6 +31,35 @@ impl AppTheme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct ProfileId(pub u64);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
pub struct Profile {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub command: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub syntax_theme_dark: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub syntax_theme_light: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tab_title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Profile {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: fl!("new-profile"),
|
||||||
|
command: String::new(),
|
||||||
|
syntax_theme_dark: "COSMIC Dark".to_string(),
|
||||||
|
syntax_theme_light: "COSMIC Light".to_string(),
|
||||||
|
tab_title: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub app_theme: AppTheme,
|
pub app_theme: AppTheme,
|
||||||
|
|
@ -39,6 +70,8 @@ pub struct Config {
|
||||||
pub bold_font_weight: u16,
|
pub bold_font_weight: u16,
|
||||||
pub font_stretch: u16,
|
pub font_stretch: u16,
|
||||||
pub font_size_zoom_step_mul_100: u16,
|
pub font_size_zoom_step_mul_100: u16,
|
||||||
|
pub opacity: u8,
|
||||||
|
pub profiles: BTreeMap<ProfileId, Profile>,
|
||||||
pub show_headerbar: bool,
|
pub show_headerbar: bool,
|
||||||
pub use_bright_bold: bool,
|
pub use_bright_bold: bool,
|
||||||
pub syntax_theme_dark: String,
|
pub syntax_theme_dark: String,
|
||||||
|
|
@ -50,18 +83,20 @@ impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
app_theme: AppTheme::System,
|
app_theme: AppTheme::System,
|
||||||
|
bold_font_weight: Weight::BOLD.0,
|
||||||
|
dim_font_weight: Weight::NORMAL.0,
|
||||||
|
focus_follow_mouse: false,
|
||||||
font_name: "Fira Mono".to_string(),
|
font_name: "Fira Mono".to_string(),
|
||||||
font_size: 14,
|
font_size: 14,
|
||||||
font_weight: Weight::NORMAL.0,
|
|
||||||
dim_font_weight: Weight::NORMAL.0,
|
|
||||||
bold_font_weight: Weight::BOLD.0,
|
|
||||||
font_stretch: Stretch::Normal.to_number(),
|
|
||||||
font_size_zoom_step_mul_100: 100,
|
font_size_zoom_step_mul_100: 100,
|
||||||
|
font_stretch: Stretch::Normal.to_number(),
|
||||||
|
font_weight: Weight::NORMAL.0,
|
||||||
|
opacity: 100,
|
||||||
|
profiles: BTreeMap::new(),
|
||||||
show_headerbar: true,
|
show_headerbar: true,
|
||||||
use_bright_bold: false,
|
|
||||||
syntax_theme_dark: "COSMIC Dark".to_string(),
|
syntax_theme_dark: "COSMIC Dark".to_string(),
|
||||||
syntax_theme_light: "COSMIC Light".to_string(),
|
syntax_theme_light: "COSMIC Light".to_string(),
|
||||||
focus_follow_mouse: false,
|
use_bright_bold: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,13 +116,46 @@ impl Config {
|
||||||
Metrics::new(font_size, line_height)
|
Metrics::new(font_size, line_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn opacity_ratio(&self) -> f32 {
|
||||||
|
(self.opacity as f32) / 100.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a sorted and adjusted for duplicates list of profiles names and ids
|
||||||
|
pub fn profile_names(&self) -> Vec<(String, ProfileId)> {
|
||||||
|
let mut profile_names = Vec::<(String, ProfileId)>::with_capacity(self.profiles.len());
|
||||||
|
for (profile_id, profile) in self.profiles.iter() {
|
||||||
|
let mut name = profile.name.clone();
|
||||||
|
|
||||||
|
let mut copies = 1;
|
||||||
|
while profile_names.iter().find(|x| x.0 == name).is_some() {
|
||||||
|
copies += 1;
|
||||||
|
name = format!("{} ({})", profile.name, copies);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_names.push((name, *profile_id));
|
||||||
|
}
|
||||||
|
profile_names.sort_by(|a, b| lexical_sort::natural_lexical_cmp(&a.0, &b.0));
|
||||||
|
profile_names
|
||||||
|
}
|
||||||
|
|
||||||
// Get current syntax theme based on dark mode
|
// Get current syntax theme based on dark mode
|
||||||
pub fn syntax_theme(&self) -> &str {
|
pub fn syntax_theme(&self, profile_id_opt: Option<ProfileId>) -> &str {
|
||||||
let dark = self.app_theme.theme().theme_type.is_dark();
|
let dark = self.app_theme.theme().theme_type.is_dark();
|
||||||
if dark {
|
match profile_id_opt.and_then(|profile_id| self.profiles.get(&profile_id)) {
|
||||||
&self.syntax_theme_dark
|
Some(profile) => {
|
||||||
} else {
|
if dark {
|
||||||
&self.syntax_theme_light
|
&profile.syntax_theme_dark
|
||||||
|
} else {
|
||||||
|
&profile.syntax_theme_light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if dark {
|
||||||
|
&self.syntax_theme_dark
|
||||||
|
} else {
|
||||||
|
&self.syntax_theme_light
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ impl IconCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
bundle!("edit-clear-symbolic", 16);
|
bundle!("edit-clear-symbolic", 16);
|
||||||
|
bundle!("edit-delete-symbolic", 16);
|
||||||
|
bundle!("list-add-symbolic", 16);
|
||||||
bundle!("go-down-symbolic", 16);
|
bundle!("go-down-symbolic", 16);
|
||||||
bundle!("go-up-symbolic", 16);
|
bundle!("go-up-symbolic", 16);
|
||||||
bundle!("window-close-symbolic", 16);
|
bundle!("window-close-symbolic", 16);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use cosmic::iced::keyboard::{KeyCode, Modifiers};
|
use cosmic::{
|
||||||
|
iced::keyboard::{Key, Modifiers},
|
||||||
|
iced_core::keyboard::key::Named,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
|
|
@ -15,12 +18,12 @@ pub enum Modifier {
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub struct KeyBind {
|
pub struct KeyBind {
|
||||||
pub modifiers: Vec<Modifier>,
|
pub modifiers: Vec<Modifier>,
|
||||||
pub key_code: KeyCode,
|
pub key: Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyBind {
|
impl KeyBind {
|
||||||
pub fn matches(&self, modifiers: Modifiers, key_code: KeyCode) -> bool {
|
pub fn matches(&self, modifiers: Modifiers, key: &Key) -> bool {
|
||||||
self.key_code == key_code
|
key == &self.key
|
||||||
&& modifiers.logo() == self.modifiers.contains(&Modifier::Super)
|
&& modifiers.logo() == self.modifiers.contains(&Modifier::Super)
|
||||||
&& modifiers.control() == self.modifiers.contains(&Modifier::Ctrl)
|
&& modifiers.control() == self.modifiers.contains(&Modifier::Ctrl)
|
||||||
&& modifiers.alt() == self.modifiers.contains(&Modifier::Alt)
|
&& modifiers.alt() == self.modifiers.contains(&Modifier::Alt)
|
||||||
|
|
@ -33,7 +36,11 @@ impl fmt::Display for KeyBind {
|
||||||
for modifier in self.modifiers.iter() {
|
for modifier in self.modifiers.iter() {
|
||||||
write!(f, "{:?} + ", modifier)?;
|
write!(f, "{:?} + ", modifier)?;
|
||||||
}
|
}
|
||||||
write!(f, "{:?}", self.key_code)
|
match &self.key {
|
||||||
|
Key::Character(c) => write!(f, "{}", c.to_uppercase()),
|
||||||
|
Key::Named(named) => write!(f, "{:?}", named),
|
||||||
|
other => write!(f, "{:?}", other),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,11 +49,11 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
|
||||||
let mut key_binds = HashMap::new();
|
let mut key_binds = HashMap::new();
|
||||||
|
|
||||||
macro_rules! bind {
|
macro_rules! bind {
|
||||||
([$($modifier:ident),+ $(,)?], $key_code:ident, $action:ident) => {{
|
([$($modifier:ident),+ $(,)?], $key:expr, $action:ident) => {{
|
||||||
key_binds.insert(
|
key_binds.insert(
|
||||||
KeyBind {
|
KeyBind {
|
||||||
modifiers: vec![$(Modifier::$modifier),+],
|
modifiers: vec![$(Modifier::$modifier),+],
|
||||||
key_code: KeyCode::$key_code,
|
key: $key,
|
||||||
},
|
},
|
||||||
Action::$action,
|
Action::$action,
|
||||||
);
|
);
|
||||||
|
|
@ -54,52 +61,56 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard key bindings
|
// Standard key bindings
|
||||||
bind!([Ctrl, Shift], A, SelectAll);
|
bind!([Ctrl, Shift], Key::Character("A".into()), SelectAll);
|
||||||
bind!([Ctrl, Shift], C, Copy);
|
bind!([Ctrl, Shift], Key::Character("C".into()), Copy);
|
||||||
bind!([Ctrl, Shift], F, Find);
|
bind!([Ctrl, Shift], Key::Character("F".into()), Find);
|
||||||
bind!([Ctrl, Shift], N, WindowNew);
|
bind!([Ctrl, Shift], Key::Character("N".into()), WindowNew);
|
||||||
bind!([Ctrl, Shift], Q, WindowClose);
|
bind!([Ctrl, Shift], Key::Character("Q".into()), WindowClose);
|
||||||
bind!([Ctrl, Shift], T, TabNew);
|
bind!([Ctrl, Shift], Key::Character("T".into()), TabNew);
|
||||||
bind!([Ctrl, Shift], V, Paste);
|
bind!([Ctrl, Shift], Key::Character("V".into()), Paste);
|
||||||
bind!([Shift], Insert, PastePrimary);
|
bind!([Shift], Key::Named(Named::Insert), PastePrimary);
|
||||||
bind!([Ctrl, Shift], W, TabClose);
|
bind!([Ctrl, Shift], Key::Character("W".into()), TabClose);
|
||||||
|
|
||||||
// Ctrl+Alt+D splits horizontally, Ctrl+Alt+R splits vertically, Ctrl+Shift+X maximizes split
|
// Ctrl+Alt+D splits horizontally, Ctrl+Alt+R splits vertically, Ctrl+Shift+X maximizes split
|
||||||
//TODO: Adjust bindings as desired by UX
|
//TODO: Adjust bindings as desired by UX
|
||||||
bind!([Ctrl, Alt], D, PaneSplitHorizontal);
|
bind!([Ctrl, Alt], Key::Character("d".into()), PaneSplitHorizontal);
|
||||||
bind!([Ctrl, Alt], R, PaneSplitVertical);
|
bind!([Ctrl, Alt], Key::Character("r".into()), PaneSplitVertical);
|
||||||
bind!([Ctrl, Shift], X, PaneToggleMaximized);
|
bind!(
|
||||||
|
[Ctrl, Shift],
|
||||||
|
Key::Character("X".into()),
|
||||||
|
PaneToggleMaximized
|
||||||
|
);
|
||||||
|
|
||||||
// Ctrl+Tab and Ctrl+Shift+Tab cycle through tabs
|
// Ctrl+Tab and Ctrl+Shift+Tab cycle through tabs
|
||||||
// Ctrl+Tab is not a special key for terminals and is free to use
|
// Ctrl+Tab is not a special key for terminals and is free to use
|
||||||
bind!([Ctrl], Tab, TabNext);
|
bind!([Ctrl], Key::Named(Named::Tab), TabNext);
|
||||||
bind!([Ctrl, Shift], Tab, TabPrev);
|
bind!([Ctrl, Shift], Key::Named(Named::Tab), TabPrev);
|
||||||
|
|
||||||
// Ctrl+Shift+# activates tabs by index
|
// Ctrl+Shift+# activates tabs by index
|
||||||
bind!([Ctrl, Shift], Key1, TabActivate0);
|
bind!([Ctrl, Shift], Key::Character("!".into()), TabActivate0);
|
||||||
bind!([Ctrl, Shift], Key2, TabActivate1);
|
bind!([Ctrl, Shift], Key::Character("@".into()), TabActivate1);
|
||||||
bind!([Ctrl, Shift], Key3, TabActivate2);
|
bind!([Ctrl, Shift], Key::Character("#".into()), TabActivate2);
|
||||||
bind!([Ctrl, Shift], Key4, TabActivate3);
|
bind!([Ctrl, Shift], Key::Character("$".into()), TabActivate3);
|
||||||
bind!([Ctrl, Shift], Key5, TabActivate4);
|
bind!([Ctrl, Shift], Key::Character("%".into()), TabActivate4);
|
||||||
bind!([Ctrl, Shift], Key6, TabActivate5);
|
bind!([Ctrl, Shift], Key::Character("^".into()), TabActivate5);
|
||||||
bind!([Ctrl, Shift], Key7, TabActivate6);
|
bind!([Ctrl, Shift], Key::Character("&".into()), TabActivate6);
|
||||||
bind!([Ctrl, Shift], Key8, TabActivate7);
|
bind!([Ctrl, Shift], Key::Character("*".into()), TabActivate7);
|
||||||
bind!([Ctrl, Shift], Key9, TabActivate8);
|
bind!([Ctrl, Shift], Key::Character("(".into()), TabActivate8);
|
||||||
|
|
||||||
// Ctrl+0, Ctrl+-, and Ctrl+= are not special keys for terminals and are free to use
|
// Ctrl+0, Ctrl+-, and Ctrl+= are not special keys for terminals and are free to use
|
||||||
bind!([Ctrl], Key0, ZoomReset);
|
bind!([Ctrl], Key::Character("0".into()), ZoomReset);
|
||||||
bind!([Ctrl], Minus, ZoomOut);
|
bind!([Ctrl], Key::Character("-".into()), ZoomOut);
|
||||||
bind!([Ctrl], Equals, ZoomIn);
|
bind!([Ctrl], Key::Character("=".into()), ZoomIn);
|
||||||
|
|
||||||
// Ctrl+Arrows and Ctrl+HJKL move between splits
|
// Ctrl+Arrows and Ctrl+HJKL move between splits
|
||||||
bind!([Ctrl, Shift], Left, PaneFocusLeft);
|
bind!([Ctrl, Shift], Key::Named(Named::ArrowLeft), PaneFocusLeft);
|
||||||
bind!([Ctrl, Shift], H, PaneFocusLeft);
|
bind!([Ctrl, Shift], Key::Character("H".into()), PaneFocusLeft);
|
||||||
bind!([Ctrl, Shift], Down, PaneFocusDown);
|
bind!([Ctrl, Shift], Key::Named(Named::ArrowDown), PaneFocusDown);
|
||||||
bind!([Ctrl, Shift], J, PaneFocusDown);
|
bind!([Ctrl, Shift], Key::Character("J".into()), PaneFocusDown);
|
||||||
bind!([Ctrl, Shift], Up, PaneFocusUp);
|
bind!([Ctrl, Shift], Key::Named(Named::ArrowUp), PaneFocusUp);
|
||||||
bind!([Ctrl, Shift], K, PaneFocusUp);
|
bind!([Ctrl, Shift], Key::Character("K".into()), PaneFocusUp);
|
||||||
bind!([Ctrl, Shift], Right, PaneFocusRight);
|
bind!([Ctrl, Shift], Key::Named(Named::ArrowRight), PaneFocusRight);
|
||||||
bind!([Ctrl, Shift], L, PaneFocusRight);
|
bind!([Ctrl, Shift], Key::Character("L".into()), PaneFocusRight);
|
||||||
|
|
||||||
key_binds
|
key_binds
|
||||||
}
|
}
|
||||||
|
|
|
||||||
658
src/main.rs
658
src/main.rs
File diff suppressed because it is too large
Load diff
24
src/menu.rs
24
src/menu.rs
|
|
@ -6,6 +6,7 @@ use cosmic::{
|
||||||
widget::{column, horizontal_rule, horizontal_space},
|
widget::{column, horizontal_rule, horizontal_space},
|
||||||
Alignment, Background, Length,
|
Alignment, Background, Length,
|
||||||
},
|
},
|
||||||
|
iced_core::Border,
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
self,
|
self,
|
||||||
|
|
@ -95,16 +96,19 @@ pub fn context_menu<'a>(
|
||||||
icon_color: Some(component.on.into()),
|
icon_color: Some(component.on.into()),
|
||||||
text_color: Some(component.on.into()),
|
text_color: Some(component.on.into()),
|
||||||
background: Some(Background::Color(component.base.into())),
|
background: Some(Background::Color(component.base.into())),
|
||||||
border_radius: 8.0.into(),
|
border: Border {
|
||||||
border_width: 1.0,
|
radius: 8.0.into(),
|
||||||
border_color: component.divider.into(),
|
width: 1.0,
|
||||||
|
color: component.divider.into(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.width(Length::Fixed(240.0))
|
.width(Length::Fixed(240.0))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn menu_bar<'a>(key_binds: &HashMap<KeyBind, Action>) -> Element<'a, Message> {
|
pub fn menu_bar<'a>(config: &Config, key_binds: &HashMap<KeyBind, Action>) -> Element<'a, Message> {
|
||||||
//TODO: port to libcosmic
|
//TODO: port to libcosmic
|
||||||
let menu_root = |label| {
|
let menu_root = |label| {
|
||||||
widget::button(widget::text(label))
|
widget::button(widget::text(label))
|
||||||
|
|
@ -112,6 +116,9 @@ pub fn menu_bar<'a>(key_binds: &HashMap<KeyBind, Action>) -> Element<'a, Message
|
||||||
.style(theme::Button::MenuRoot)
|
.style(theme::Button::MenuRoot)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let menu_folder =
|
||||||
|
|label| menu_button!(widget::text(label), horizontal_space(Length::Fill), ">");
|
||||||
|
|
||||||
let find_key = |action: &Action| -> String {
|
let find_key = |action: &Action| -> String {
|
||||||
for (key_bind, key_action) in key_binds.iter() {
|
for (key_bind, key_action) in key_binds.iter() {
|
||||||
if action == key_action {
|
if action == key_action {
|
||||||
|
|
@ -133,6 +140,12 @@ pub fn menu_bar<'a>(key_binds: &HashMap<KeyBind, Action>) -> Element<'a, Message
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut profile_items = Vec::with_capacity(config.profiles.len());
|
||||||
|
for (name, id) in config.profile_names() {
|
||||||
|
profile_items.push(menu_item(name, Action::ProfileOpen(id)));
|
||||||
|
}
|
||||||
|
//TODO: what to do if there are no profiles?
|
||||||
|
|
||||||
MenuBar::new(vec![
|
MenuBar::new(vec![
|
||||||
MenuTree::with_children(
|
MenuTree::with_children(
|
||||||
menu_root(fl!("file")),
|
menu_root(fl!("file")),
|
||||||
|
|
@ -140,6 +153,9 @@ pub fn menu_bar<'a>(key_binds: &HashMap<KeyBind, Action>) -> Element<'a, Message
|
||||||
menu_item(fl!("new-tab"), Action::TabNew),
|
menu_item(fl!("new-tab"), Action::TabNew),
|
||||||
menu_item(fl!("new-window"), Action::WindowNew),
|
menu_item(fl!("new-window"), Action::WindowNew),
|
||||||
MenuTree::new(horizontal_rule(1)),
|
MenuTree::new(horizontal_rule(1)),
|
||||||
|
MenuTree::with_children(menu_folder(fl!("profile")), profile_items),
|
||||||
|
menu_item(fl!("menu-profiles"), Action::Profiles),
|
||||||
|
MenuTree::new(horizontal_rule(1)),
|
||||||
menu_item(fl!("close-tab"), Action::TabClose),
|
menu_item(fl!("close-tab"), Action::TabClose),
|
||||||
MenuTree::new(horizontal_rule(1)),
|
MenuTree::new(horizontal_rule(1)),
|
||||||
menu_item(fl!("quit"), Action::WindowClose),
|
menu_item(fl!("quit"), Action::WindowClose),
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,7 @@ impl MouseReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn report_sgr_mouse_wheel_scroll(
|
pub fn report_sgr_mouse_wheel_scroll(
|
||||||
&self,
|
&self,
|
||||||
terminal: &Terminal,
|
terminal: &Terminal,
|
||||||
|
|
|
||||||
159
src/terminal.rs
159
src/terminal.rs
|
|
@ -9,7 +9,7 @@ use alacritty_terminal::{
|
||||||
cell::Flags,
|
cell::Flags,
|
||||||
color::{self, Colors},
|
color::{self, Colors},
|
||||||
search::RegexSearch,
|
search::RegexSearch,
|
||||||
viewport_to_point, Config, TermMode,
|
viewport_to_point, Config, TermDamage, TermMode,
|
||||||
},
|
},
|
||||||
tty::{self, Options},
|
tty::{self, Options},
|
||||||
vte::ansi::{Color, NamedColor, Rgb},
|
vte::ansi::{Color, NamedColor, Rgb},
|
||||||
|
|
@ -27,7 +27,7 @@ use indexmap::IndexSet;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
mem,
|
io, mem,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU32, Ordering},
|
atomic::{AtomicU32, Ordering},
|
||||||
Arc, Weak,
|
Arc, Weak,
|
||||||
|
|
@ -38,7 +38,10 @@ use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub use alacritty_terminal::grid::Scroll as TerminalScroll;
|
pub use alacritty_terminal::grid::Scroll as TerminalScroll;
|
||||||
|
|
||||||
use crate::{config::Config as AppConfig, mouse_reporter::MouseReporter};
|
use crate::{
|
||||||
|
config::{Config as AppConfig, ProfileId},
|
||||||
|
mouse_reporter::MouseReporter,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
|
|
@ -187,21 +190,23 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Terminal {
|
pub struct Terminal {
|
||||||
default_attrs: Attrs<'static>,
|
|
||||||
buffer: Arc<Buffer>,
|
|
||||||
size: Size,
|
|
||||||
pub term: Arc<FairMutex<Term<EventProxy>>>,
|
|
||||||
colors: Colors,
|
|
||||||
dim_font_weight: Weight,
|
|
||||||
bold_font_weight: Weight,
|
|
||||||
use_bright_bold: bool,
|
|
||||||
notifier: Notifier,
|
|
||||||
pub context_menu: Option<cosmic::iced::Point>,
|
pub context_menu: Option<cosmic::iced::Point>,
|
||||||
|
pub metadata_set: IndexSet<Metadata>,
|
||||||
pub needs_update: bool,
|
pub needs_update: bool,
|
||||||
|
pub profile_id_opt: Option<ProfileId>,
|
||||||
|
pub tab_title_override: Option<String>,
|
||||||
|
pub term: Arc<FairMutex<Term<EventProxy>>>,
|
||||||
|
bold_font_weight: Weight,
|
||||||
|
buffer: Arc<Buffer>,
|
||||||
|
colors: Colors,
|
||||||
|
default_attrs: Attrs<'static>,
|
||||||
|
dim_font_weight: Weight,
|
||||||
|
mouse_reporter: MouseReporter,
|
||||||
|
notifier: Notifier,
|
||||||
search_regex_opt: Option<RegexSearch>,
|
search_regex_opt: Option<RegexSearch>,
|
||||||
search_value: String,
|
search_value: String,
|
||||||
pub metadata_set: IndexSet<Metadata>,
|
size: Size,
|
||||||
mouse_reporter: MouseReporter,
|
use_bright_bold: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
|
|
@ -214,7 +219,9 @@ impl Terminal {
|
||||||
options: Options,
|
options: Options,
|
||||||
app_config: &AppConfig,
|
app_config: &AppConfig,
|
||||||
colors: Colors,
|
colors: Colors,
|
||||||
) -> Self {
|
profile_id_opt: Option<ProfileId>,
|
||||||
|
tab_title_override: Option<String>,
|
||||||
|
) -> Result<Self, io::Error> {
|
||||||
let font_stretch = app_config.typed_font_stretch();
|
let font_stretch = app_config.typed_font_stretch();
|
||||||
let font_weight = app_config.font_weight;
|
let font_weight = app_config.font_weight;
|
||||||
let dim_font_weight = app_config.dim_font_weight;
|
let dim_font_weight = app_config.dim_font_weight;
|
||||||
|
|
@ -242,12 +249,12 @@ impl Terminal {
|
||||||
|
|
||||||
let (cell_width, cell_height) = {
|
let (cell_width, cell_height) = {
|
||||||
let mut font_system = font_system().write().unwrap();
|
let mut font_system = font_system().write().unwrap();
|
||||||
let mut font_system = font_system.raw();
|
let font_system = font_system.raw();
|
||||||
buffer.set_wrap(&mut font_system, Wrap::None);
|
buffer.set_wrap(font_system, Wrap::None);
|
||||||
|
|
||||||
// Use size of space to determine cell size
|
// Use size of space to determine cell size
|
||||||
buffer.set_text(&mut font_system, " ", default_attrs, Shaping::Advanced);
|
buffer.set_text(font_system, " ", default_attrs, Shaping::Advanced);
|
||||||
let layout = buffer.line_layout(&mut font_system, 0).unwrap();
|
let layout = buffer.line_layout(font_system, 0).unwrap();
|
||||||
let w = layout[0].w;
|
let w = layout[0].w;
|
||||||
buffer.set_monospace_width(font_system, Some(w));
|
buffer.set_monospace_width(font_system, Some(w));
|
||||||
(w, metrics.line_height)
|
(w, metrics.line_height)
|
||||||
|
|
@ -267,29 +274,31 @@ impl Terminal {
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let window_id = 0;
|
let window_id = 0;
|
||||||
let pty = tty::new(&options, size.into(), window_id).unwrap();
|
let pty = tty::new(&options, size.into(), window_id)?;
|
||||||
|
|
||||||
let pty_event_loop = EventLoop::new(term.clone(), event_proxy, pty, options.hold, false);
|
let pty_event_loop = EventLoop::new(term.clone(), event_proxy, pty, options.hold, false);
|
||||||
let notifier = Notifier(pty_event_loop.channel());
|
let notifier = Notifier(pty_event_loop.channel());
|
||||||
let _pty_join_handle = pty_event_loop.spawn();
|
let _pty_join_handle = pty_event_loop.spawn();
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
colors,
|
|
||||||
dim_font_weight: Weight(dim_font_weight),
|
|
||||||
bold_font_weight: Weight(bold_font_weight),
|
bold_font_weight: Weight(bold_font_weight),
|
||||||
use_bright_bold,
|
|
||||||
default_attrs,
|
|
||||||
buffer: Arc::new(buffer),
|
buffer: Arc::new(buffer),
|
||||||
size,
|
colors,
|
||||||
term,
|
|
||||||
notifier,
|
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
needs_update: true,
|
default_attrs,
|
||||||
search_regex_opt: None,
|
dim_font_weight: Weight(dim_font_weight),
|
||||||
search_value: String::new(),
|
|
||||||
metadata_set,
|
metadata_set,
|
||||||
mouse_reporter: Default::default(),
|
mouse_reporter: Default::default(),
|
||||||
}
|
needs_update: true,
|
||||||
|
notifier,
|
||||||
|
profile_id_opt,
|
||||||
|
search_regex_opt: None,
|
||||||
|
search_value: String::new(),
|
||||||
|
size,
|
||||||
|
tab_title_override,
|
||||||
|
term,
|
||||||
|
use_bright_bold,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_weak(&self) -> Weak<Buffer> {
|
pub fn buffer_weak(&self) -> Weak<Buffer> {
|
||||||
|
|
@ -371,6 +380,8 @@ impl Terminal {
|
||||||
buffer.set_size(font_system.raw(), width as f32, height as f32);
|
buffer.set_size(font_system.raw(), width as f32, height as f32);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.needs_update = true;
|
||||||
|
|
||||||
log::debug!("resize {:?}", instant.elapsed());
|
log::debug!("resize {:?}", instant.elapsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +470,7 @@ impl Terminal {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find next search match
|
// Find next search match
|
||||||
match term.search_next(
|
if let Some(search_match) = term.search_next(
|
||||||
search_regex,
|
search_regex,
|
||||||
search_origin,
|
search_origin,
|
||||||
if forwards {
|
if forwards {
|
||||||
|
|
@ -471,21 +482,18 @@ impl Terminal {
|
||||||
if forwards { Side::Left } else { Side::Right },
|
if forwards { Side::Left } else { Side::Right },
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
Some(search_match) => {
|
// Scroll to match
|
||||||
// Scroll to match
|
if forwards {
|
||||||
if forwards {
|
term.scroll_to_point(*search_match.end());
|
||||||
term.scroll_to_point(*search_match.end());
|
} else {
|
||||||
} else {
|
term.scroll_to_point(*search_match.start());
|
||||||
term.scroll_to_point(*search_match.start());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set selection to match
|
|
||||||
let mut selection =
|
|
||||||
Selection::new(SelectionType::Simple, *search_match.start(), Side::Left);
|
|
||||||
selection.update(*search_match.end(), Side::Right);
|
|
||||||
term.selection = Some(selection);
|
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
|
// Set selection to match
|
||||||
|
let mut selection =
|
||||||
|
Selection::new(SelectionType::Simple, *search_match.start(), Side::Left);
|
||||||
|
selection.update(*search_match.end(), Side::Right);
|
||||||
|
term.selection = Some(selection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,7 +559,7 @@ impl Terminal {
|
||||||
update_cell_size = true;
|
update_cell_size = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(colors) = themes.get(config.syntax_theme()) {
|
if let Some(colors) = themes.get(config.syntax_theme(self.profile_id_opt)) {
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
for i in 0..color::COUNT {
|
for i in 0..color::COUNT {
|
||||||
if self.colors[i] != colors[i] {
|
if self.colors[i] != colors[i] {
|
||||||
|
|
@ -560,19 +568,7 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
self.metadata_set.clear();
|
self.update_colors(config);
|
||||||
let default_bg = convert_color(&colors, Color::Named(NamedColor::Background));
|
|
||||||
let default_fg = convert_color(&colors, Color::Named(NamedColor::Foreground));
|
|
||||||
|
|
||||||
let default_metadata = Metadata::new(default_bg, default_fg);
|
|
||||||
let (default_metadata_idx, _) = self.metadata_set.insert_full(default_metadata);
|
|
||||||
|
|
||||||
self.default_attrs = Attrs::new()
|
|
||||||
.family(Family::Monospace)
|
|
||||||
.weight(Weight(config.font_weight))
|
|
||||||
.stretch(config.typed_font_stretch())
|
|
||||||
.color(default_fg)
|
|
||||||
.metadata(default_metadata_idx);
|
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -584,6 +580,22 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_colors(&mut self, config: &AppConfig) {
|
||||||
|
self.metadata_set.clear();
|
||||||
|
let default_bg = convert_color(&self.colors, Color::Named(NamedColor::Background));
|
||||||
|
let default_fg = convert_color(&self.colors, Color::Named(NamedColor::Foreground));
|
||||||
|
|
||||||
|
let default_metadata = Metadata::new(default_bg, default_fg);
|
||||||
|
let (default_metadata_idx, _) = self.metadata_set.insert_full(default_metadata);
|
||||||
|
|
||||||
|
self.default_attrs = Attrs::new()
|
||||||
|
.family(Family::Monospace)
|
||||||
|
.weight(Weight(config.font_weight))
|
||||||
|
.stretch(config.typed_font_stretch())
|
||||||
|
.color(default_fg)
|
||||||
|
.metadata(default_metadata_idx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_cell_size(&mut self) {
|
pub fn update_cell_size(&mut self) {
|
||||||
let default_attrs = self.default_attrs;
|
let default_attrs = self.default_attrs;
|
||||||
let (cell_width, cell_height) = {
|
let (cell_width, cell_height) = {
|
||||||
|
|
@ -634,7 +646,14 @@ impl Terminal {
|
||||||
let mut text = String::from(LRI);
|
let mut text = String::from(LRI);
|
||||||
let mut attrs_list = AttrsList::new(self.default_attrs);
|
let mut attrs_list = AttrsList::new(self.default_attrs);
|
||||||
{
|
{
|
||||||
let term = self.term.lock();
|
let mut term = self.term.lock();
|
||||||
|
//TODO: use damage?
|
||||||
|
match term.damage() {
|
||||||
|
TermDamage::Full => {}
|
||||||
|
TermDamage::Partial(_damage_lines) => {}
|
||||||
|
}
|
||||||
|
term.reset_damage();
|
||||||
|
|
||||||
let grid = term.grid();
|
let grid = term.grid();
|
||||||
for indexed in grid.display_iter() {
|
for indexed in grid.display_iter() {
|
||||||
if indexed.point.line != last_point.unwrap_or(indexed.point).line {
|
if indexed.point.line != last_point.unwrap_or(indexed.point).line {
|
||||||
|
|
@ -647,10 +666,7 @@ impl Terminal {
|
||||||
buffer.set_redraw(true);
|
buffer.set_redraw(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab skip/stop is handled by alacritty_terminal
|
if buffer.lines[line_i].set_text(text.clone(), attrs_list.clone()) {
|
||||||
if buffer.lines[line_i]
|
|
||||||
.set_text(text.replace('\t', " "), attrs_list.clone())
|
|
||||||
{
|
|
||||||
buffer.set_redraw(true);
|
buffer.set_redraw(true);
|
||||||
}
|
}
|
||||||
line_i += 1;
|
line_i += 1;
|
||||||
|
|
@ -668,7 +684,11 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = text.len();
|
let start = text.len();
|
||||||
text.push(indexed.cell.c);
|
// Tab skip/stop is handled by alacritty_terminal
|
||||||
|
text.push(match indexed.cell.c {
|
||||||
|
'\t' => ' ',
|
||||||
|
c => c,
|
||||||
|
});
|
||||||
if let Some(zerowidth) = indexed.cell.zerowidth() {
|
if let Some(zerowidth) = indexed.cell.zerowidth() {
|
||||||
for &c in zerowidth {
|
for &c in zerowidth {
|
||||||
text.push(c);
|
text.push(c);
|
||||||
|
|
@ -775,9 +795,11 @@ impl Terminal {
|
||||||
buffer.set_redraw(true);
|
buffer.set_redraw(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shape and trim shape run cache
|
||||||
{
|
{
|
||||||
let mut font_system = font_system().write().unwrap();
|
let mut font_system = font_system().write().unwrap();
|
||||||
buffer.shape_until_scroll(font_system.raw(), true);
|
buffer.shape_until_scroll(font_system.raw(), true);
|
||||||
|
font_system.raw().shape_run_cache.trim(1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -800,6 +822,7 @@ impl Terminal {
|
||||||
) {
|
) {
|
||||||
let term_lock = self.term.lock();
|
let term_lock = self.term.lock();
|
||||||
let mode = term_lock.mode();
|
let mode = term_lock.mode();
|
||||||
|
#[allow(clippy::collapsible_else_if)]
|
||||||
if mode.contains(TermMode::SGR_MOUSE) {
|
if mode.contains(TermMode::SGR_MOUSE) {
|
||||||
if let Some(code) = self.mouse_reporter.sgr_mouse_code(event, modifiers, x, y) {
|
if let Some(code) = self.mouse_reporter.sgr_mouse_code(event, modifiers, x, y) {
|
||||||
self.input_no_scroll(code)
|
self.input_no_scroll(code)
|
||||||
|
|
@ -849,6 +872,6 @@ impl Terminal {
|
||||||
impl Drop for Terminal {
|
impl Drop for Terminal {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Ensure shutdown on terminal drop
|
// Ensure shutdown on terminal drop
|
||||||
let _ = self.notifier.0.send(Msg::Shutdown);
|
self.notifier.0.send(Msg::Shutdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,15 @@ use alacritty_terminal::{
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
cosmic_theme::palette::{blend::Compose, WithAlpha},
|
cosmic_theme::palette::{blend::Compose, WithAlpha},
|
||||||
iced::{
|
iced::{
|
||||||
advanced::graphics::text::{font_system, Raw},
|
advanced::graphics::text::Raw,
|
||||||
event::{Event, Status},
|
event::{Event, Status},
|
||||||
keyboard::{Event as KeyEvent, KeyCode, Modifiers},
|
keyboard::{Event as KeyEvent, Key, Modifiers},
|
||||||
mouse::{self, Button, Event as MouseEvent, ScrollDelta},
|
mouse::{self, Button, Event as MouseEvent, ScrollDelta},
|
||||||
Color, Element, Length, Padding, Point, Rectangle, Size, Vector,
|
Color, Element, Length, Padding, Point, Rectangle, Size, Vector,
|
||||||
},
|
},
|
||||||
iced_core::{
|
iced_core::{
|
||||||
clipboard::Clipboard,
|
clipboard::Clipboard,
|
||||||
|
keyboard::key::Named,
|
||||||
layout::{self, Layout},
|
layout::{self, Layout},
|
||||||
renderer::{self, Quad, Renderer as _},
|
renderer::{self, Quad, Renderer as _},
|
||||||
text::Renderer as _,
|
text::Renderer as _,
|
||||||
|
|
@ -24,7 +25,7 @@ use cosmic::{
|
||||||
operation::{self, Operation, OperationOutputWrapper},
|
operation::{self, Operation, OperationOutputWrapper},
|
||||||
tree, Id, Widget,
|
tree, Id, Widget,
|
||||||
},
|
},
|
||||||
Shell,
|
Border, Shell,
|
||||||
},
|
},
|
||||||
theme::Theme,
|
theme::Theme,
|
||||||
Renderer,
|
Renderer,
|
||||||
|
|
@ -34,22 +35,30 @@ use indexmap::IndexSet;
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
cmp,
|
cmp,
|
||||||
|
collections::HashMap,
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{terminal::Metadata, Terminal, TerminalScroll};
|
use crate::{
|
||||||
|
key_bind::{key_binds, KeyBind},
|
||||||
|
terminal::Metadata,
|
||||||
|
Action, Terminal, TerminalScroll,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct TerminalBox<'a, Message> {
|
pub struct TerminalBox<'a, Message> {
|
||||||
terminal: &'a Mutex<Terminal>,
|
terminal: &'a Mutex<Terminal>,
|
||||||
id: Option<Id>,
|
id: Option<Id>,
|
||||||
|
border: Border,
|
||||||
padding: Padding,
|
padding: Padding,
|
||||||
click_timing: Duration,
|
click_timing: Duration,
|
||||||
context_menu: Option<Point>,
|
context_menu: Option<Point>,
|
||||||
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
||||||
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
|
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||||
|
opacity: Option<f32>,
|
||||||
mouse_inside_boundary: Option<bool>,
|
mouse_inside_boundary: Option<bool>,
|
||||||
on_middle_click: Option<Box<dyn Fn() -> Message + 'a>>,
|
on_middle_click: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||||
|
key_binds: HashMap<KeyBind, Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> TerminalBox<'a, Message>
|
impl<'a, Message> TerminalBox<'a, Message>
|
||||||
|
|
@ -60,13 +69,16 @@ where
|
||||||
Self {
|
Self {
|
||||||
terminal,
|
terminal,
|
||||||
id: None,
|
id: None,
|
||||||
|
border: Border::default(),
|
||||||
padding: Padding::new(0.0),
|
padding: Padding::new(0.0),
|
||||||
click_timing: Duration::from_millis(500),
|
click_timing: Duration::from_millis(500),
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
on_context_menu: None,
|
on_context_menu: None,
|
||||||
on_mouse_enter: None,
|
on_mouse_enter: None,
|
||||||
|
opacity: None,
|
||||||
mouse_inside_boundary: None,
|
mouse_inside_boundary: None,
|
||||||
on_middle_click: None,
|
on_middle_click: None,
|
||||||
|
key_binds: key_binds(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +87,11 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn border<B: Into<Border>>(mut self, border: B) -> Self {
|
||||||
|
self.border = border.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||||
self.padding = padding.into();
|
self.padding = padding.into();
|
||||||
self
|
self
|
||||||
|
|
@ -107,16 +124,21 @@ where
|
||||||
self.on_middle_click = Some(Box::new(on_middle_click));
|
self.on_middle_click = Some(Box::new(on_middle_click));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn opacity(mut self, opacity: f32) -> Self {
|
||||||
|
self.opacity = Some(opacity);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminal_box<'a, Message>(terminal: &'a Mutex<Terminal>) -> TerminalBox<'a, Message>
|
pub fn terminal_box<Message>(terminal: &Mutex<Terminal>) -> TerminalBox<'_, Message>
|
||||||
where
|
where
|
||||||
Message: Clone,
|
Message: Clone,
|
||||||
{
|
{
|
||||||
TerminalBox::new(terminal)
|
TerminalBox::new(terminal)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Widget<Message, Renderer> for TerminalBox<'a, Message>
|
impl<'a, Message> Widget<Message, cosmic::Theme, Renderer> for TerminalBox<'a, Message>
|
||||||
where
|
where
|
||||||
Message: Clone,
|
Message: Clone,
|
||||||
{
|
{
|
||||||
|
|
@ -128,12 +150,8 @@ where
|
||||||
tree::State::new(State::new())
|
tree::State::new(State::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width(&self) -> Length {
|
fn size(&self) -> Size<Length> {
|
||||||
Length::Fill
|
Size::new(Length::Fill, Length::Fill)
|
||||||
}
|
|
||||||
|
|
||||||
fn height(&self) -> Length {
|
|
||||||
Length::Fill
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
|
|
@ -154,12 +172,6 @@ where
|
||||||
terminal.needs_update = false;
|
terminal.needs_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure terminal is shaped
|
|
||||||
terminal.with_buffer_mut(|buffer| {
|
|
||||||
let mut font_system = font_system().write().unwrap();
|
|
||||||
buffer.shape_until_scroll(font_system.raw(), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate layout lines
|
// Calculate layout lines
|
||||||
terminal.with_buffer(|buffer| {
|
terminal.with_buffer(|buffer| {
|
||||||
let mut layout_lines = 0;
|
let mut layout_lines = 0;
|
||||||
|
|
@ -173,7 +185,7 @@ where
|
||||||
let height = layout_lines as f32 * buffer.metrics().line_height;
|
let height = layout_lines as f32 * buffer.metrics().line_height;
|
||||||
let size = Size::new(limits.max().width, height);
|
let size = Size::new(limits.max().width, height);
|
||||||
|
|
||||||
layout::Node::new(limits.resolve(size))
|
layout::Node::new(limits.resolve(Length::Fill, Length::Fill, size))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,9 +211,8 @@ where
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
let state = tree.state.downcast_ref::<State>();
|
let state = tree.state.downcast_ref::<State>();
|
||||||
|
|
||||||
match &state.dragging {
|
if let Some(Dragging::Scrollbar { .. }) = &state.dragging {
|
||||||
Some(Dragging::Scrollbar { .. }) => return mouse::Interaction::Idle,
|
return mouse::Interaction::Idle;
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||||
|
|
@ -235,8 +246,7 @@ where
|
||||||
let cosmic_theme = theme.cosmic();
|
let cosmic_theme = theme.cosmic();
|
||||||
let scrollbar_w = cosmic_theme.spacing.space_xxs as f32;
|
let scrollbar_w = cosmic_theme.spacing.space_xxs as f32;
|
||||||
|
|
||||||
let view_position =
|
let view_position = layout.position() + [self.padding.left, self.padding.top].into();
|
||||||
layout.position() + [self.padding.left as f32, self.padding.top as f32].into();
|
|
||||||
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
||||||
- self.padding.horizontal() as i32
|
- self.padding.horizontal() as i32
|
||||||
- scrollbar_w as i32;
|
- scrollbar_w as i32;
|
||||||
|
|
@ -259,12 +269,6 @@ where
|
||||||
terminal.needs_update = false;
|
terminal.needs_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure terminal is shaped
|
|
||||||
terminal.with_buffer_mut(|buffer| {
|
|
||||||
let mut font_system = font_system().write().unwrap();
|
|
||||||
buffer.shape_until_scroll(font_system.raw(), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render default background
|
// Render default background
|
||||||
{
|
{
|
||||||
let meta = &terminal.metadata_set[terminal.default_attrs().metadata];
|
let meta = &terminal.metadata_set[terminal.default_attrs().metadata];
|
||||||
|
|
@ -272,19 +276,18 @@ where
|
||||||
|
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
Quad {
|
Quad {
|
||||||
bounds: Rectangle::new(
|
bounds: layout.bounds(),
|
||||||
view_position,
|
border: self.border,
|
||||||
Size::new(view_w as f32 + scrollbar_w, view_h as f32),
|
..Default::default()
|
||||||
),
|
|
||||||
border_radius: 0.0.into(),
|
|
||||||
border_width: 0.0,
|
|
||||||
border_color: Color::TRANSPARENT,
|
|
||||||
},
|
},
|
||||||
Color::new(
|
Color::new(
|
||||||
background_color.r() as f32 / 255.0,
|
background_color.r() as f32 / 255.0,
|
||||||
background_color.g() as f32 / 255.0,
|
background_color.g() as f32 / 255.0,
|
||||||
background_color.b() as f32 / 255.0,
|
background_color.b() as f32 / 255.0,
|
||||||
background_color.a() as f32 / 255.0,
|
match self.opacity {
|
||||||
|
Some(opacity) => opacity,
|
||||||
|
None => background_color.a() as f32 / 255.0,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -327,10 +330,6 @@ where
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
is_focused: bool,
|
is_focused: bool,
|
||||||
) {
|
) {
|
||||||
if self.metadata == self.default_metadata {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cosmic_text_to_iced_color = |color: cosmic_text::Color| {
|
let cosmic_text_to_iced_color = |color: cosmic_text::Color| {
|
||||||
Color::new(
|
Color::new(
|
||||||
color.r() as f32 / 255.0,
|
color.r() as f32 / 255.0,
|
||||||
|
|
@ -356,9 +355,7 @@ where
|
||||||
self.view_position + $pos_offset,
|
self.view_position + $pos_offset,
|
||||||
Size::new($width, $style_line_height),
|
Size::new($width, $style_line_height),
|
||||||
),
|
),
|
||||||
border_radius: 0.0.into(),
|
..Default::default()
|
||||||
border_width: 0.0,
|
|
||||||
border_color: Color::TRANSPARENT,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($pos_offset:expr, $style_line_height:expr) => {
|
($pos_offset:expr, $style_line_height:expr) => {
|
||||||
|
|
@ -367,11 +364,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = &self.metadata_set[self.metadata];
|
let metadata = &self.metadata_set[self.metadata];
|
||||||
let color = shade(metadata.bg, is_focused);
|
if metadata.bg != self.metadata_set[self.default_metadata].bg {
|
||||||
renderer.fill_quad(
|
let color = shade(metadata.bg, is_focused);
|
||||||
mk_quad!(mk_pos_offset!(0.0, self.line_height), self.line_height),
|
renderer.fill_quad(
|
||||||
cosmic_text_to_iced_color(color),
|
mk_quad!(mk_pos_offset!(0.0, self.line_height), self.line_height),
|
||||||
);
|
cosmic_text_to_iced_color(color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if !metadata.flags.is_empty() {
|
if !metadata.flags.is_empty() {
|
||||||
let style_line_height =
|
let style_line_height =
|
||||||
|
|
@ -462,7 +461,7 @@ where
|
||||||
dot_width = dot_width.min(full_width - accu_width);
|
dot_width = dot_width.min(full_width - accu_width);
|
||||||
|
|
||||||
let dot_bottom_offset = match accu_width as u32 % 8 {
|
let dot_bottom_offset = match accu_width as u32 % 8 {
|
||||||
3 | 4 | 5 => bottom_offset + style_line_height,
|
3..=5 => bottom_offset + style_line_height,
|
||||||
2 | 6 => bottom_offset + 2.0 * style_line_height / 3.0,
|
2 | 6 => bottom_offset + 2.0 * style_line_height / 3.0,
|
||||||
1 | 7 => bottom_offset + 1.0 * style_line_height / 3.0,
|
1 | 7 => bottom_offset + 1.0 * style_line_height / 3.0,
|
||||||
_ => bottom_offset,
|
_ => bottom_offset,
|
||||||
|
|
@ -515,10 +514,7 @@ where
|
||||||
Size::new(scrollbar_w, scrollbar_h),
|
Size::new(scrollbar_w, scrollbar_h),
|
||||||
);
|
);
|
||||||
|
|
||||||
let pressed = match &state.dragging {
|
let pressed = matches!(&state.dragging, Some(Dragging::Scrollbar { .. }));
|
||||||
Some(Dragging::Scrollbar { .. }) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut hover = false;
|
let mut hover = false;
|
||||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||||
|
|
@ -568,9 +564,12 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
Quad {
|
Quad {
|
||||||
bounds: scrollbar_draw,
|
bounds: scrollbar_draw,
|
||||||
border_radius: (scrollbar_draw.width / 2.0).into(),
|
border: Border {
|
||||||
border_width: 0.0,
|
radius: (scrollbar_draw.width / 2.0).into(),
|
||||||
border_color: Color::TRANSPARENT,
|
width: 0.0,
|
||||||
|
color: Color::TRANSPARENT,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
scrollbar_color,
|
scrollbar_color,
|
||||||
);
|
);
|
||||||
|
|
@ -606,238 +605,100 @@ where
|
||||||
let mut status = Status::Ignored;
|
let mut status = Status::Ignored;
|
||||||
match event {
|
match event {
|
||||||
Event::Keyboard(KeyEvent::KeyPressed {
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
key_code,
|
key: Key::Named(named),
|
||||||
modifiers,
|
modifiers,
|
||||||
}) if state.is_focused => match (
|
..
|
||||||
modifiers.logo(),
|
}) if state.is_focused => {
|
||||||
modifiers.control(),
|
for (key_bind, _) in self.key_binds.iter() {
|
||||||
modifiers.alt(),
|
if key_bind.matches(modifiers, &Key::Named(named)) {
|
||||||
modifiers.shift(),
|
return Status::Captured;
|
||||||
) {
|
}
|
||||||
(true, _, _, _) => {
|
|
||||||
// Ignore super keys
|
|
||||||
}
|
}
|
||||||
(_, true, _, _) => match key_code {
|
let mod_no = calculate_modifier_number(state);
|
||||||
KeyCode::Up => {
|
let escape_code = match named {
|
||||||
terminal.input_scroll(b"\x1B[1;5A".as_slice());
|
Named::Insert => csi("2", "~", mod_no),
|
||||||
|
Named::Delete => csi("3", "~", mod_no),
|
||||||
|
Named::PageUp => csi("5", "~", mod_no),
|
||||||
|
Named::PageDown => csi("6", "~", mod_no),
|
||||||
|
Named::ArrowUp => {
|
||||||
|
if is_app_cursor {
|
||||||
|
ss3("A", mod_no)
|
||||||
|
} else {
|
||||||
|
csi("A", "", mod_no)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Named::ArrowDown => {
|
||||||
|
if is_app_cursor {
|
||||||
|
ss3("B", mod_no)
|
||||||
|
} else {
|
||||||
|
csi("B", "", mod_no)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Named::ArrowRight => {
|
||||||
|
if is_app_cursor {
|
||||||
|
ss3("C", mod_no)
|
||||||
|
} else {
|
||||||
|
csi("C", "", mod_no)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Named::ArrowLeft => {
|
||||||
|
if is_app_cursor {
|
||||||
|
ss3("D", mod_no)
|
||||||
|
} else {
|
||||||
|
csi("D", "", mod_no)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Named::End => {
|
||||||
|
if is_app_cursor {
|
||||||
|
ss3("F", mod_no)
|
||||||
|
} else {
|
||||||
|
csi("F", "", mod_no)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Named::Home => {
|
||||||
|
if is_app_cursor {
|
||||||
|
ss3("H", mod_no)
|
||||||
|
} else {
|
||||||
|
csi("H", "", mod_no)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Named::F1 => ss3("P", mod_no),
|
||||||
|
Named::F2 => ss3("Q", mod_no),
|
||||||
|
Named::F3 => ss3("R", mod_no),
|
||||||
|
Named::F4 => ss3("S", mod_no),
|
||||||
|
Named::F5 => csi("15", "~", mod_no),
|
||||||
|
Named::F6 => csi("17", "~", mod_no),
|
||||||
|
Named::F7 => csi("18", "~", mod_no),
|
||||||
|
Named::F8 => csi("19", "~", mod_no),
|
||||||
|
Named::F9 => csi("20", "~", mod_no),
|
||||||
|
Named::F10 => csi("21", "~", mod_no),
|
||||||
|
Named::F11 => csi("23", "~", mod_no),
|
||||||
|
Named::F12 => csi("24", "~", mod_no),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(escape_code) = escape_code {
|
||||||
|
terminal.input_scroll(escape_code);
|
||||||
|
return Status::Captured;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Special handle Enter, Escape, Backspace and Tab as described in
|
||||||
|
//https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-key-event-encoding
|
||||||
|
//Also special handle Ctrl-_ to behave like xterm
|
||||||
|
let alt_prefix = if modifiers.alt() { "\x1B" } else { "" };
|
||||||
|
match named {
|
||||||
|
Named::Backspace => {
|
||||||
|
let code = if modifiers.control() { "\x08" } else { "\x7f" };
|
||||||
|
terminal
|
||||||
|
.input_scroll(format!("{}{}", alt_prefix, code).as_bytes().to_vec());
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
KeyCode::Down => {
|
Named::Enter => {
|
||||||
terminal.input_scroll(b"\x1B[1;5B".as_slice());
|
terminal
|
||||||
|
.input_scroll(format!("{}{}", alt_prefix, "\x0D").as_bytes().to_vec());
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
KeyCode::Right => {
|
Named::Escape => {
|
||||||
terminal.input_scroll(b"\x1B[1;5C".as_slice());
|
//Escape with any modifier will cancel selection
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Left => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;5D".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::End => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;5F".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Home => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;5H".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Insert => {
|
|
||||||
terminal.input_scroll(b"\x1B[2;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Delete => {
|
|
||||||
terminal.input_scroll(b"\x1B[3;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::PageUp => {
|
|
||||||
terminal.input_scroll(b"\x1B[5;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::PageDown => {
|
|
||||||
terminal.input_scroll(b"\x1B[6;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F1 => {
|
|
||||||
terminal.input_scroll(b"\x1BO;5P".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F2 => {
|
|
||||||
terminal.input_scroll(b"\x1BO;5Q".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F3 => {
|
|
||||||
terminal.input_scroll(b"\x1BO;5R".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F4 => {
|
|
||||||
terminal.input_scroll(b"\x1BO;5S".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F5 => {
|
|
||||||
terminal.input_scroll(b"\x1B[15;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F6 => {
|
|
||||||
terminal.input_scroll(b"\x1B[17;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F7 => {
|
|
||||||
terminal.input_scroll(b"\x1B[18;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F8 => {
|
|
||||||
terminal.input_scroll(b"\x1B[19;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F9 => {
|
|
||||||
terminal.input_scroll(b"\x1B[20;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F10 => {
|
|
||||||
terminal.input_scroll(b"\x1B[21;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F11 => {
|
|
||||||
terminal.input_scroll(b"\x1B[23;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F12 => {
|
|
||||||
terminal.input_scroll(b"\x1B[24;5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
// Handle alt keys
|
|
||||||
(_, _, true, _) => match key_code {
|
|
||||||
KeyCode::Up => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3A".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Down => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3B".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Right => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3C".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Left => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3D".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::End => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3F".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Home => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3H".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Insert => {
|
|
||||||
terminal.input_scroll(b"\x1B[2;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Delete => {
|
|
||||||
terminal.input_scroll(b"\x1B[3;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::PageUp => {
|
|
||||||
terminal.input_scroll(b"\x1B[5;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::PageDown => {
|
|
||||||
terminal.input_scroll(b"\x1B[6;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F1 => {
|
|
||||||
terminal.input_scroll(b"\x1B[1;3P".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F2 => {
|
|
||||||
terminal.input_scroll(b"\x1B1;3Q".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F3 => {
|
|
||||||
terminal.input_scroll(b"\x1B1;3R".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F4 => {
|
|
||||||
terminal.input_scroll(b"\x1B1;3S".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F5 => {
|
|
||||||
terminal.input_scroll(b"\x1B[15;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F6 => {
|
|
||||||
terminal.input_scroll(b"\x1B[17;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F7 => {
|
|
||||||
terminal.input_scroll(b"\x1B[18;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F8 => {
|
|
||||||
terminal.input_scroll(b"\x1B[19;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F9 => {
|
|
||||||
terminal.input_scroll(b"\x1B[20;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F10 => {
|
|
||||||
terminal.input_scroll(b"\x1B[21;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F11 => {
|
|
||||||
terminal.input_scroll(b"\x1B[23;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F12 => {
|
|
||||||
terminal.input_scroll(b"\x1B[24;3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Backspace => {
|
|
||||||
terminal.input_scroll(b"\x1B\x7F".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
// Handle shift keys
|
|
||||||
(_, _, _, true) => match key_code {
|
|
||||||
KeyCode::End => {
|
|
||||||
terminal.scroll(TerminalScroll::Bottom);
|
|
||||||
}
|
|
||||||
KeyCode::Home => {
|
|
||||||
terminal.scroll(TerminalScroll::Top);
|
|
||||||
}
|
|
||||||
KeyCode::PageDown => {
|
|
||||||
terminal.scroll(TerminalScroll::PageDown);
|
|
||||||
}
|
|
||||||
KeyCode::PageUp => {
|
|
||||||
terminal.scroll(TerminalScroll::PageUp);
|
|
||||||
}
|
|
||||||
KeyCode::Tab => {
|
|
||||||
terminal.input_scroll(b"\x1B[Z".as_slice());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
// Handle keys with no modifiers
|
|
||||||
(_, _, _, false) => match key_code {
|
|
||||||
KeyCode::Backspace => {
|
|
||||||
terminal.input_scroll(b"\x7F".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Tab => {
|
|
||||||
terminal.input_scroll(b"\t".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Enter => {
|
|
||||||
terminal.input_scroll(b"\r".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Escape => {
|
|
||||||
let had_selection = {
|
let had_selection = {
|
||||||
let mut term = terminal.term.lock();
|
let mut term = terminal.term.lock();
|
||||||
term.selection.take().is_some()
|
term.selection.take().is_some()
|
||||||
|
|
@ -845,138 +706,60 @@ where
|
||||||
if had_selection {
|
if had_selection {
|
||||||
terminal.update();
|
terminal.update();
|
||||||
} else {
|
} else {
|
||||||
terminal.input_scroll(b"\x1B".as_slice());
|
terminal.input_scroll(
|
||||||
|
format!("{}{}", alt_prefix, "\x1B").as_bytes().to_vec(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
KeyCode::Up => {
|
Named::Space => {
|
||||||
let code = if is_app_cursor { b"\x1BOA" } else { b"\x1B[A" };
|
terminal.input_scroll(format!("{}{}", alt_prefix, " ").as_bytes().to_vec());
|
||||||
|
|
||||||
terminal.input_scroll(code.as_slice());
|
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
KeyCode::Down => {
|
Named::Tab => {
|
||||||
let code = if is_app_cursor { b"\x1BOB" } else { b"\x1B[B" };
|
let code = if modifiers.shift() { "\x1b[Z" } else { "\x09" };
|
||||||
|
terminal
|
||||||
terminal.input_scroll(code.as_slice());
|
.input_scroll(format!("{}{}", alt_prefix, code).as_bytes().to_vec());
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
KeyCode::Right => {
|
_ => {}
|
||||||
let code = if is_app_cursor { b"\x1BOC" } else { b"\x1B[C" };
|
}
|
||||||
|
}
|
||||||
terminal.input_scroll(code.as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Left => {
|
|
||||||
let code = if is_app_cursor { b"\x1BOD" } else { b"\x1B[D" };
|
|
||||||
|
|
||||||
terminal.input_scroll(code.as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::End => {
|
|
||||||
let code = if is_app_cursor { b"\x1BOF" } else { b"\x1B[F" };
|
|
||||||
|
|
||||||
terminal.input_scroll(code.as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Home => {
|
|
||||||
let code = if is_app_cursor { b"\x1BOH" } else { b"\x1B[H" };
|
|
||||||
|
|
||||||
terminal.input_scroll(code.as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Insert => {
|
|
||||||
terminal.input_scroll(b"\x1B[2~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::Delete => {
|
|
||||||
terminal.input_scroll(b"\x1B[3~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::PageUp => {
|
|
||||||
terminal.input_scroll(b"\x1B[5~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::PageDown => {
|
|
||||||
terminal.input_scroll(b"\x1B[6~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F1 => {
|
|
||||||
terminal.input_scroll(b"\x1BOP".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F2 => {
|
|
||||||
terminal.input_scroll(b"\x1BOQ".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F3 => {
|
|
||||||
terminal.input_scroll(b"\x1BOR".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F4 => {
|
|
||||||
terminal.input_scroll(b"\x1BOS".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F5 => {
|
|
||||||
terminal.input_scroll(b"\x1B[15~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F6 => {
|
|
||||||
terminal.input_scroll(b"\x1B[17~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F7 => {
|
|
||||||
terminal.input_scroll(b"\x1B[18~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F8 => {
|
|
||||||
terminal.input_scroll(b"\x1B[19~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F9 => {
|
|
||||||
terminal.input_scroll(b"\x1B[20~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F10 => {
|
|
||||||
terminal.input_scroll(b"\x1B[21~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F11 => {
|
|
||||||
terminal.input_scroll(b"\x1B[23~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
KeyCode::F12 => {
|
|
||||||
terminal.input_scroll(b"\x1B[24~".as_slice());
|
|
||||||
status = Status::Captured;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||||
state.modifiers = modifiers;
|
state.modifiers = modifiers;
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent::CharacterReceived(character)) if state.is_focused => {
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
|
text,
|
||||||
|
modifiers,
|
||||||
|
key,
|
||||||
|
..
|
||||||
|
}) if state.is_focused => {
|
||||||
|
for (key_bind, _) in self.key_binds.iter() {
|
||||||
|
if key_bind.matches(modifiers, &key) {
|
||||||
|
return Status::Captured;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let character = text.and_then(|c| c.chars().next()).unwrap_or_default();
|
||||||
match (
|
match (
|
||||||
state.modifiers.logo(),
|
modifiers.logo(),
|
||||||
state.modifiers.control(),
|
modifiers.control(),
|
||||||
state.modifiers.alt(),
|
modifiers.alt(),
|
||||||
state.modifiers.shift(),
|
modifiers.shift(),
|
||||||
) {
|
) {
|
||||||
(true, _, _, _) => {
|
(true, _, _, _) => {
|
||||||
// Ignore super
|
// Ignore super
|
||||||
}
|
}
|
||||||
(false, true, true, false) => {
|
(false, true, true, _) => {
|
||||||
// Handle ctrl-alt for non-control characters
|
// Handle ctrl-alt for non-control characters
|
||||||
// Or should I try to minimize this to only
|
// and control characters 0-32
|
||||||
// catch control sequences that conflicts with
|
if !character.is_control() || (character as u32) < 32 {
|
||||||
// keykodes for Split
|
// Handle alt for non-control characters
|
||||||
// if character != '\u{4}' && character != '\u{12}' {
|
let mut buf = [0x1B, 0, 0, 0, 0];
|
||||||
// is there any valid case for control characters with modifers
|
let len = {
|
||||||
// ctrl-alt?
|
let str = character.encode_utf8(&mut buf[1..]);
|
||||||
if !character.is_control() {
|
str.len() + 1
|
||||||
let mut buf = [0, 0, 0, 0];
|
};
|
||||||
let str = character.encode_utf8(&mut buf);
|
terminal.input_scroll(buf[..len].to_vec());
|
||||||
terminal.input_scroll(str.as_bytes().to_vec());
|
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -990,7 +773,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(false, true, _, true) => {
|
(false, true, _, true) => {
|
||||||
// Ignore ctrl+shift
|
//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()) {
|
||||||
|
terminal.input_scroll(b"\x1F".as_slice());
|
||||||
|
status = Status::Captured;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(false, false, true, _) => {
|
(false, false, true, _) => {
|
||||||
if !character.is_control() {
|
if !character.is_control() {
|
||||||
|
|
@ -1029,6 +819,7 @@ where
|
||||||
state.is_focused = true;
|
state.is_focused = true;
|
||||||
|
|
||||||
// Handle left click drag
|
// Handle left click drag
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
if let Button::Left = button {
|
if let Button::Left = button {
|
||||||
let x = p.x - self.padding.left;
|
let x = p.x - self.padding.left;
|
||||||
let y = p.y - self.padding.top;
|
let y = p.y - self.padding.top;
|
||||||
|
|
@ -1264,7 +1055,7 @@ fn shade(color: cosmic_text::Color, is_focused: bool) -> cosmic_text::Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> From<TerminalBox<'a, Message>> for Element<'a, Message, Renderer>
|
impl<'a, Message> From<TerminalBox<'a, Message>> for Element<'a, Message, cosmic::Theme, Renderer>
|
||||||
where
|
where
|
||||||
Message: Clone + 'a,
|
Message: Clone + 'a,
|
||||||
{
|
{
|
||||||
|
|
@ -1323,3 +1114,52 @@ impl operation::Focusable for State {
|
||||||
self.is_focused = false;
|
self.is_focused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
shift 0b1 (1)
|
||||||
|
alt 0b10 (2)
|
||||||
|
ctrl 0b100 (4)
|
||||||
|
super 0b1000 (8)
|
||||||
|
hyper 0b10000 (16)
|
||||||
|
meta 0b100000 (32)
|
||||||
|
caps_lock 0b1000000 (64)
|
||||||
|
num_lock 0b10000000 (128)
|
||||||
|
*/
|
||||||
|
fn calculate_modifier_number(state: &mut State) -> u8 {
|
||||||
|
let mut mod_no = 0;
|
||||||
|
if state.modifiers.shift() {
|
||||||
|
mod_no |= 1;
|
||||||
|
}
|
||||||
|
if state.modifiers.alt() {
|
||||||
|
mod_no |= 2;
|
||||||
|
}
|
||||||
|
if state.modifiers.control() {
|
||||||
|
mod_no |= 4;
|
||||||
|
}
|
||||||
|
if state.modifiers.logo() {
|
||||||
|
mod_no |= 8;
|
||||||
|
}
|
||||||
|
mod_no + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn csi(code: &str, suffix: &str, modifiers: u8) -> Option<Vec<u8>> {
|
||||||
|
if modifiers == 1 {
|
||||||
|
Some(format!("\x1B[{}{}", code, suffix).as_bytes().to_vec())
|
||||||
|
} else {
|
||||||
|
Some(
|
||||||
|
format!("\x1B[{};{}{}", code, modifiers, suffix)
|
||||||
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn ss3(code: &str, modifiers: u8) -> Option<Vec<u8>> {
|
||||||
|
if modifiers == 1 {
|
||||||
|
Some(format!("\x1B\x4F{}", code).as_bytes().to_vec())
|
||||||
|
} else {
|
||||||
|
Some(format!("\x1B[1;{}{}", modifiers, code).as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -656,6 +656,166 @@ fn pop_dark() -> Colors {
|
||||||
colors
|
colors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn selenized_white() -> Colors {
|
||||||
|
let mut colors = auto_colors();
|
||||||
|
|
||||||
|
let encode_rgb = |data: u32| -> Rgb {
|
||||||
|
Rgb {
|
||||||
|
r: (data >> 16) as u8,
|
||||||
|
g: (data >> 8) as u8,
|
||||||
|
b: data as u8,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
colors[NamedColor::Black] = Some(encode_rgb(0xEBEBEB));
|
||||||
|
colors[NamedColor::Red] = Some(encode_rgb(0xD6000C));
|
||||||
|
colors[NamedColor::Green] = Some(encode_rgb(0x1D9700));
|
||||||
|
colors[NamedColor::Yellow] = Some(encode_rgb(0xC49700));
|
||||||
|
colors[NamedColor::Blue] = Some(encode_rgb(0x0064E4));
|
||||||
|
colors[NamedColor::Magenta] = Some(encode_rgb(0xDD0F9D));
|
||||||
|
colors[NamedColor::Cyan] = Some(encode_rgb(0x00AD9C));
|
||||||
|
colors[NamedColor::White] = Some(encode_rgb(0x878787));
|
||||||
|
|
||||||
|
colors[NamedColor::BrightBlack] = Some(encode_rgb(0xCDCDCD));
|
||||||
|
colors[NamedColor::BrightRed] = Some(encode_rgb(0xBF0000));
|
||||||
|
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x008400));
|
||||||
|
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xAF8500));
|
||||||
|
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x0054CF));
|
||||||
|
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xC7008B));
|
||||||
|
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x009A8A));
|
||||||
|
colors[NamedColor::BrightWhite] = Some(encode_rgb(0x282828));
|
||||||
|
|
||||||
|
// Set special colors
|
||||||
|
colors[NamedColor::Background] = Some(encode_rgb(0xFFFFFF));
|
||||||
|
colors[NamedColor::Foreground] = Some(encode_rgb(0x474747));
|
||||||
|
colors[NamedColor::Cursor] = colors[NamedColor::Black];
|
||||||
|
|
||||||
|
// Fill missing dim colors
|
||||||
|
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||||
|
|
||||||
|
colors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selenized_light() -> Colors {
|
||||||
|
let mut colors = auto_colors();
|
||||||
|
|
||||||
|
let encode_rgb = |data: u32| -> Rgb {
|
||||||
|
Rgb {
|
||||||
|
r: (data >> 16) as u8,
|
||||||
|
g: (data >> 8) as u8,
|
||||||
|
b: data as u8,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
colors[NamedColor::Black] = Some(encode_rgb(0xECE3CC));
|
||||||
|
colors[NamedColor::Red] = Some(encode_rgb(0xD2212D));
|
||||||
|
colors[NamedColor::Green] = Some(encode_rgb(0x489100));
|
||||||
|
colors[NamedColor::Yellow] = Some(encode_rgb(0xAD8900));
|
||||||
|
colors[NamedColor::Blue] = Some(encode_rgb(0x0072D4));
|
||||||
|
colors[NamedColor::Magenta] = Some(encode_rgb(0xCA4898));
|
||||||
|
colors[NamedColor::Cyan] = Some(encode_rgb(0x009C8F));
|
||||||
|
colors[NamedColor::White] = Some(encode_rgb(0x909995));
|
||||||
|
|
||||||
|
colors[NamedColor::BrightBlack] = Some(encode_rgb(0xD5CDB6));
|
||||||
|
colors[NamedColor::BrightRed] = Some(encode_rgb(0xCC1729));
|
||||||
|
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x428B00));
|
||||||
|
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xA78300));
|
||||||
|
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x006DCE));
|
||||||
|
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xC44392));
|
||||||
|
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x00978A));
|
||||||
|
colors[NamedColor::BrightWhite] = Some(encode_rgb(0x3A4D53));
|
||||||
|
|
||||||
|
// Set special colors
|
||||||
|
colors[NamedColor::Background] = Some(encode_rgb(0xFBF3DB));
|
||||||
|
colors[NamedColor::Foreground] = Some(encode_rgb(0x53676D));
|
||||||
|
colors[NamedColor::Cursor] = colors[NamedColor::Black];
|
||||||
|
|
||||||
|
// Fill missing dim colors
|
||||||
|
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||||
|
|
||||||
|
colors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selenized_dark() -> Colors {
|
||||||
|
let mut colors = auto_colors();
|
||||||
|
|
||||||
|
let encode_rgb = |data: u32| -> Rgb {
|
||||||
|
Rgb {
|
||||||
|
r: (data >> 16) as u8,
|
||||||
|
g: (data >> 8) as u8,
|
||||||
|
b: data as u8,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
colors[NamedColor::Black] = Some(encode_rgb(0x184956));
|
||||||
|
colors[NamedColor::Red] = Some(encode_rgb(0xFA5750));
|
||||||
|
colors[NamedColor::Green] = Some(encode_rgb(0x75B938));
|
||||||
|
colors[NamedColor::Yellow] = Some(encode_rgb(0xDBB32D));
|
||||||
|
colors[NamedColor::Blue] = Some(encode_rgb(0x4695F7));
|
||||||
|
colors[NamedColor::Magenta] = Some(encode_rgb(0xF275BE));
|
||||||
|
colors[NamedColor::Cyan] = Some(encode_rgb(0x41C7B9));
|
||||||
|
colors[NamedColor::White] = Some(encode_rgb(0x72898F));
|
||||||
|
|
||||||
|
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x2D5B69));
|
||||||
|
colors[NamedColor::BrightRed] = Some(encode_rgb(0xFF665C));
|
||||||
|
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x84C747));
|
||||||
|
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xEBC13D));
|
||||||
|
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x58A3FF));
|
||||||
|
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xFF84CD));
|
||||||
|
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x53D6C7));
|
||||||
|
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xCAD8D9));
|
||||||
|
|
||||||
|
// Set special colors
|
||||||
|
colors[NamedColor::Background] = Some(encode_rgb(0x103C48));
|
||||||
|
colors[NamedColor::Foreground] = Some(encode_rgb(0xADBCBC));
|
||||||
|
colors[NamedColor::Cursor] = colors[NamedColor::White];
|
||||||
|
|
||||||
|
// Fill missing dim colors
|
||||||
|
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||||
|
|
||||||
|
colors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selenized_black() -> Colors {
|
||||||
|
let mut colors = auto_colors();
|
||||||
|
|
||||||
|
let encode_rgb = |data: u32| -> Rgb {
|
||||||
|
Rgb {
|
||||||
|
r: (data >> 16) as u8,
|
||||||
|
g: (data >> 8) as u8,
|
||||||
|
b: data as u8,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
colors[NamedColor::Black] = Some(encode_rgb(0x252525));
|
||||||
|
colors[NamedColor::Red] = Some(encode_rgb(0xED4A46));
|
||||||
|
colors[NamedColor::Green] = Some(encode_rgb(0x70B433));
|
||||||
|
colors[NamedColor::Yellow] = Some(encode_rgb(0xDBB32D));
|
||||||
|
colors[NamedColor::Blue] = Some(encode_rgb(0x368AEB));
|
||||||
|
colors[NamedColor::Magenta] = Some(encode_rgb(0xEB6EB7));
|
||||||
|
colors[NamedColor::Cyan] = Some(encode_rgb(0x3FC5B7));
|
||||||
|
colors[NamedColor::White] = Some(encode_rgb(0x777777));
|
||||||
|
|
||||||
|
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x3B3B3B));
|
||||||
|
colors[NamedColor::BrightRed] = Some(encode_rgb(0xFF5E56));
|
||||||
|
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x83C746));
|
||||||
|
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xEFC541));
|
||||||
|
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x4F9CFE));
|
||||||
|
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xFF81CA));
|
||||||
|
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x56D8C9));
|
||||||
|
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xDEDEDE));
|
||||||
|
|
||||||
|
// Set special colors
|
||||||
|
colors[NamedColor::Background] = Some(encode_rgb(0x181818));
|
||||||
|
colors[NamedColor::Foreground] = Some(encode_rgb(0xB9B9B9));
|
||||||
|
colors[NamedColor::Cursor] = colors[NamedColor::White];
|
||||||
|
|
||||||
|
// Fill missing dim colors
|
||||||
|
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||||
|
|
||||||
|
colors
|
||||||
|
}
|
||||||
|
|
||||||
pub fn terminal_themes() -> HashMap<String, Colors> {
|
pub fn terminal_themes() -> HashMap<String, Colors> {
|
||||||
let mut themes = HashMap::new();
|
let mut themes = HashMap::new();
|
||||||
themes.insert("Tango Dark".to_string(), tango_dark());
|
themes.insert("Tango Dark".to_string(), tango_dark());
|
||||||
|
|
@ -672,5 +832,9 @@ pub fn terminal_themes() -> HashMap<String, Colors> {
|
||||||
themes.insert("gruvbox-dark".to_string(), gruvbox_dark());
|
themes.insert("gruvbox-dark".to_string(), gruvbox_dark());
|
||||||
themes.insert("OneHalfDark".to_string(), one_half_dark());
|
themes.insert("OneHalfDark".to_string(), one_half_dark());
|
||||||
themes.insert("Pop Dark".to_string(), pop_dark());
|
themes.insert("Pop Dark".to_string(), pop_dark());
|
||||||
|
themes.insert("Selenized Black".to_string(), selenized_black());
|
||||||
|
themes.insert("Selenized Dark".to_string(), selenized_dark());
|
||||||
|
themes.insert("Selenized Light".to_string(), selenized_light());
|
||||||
|
themes.insert("Selenized White".to_string(), selenized_white());
|
||||||
themes
|
themes
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue