Compare commits

...

9 commits

Author SHA1 Message Date
81dc7d3eeb chore: fige Cargo.lock (epoch-1.9.0, [patch] locaux)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 21:03:54 +02:00
083e09f97e merge: epoch-1.9.0 d'upstream
# Conflicts:
#	Cargo.lock
#	src/main.rs
#	src/menu.rs
2026-09-24 20:52:15 +02:00
Hojjat
9129277bed chore: update libcosmic to fix esc handling in menus 2026-09-23 11:55:55 -06:00
Hojjat
78eaba9db1 chore: tweak context menu items order and wording 2026-09-15 19:09:43 -04:00
Michael Aaron Murphy
d25c2108c0 chore: update cosmic-files 2026-09-15 19:09:43 -04:00
Hojjat
120b4fdb12 feat: use libcosmic's context_menu widget for the terminal context menu 2026-09-15 19:09:43 -04:00
Michael Aaron Murphy
4cd8f8d605 Epoch 1.9.0 version update
Generated by cosmic-epoch scripts/version-update.sh
2026-09-15 19:09:43 -04:00
Michael Murphy
1d9e5784d2
i18n: translation update from Hosted Weblate (#908)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pop OS/COSMIC
Term](https://hosted.weblate.org/projects/pop-os/cosmic-term/).



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pop-os/cosmic-term/matrix-auto.svg)
2026-09-15 21:57:44 +02:00
Hosted Weblate
327febcc92
i18n: translation updates from weblate
Co-authored-by: Amadɣas <massiin@proton.me>
Co-authored-by: Blauen <butterflee555@gmail.com>
Co-authored-by: CYAXXX <85353920+CYAXXX@users.noreply.github.com>
Co-authored-by: Geeson Wan <wang14240@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kurt Harren Catindig <kurtharrencatindig03@gmail.com>
Co-authored-by: Quentin PAGÈS <quentinantonin@free.fr>
Co-authored-by: Satakun Utama <galaxytomostore@gmail.com>
Co-authored-by: VandaL <vandalhj@gmail.com>
Co-authored-by: Zahid Rizky Fakhri <zahidrizkyfakhri@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/id/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/kab/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/kmr/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/oc/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/pl/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/th/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-term/zh_Hant/
Translation: Pop OS/COSMIC Term
2026-09-13 01:56:16 +02:00
16 changed files with 370 additions and 642 deletions

501
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-term"
version = "1.8.0"
version = "1.9.0"
authors = ["Jeremy Soller <jeremy@system76.com>"]
edition = "2024"
license = "GPL-3.0-only"

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
cosmic-term (1.9.0) noble; urgency=medium
* Epoch 1.9.0 version update
-- Michael Murphy <michael@mmurphy.dev> Mon, 14 Sep 2026 19:50:47 +0200
cosmic-term (1.8.0) noble; urgency=medium
* Epoch 1.8.0 version update

View file

@ -1,4 +1,6 @@
tar-ignore=.github
tar-ignore=.vscode
tar-ignore=target/.rustc_info.json
tar-ignore=target/CACHEDIR.TAG
tar-ignore=target/debug
tar-ignore=target/release
tar-ignore=vendor
tar-ignore=target

View file

@ -63,6 +63,7 @@ show-pane-borders = Show pane borders
### Advanced
advanced = Advanced
show-headerbar = Show header
window-header = Window header
show-header-description = Reveal the header from the right-click menu
tab-new-inherit-working-directory = New tabs and windows use current directory
tab-new-inherit-working-directory-description = Open new tabs and windows in the active tab's working directory
@ -152,7 +153,7 @@ next-tab = Next tab
previous-tab = Previous tab
split-horizontal = Split horizontal
split-vertical = Split vertical
pane-toggle-maximize = Toggle maximized
pane-toggle-maximize = Maximize or restore pane
menu-color-schemes = Color schemes...
menu-settings = Settings...
menu-about = About COSMIC Terminal...

0
i18n/fil/cosmic_term.ftl Normal file
View file

View file

@ -4,7 +4,7 @@ repository = Repositori
support = Dukungan
color-schemes = Skema warna
cosmic-terminal = Terminal COSMIC
rename = Ganti nama
rename = Ganti Nama
export = Ekspor
delete = Hapus
import = Impor

View file

@ -1,9 +1,9 @@
cancel = Sefsex
cancel = Semmet
name = Isem
delete = kkes
rename = Snifel isem
quit = Tuffɣa
support = Tallalt
quit = Ffeɣ
support = Tallelt
repository = Asarsay
settings = Iɣewwaṛen
appearance = Timeẓri
@ -14,7 +14,7 @@ paste = Senteḍ
select-all = Fren akk
view = Wali
menu-settings = Iɣewwaṛen…
match-desktop = Amṣada d tnarit
match-desktop = Semṣada akked tnarit
file = Afaylu
new-window = Asfaylu amaynut
edit = Ẓreg

View file

@ -27,7 +27,7 @@ light = Ronî
advanced = Pêşketî
support = Piştgirî
default-font = Cûrenivîs
paste = Pêve bike
paste = Pê ve bike
menu-settings = Sazkarî...
command-line = Rêza fermanê
view = Dîtin
@ -100,7 +100,7 @@ focus-pane-left = Balê bikişîne ser destgeha çepê
focus-pane-right = Balê bikişîne ser destgeha rastê
focus-pane-up = Balê bikişîne ser destgeha jorîn
password-manager = Rêveberê borînpeyvê
paste-primary = A sereke pêve bike
paste-primary = A sereke pê ve bike
reset-to-default = Vegere rewşa berdest
shortcut-capture-hint = Kombînasyona kilîtan bitikîne
shortcut-replace-body = { $binding } berê ji bo { $existing } hatiye destnîşankirin. Tu dixwazî bi { $new_action } re biguherînî?

View file

@ -1,7 +1,14 @@
settings = Paramètres
settings = Configuracion
appearance = Aparéncia
theme = Tèma
match-desktop = Assortir al burèu
dark = Fosc
dark = Escur
light = Clar
file = Fichièr
name = Nom
repository = Depaus
cancel = Anullar
menu-settings = Configuracion...
quit = Quitar
delete = Suprimir
replace = Remplaçar

View file

@ -1,5 +1,5 @@
cosmic-terminal = Terminal COSMIC
comment = Emulator terminala dla pulpitu COSMIC
comment = Emulator terminala dla środowiska COSMIC
keywords = Cli;WierszPoleceń;Powłoka;Terminal;
new-terminal = Nowy terminal
@ -73,7 +73,7 @@ advanced = Zaawansowane
show-headerbar = Pokaż pasek nagłówka
show-header-description = Ukazuje pasek nagłówka z menu wybieranego prawym przyciskiem myszki
# Find
find-placeholder = Szukaj…
find-placeholder = Wyszukaj…
find-previous = Znajdź poprzedni
find-next = Znajdź następny
@ -96,7 +96,7 @@ edit = Edytuj
copy = Kopiuj
paste = Wklej
select-all = Zaznacz wszystko
find = Szukaj
find = Wyszukaj
clear-scrollback = Wyczyść przewijanie
## View

View file

@ -39,8 +39,8 @@ settings = การตั้งค่า
appearance = ลักษณะ
theme = ธีม
match-desktop = ใช้ตามธีมหน้าจอหลัก
dark = ธีมมืด
light = ธีมสว่าง
dark = มืด
light = สว่าง
syntax-dark = โทนสีมืด
syntax-light = โทนสีสว่าง
default-zoom-step = ระยะซูม
@ -109,3 +109,8 @@ pane-toggle-maximize = ขยายให้ใหญ่สุด
menu-color-schemes = โทนสี...
menu-settings = การตั้งค่า...
menu-about = เกี่ยวกับ เทอร์มินัล COSMIC...
cancel = ยกเลิก
reset-to-default = รีเซ็ตเป็นค่าเริ่มต้น
type-to-search = พิมพ์เพื่อค้นหา
add-another-keybinding = เพิ่มคีย์ลัดเพิ่มเติม
replace = แทนที่

View file

@ -10,7 +10,7 @@ new-terminal = 新建終端機
## Color schemes
color-schemes = 配色方案
rename = 重命名
rename = 重新命名
export = 匯出
delete = 刪除
import = 匯入

View file

@ -566,7 +566,7 @@ pub enum Message {
ReorderTab(Pane, ReorderEvent),
SaveOutput(Option<segmented_button::Entity>),
SaveOutputResult(pane_grid::Pane, segmented_button::Entity, DialogResult),
Surface(surface::Action),
Surface(surface::Action<Message>),
SelectAll(Option<segmented_button::Entity>),
ShowAdvancedFontSettings(bool),
ShowHeaderBar(bool),
@ -604,7 +604,6 @@ pub enum Message {
ZoomIn,
ZoomOut,
ZoomReset,
ContextMenuPopupClosed(window::Id),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@ -682,14 +681,6 @@ pub struct App {
shortcut_search_regex: Option<regex::Regex>,
shortcut_search_value: String,
modifiers: Modifiers,
context_menu_popup: Option<(
window::Id,
pane_grid::Pane,
segmented_button::Entity,
Option<String>,
widget::Id,
cosmic::iced::Point,
)>,
#[cfg(feature = "password_manager")]
password_mgr: password_manager::PasswordManager,
}
@ -2213,7 +2204,6 @@ impl Application for App {
shortcut_search_regex: None,
shortcut_search_value: String::new(),
modifiers: Modifiers::empty(),
context_menu_popup: None,
#[cfg(feature = "password_manager")]
password_mgr: Default::default(),
};
@ -3433,16 +3423,7 @@ impl Application for App {
return self.update_title(None);
}
Message::TabContextAction(entity, action) => {
// Close context menu popup
let mut tasks = Vec::new();
if let Some((_popup_id, _, _, _, _, _)) = self.context_menu_popup.take() {
#[cfg(feature = "wayland")]
if is_wayland() {
tasks.push(cosmic::task::message(Message::Surface(
cosmic::surface::action::destroy_popup(_popup_id),
)));
}
}
// Close terminal context menu state
if let Some(tab_model) = self.pane_model.active()
&& let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity)
@ -3451,11 +3432,8 @@ impl Application for App {
//Some actions need the menu_state,
//so only clear the position for them.
match action {
Action::LaunchUrlByMenu | Action::CopyUrlByMenu => {
if let Some(context_menu) = terminal.context_menu.as_mut() {
context_menu.position = None;
}
}
// these read the link from the menu state and clear it themselves
Action::LaunchUrlByMenu | Action::CopyUrlByMenu => {}
_ => {
terminal.context_menu = None;
}
@ -3465,94 +3443,29 @@ impl Application for App {
return cosmic::Task::batch(tasks);
}
Message::TabContextMenu(pane, menu_state) => {
#[allow(unused_mut)]
let mut tasks = Vec::new();
// Close existing context menu popup if any
if let Some((_popup_id, _, _, _, _, _)) = self.context_menu_popup.take() {
#[cfg(feature = "wayland")]
if is_wayland() {
tasks.push(cosmic::task::message(Message::Surface(
cosmic::surface::action::destroy_popup(_popup_id),
)));
}
}
// Clear all terminal context_menu state
for (_, tab_model) in self.pane_model.panes.iter() {
for entity in tab_model.iter() {
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
let mut terminal = terminal.lock().unwrap();
terminal.context_menu = None;
if terminal.context_menu.take().is_some() {
terminal.active_regex_match = None;
terminal.needs_update = true;
}
}
}
}
if let Some(menu_state) = menu_state {
if let Some(_position) = menu_state.position {
let local_position = menu_state.local_position.unwrap_or(_position);
if let Some(tab_model) = self.pane_model.panes.get(pane) {
let entity = tab_model.active();
let link = menu_state.link.clone();
let popup_id = window::Id::unique();
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
let mut terminal = terminal.lock().unwrap();
terminal.context_menu = Some(menu_state);
}
self.context_menu_popup = Some((
popup_id,
pane,
entity,
link,
widget::Id::unique(),
local_position,
));
#[cfg(feature = "wayland")]
if is_wayland() {
let main_window = self.core.main_window_id().unwrap();
let pos_x = _position.x as i32;
let pos_y = _position.y as i32;
tasks.push(cosmic::task::message(Message::Surface(
cosmic::surface::action::app_popup(
|_| Default::default(),
move |_app: &mut Self| {
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
use cosmic::iced::runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
SctkPopupSettings {
parent: main_window,
id: popup_id,
positioner: SctkPositioner {
size: None,
anchor_rect: cosmic::iced::Rectangle {
x: pos_x,
y: pos_y,
width: 1,
height: 1,
},
anchor: Anchor::None,
gravity: Gravity::BottomRight,
reactive: true,
..Default::default()
},
parent_size: None,
grab: true,
close_with_children: false,
input_zone: None,
}
}, None),
)));
}
}
// A right press records what is under the cursor
if let Some(menu_state) = menu_state
&& let Some(tab_model) = self.pane_model.panes.get(pane)
{
let entity = tab_model.active();
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
terminal.lock().unwrap().context_menu = Some(menu_state);
}
self.pane_model.set_focus(pane);
}
return cosmic::Task::batch(tasks);
}
Message::TabNew => {
// The profile from `--profile` only applies to the first tab
@ -3944,26 +3857,8 @@ impl Application for App {
self.reset_active_pane_zoom();
return self.update_config();
}
Message::ContextMenuPopupClosed(id) => {
if let Some((popup_id, pane, entity, _, _, _)) = &self.context_menu_popup
&& id == *popup_id
{
// Clear link underline on the terminal
if let Some(tab_model) = self.pane_model.panes.get(*pane)
&& let Some(terminal) = tab_model.data::<Mutex<Terminal>>(*entity)
{
let mut terminal = terminal.lock().unwrap();
terminal.context_menu = None;
terminal.active_regex_match = None;
terminal.needs_update = true;
}
self.context_menu_popup = None;
}
}
Message::Surface(a) => {
return cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(a),
));
return cosmic::task::message(cosmic::Action::Surface(a));
}
Message::ReorderTab(
pane,
@ -4112,32 +4007,7 @@ impl Application for App {
]
}
fn on_close_requested(&self, id: window::Id) -> Option<Self::Message> {
if let Some((popup_id, _, _, _, _, _)) = &self.context_menu_popup
&& id == *popup_id
{
return Some(Message::ContextMenuPopupClosed(id));
}
None
}
fn view_window(&self, window_id: window::Id) -> Element<'_, Message> {
if let Some((popup_id, pane, entity, ref link, ref autosize_id, _)) =
self.context_menu_popup
&& window_id == popup_id
{
return widget::autosize::autosize(
menu::context_menu(
&self.config,
&self.key_binds,
entity,
link.clone(),
self.terminal_monitors(pane, entity),
),
autosize_id.clone(),
)
.into();
}
match &self.dialog_opt {
Some(dialog) => dialog.view(window_id),
None => widget::text("Unknown window ID").into(),
@ -4226,51 +4096,36 @@ impl Application for App {
terminal_box = terminal_box.on_mouse_enter(move || Message::MouseEnter(pane));
}
// If a context menu popup is active for this pane, inform the
// terminal_box so it will emit on_context_menu(None) on click
// to dismiss the popup.
if self.context_menu_popup.is_some() {
terminal_box = terminal_box.context_menu(cosmic::iced::Point::ORIGIN);
// The terminal records what was under the right press; the widget opens the
// menu on release, so build it from that state
let context_link = terminal
.lock()
.unwrap()
.context_menu
.as_ref()
.map(|menu_state| menu_state.link.clone());
if context_link.is_some() {
terminal_box = terminal_box.context_menu_open(true);
}
let use_wayland_popup = {
#[cfg(feature = "wayland")]
{
is_wayland()
}
#[cfg(not(feature = "wayland"))]
{
false
}
};
let tab_element: Element<'_, Message> = if !use_wayland_popup {
// Fallback: render context menu as an inline popover
if let Some((_, popup_pane, popup_entity, ref link, _, point)) =
self.context_menu_popup
{
if pane == popup_pane {
let mut popover = widget::popover(terminal_box.context_menu(point));
popover = popover
.popup(menu::context_menu(
&self.config,
&self.key_binds,
popup_entity,
link.clone(),
self.terminal_monitors(popup_pane, popup_entity),
))
.position(widget::popover::Position::Point(point));
popover.into()
} else {
terminal_box.into()
}
} else {
terminal_box.into()
}
} else {
terminal_box.into()
};
tab_column = tab_column.push(tab_element);
let mut context_menu = widget::context_menu(
terminal_box,
context_link.map(|link| {
menu::context_menu(
&self.config,
&self.key_binds,
entity,
link,
self.terminal_monitors(pane, entity),
)
}),
)
.item_width(widget::menu::ItemWidth::Uniform(360))
.on_close(Message::TabContextMenu(pane, None))
.on_surface_action(Message::Surface);
if let Some(window_id) = self.core.main_window_id() {
context_menu = context_menu.window_id(window_id);
}
tab_column = tab_column.push(context_menu);
}
//Only draw find in the currently focused pane
@ -4451,14 +4306,6 @@ impl Application for App {
}
}
#[cfg(feature = "wayland")]
fn is_wayland() -> bool {
matches!(
cosmic::app::cosmic::windowing_system(),
Some(cosmic::app::cosmic::WindowingSystem::Wayland)
)
}
/// Divider color painted behind the pane grid to form pane borders.
///
/// When blur is active the transparent container must be used, otherwise the

View file

@ -1,17 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::iced::Point;
use cosmic::widget::menu::action::MenuAction;
use cosmic::widget::menu::key_bind::KeyBind;
use cosmic::widget::menu::{Item as MenuItem, menu_button};
use cosmic::widget::space;
use cosmic::{
Element,
app::Core,
iced::core::Border,
iced::{Background, Length, advanced::widget::text::Style as TextStyle},
theme,
iced::{Background, Length},
widget::{
self, divider,
self,
menu::{ItemHeight, ItemWidth},
responsive_menu_bar, segmented_button,
},
@ -23,147 +21,92 @@ use crate::{Action, ColorSchemeId, ColorSchemeKind, Config, Message, fl, termina
static MENU_ID: LazyLock<cosmic::widget::Id> =
LazyLock::new(|| cosmic::widget::Id::new("responsive-menu"));
/// What the terminal found under the right-click
#[derive(Debug, Clone)]
pub struct MenuState {
pub position: Option<Point>,
pub local_position: Option<Point>,
pub link: Option<String>,
}
pub fn context_menu<'a>(
/// A context menu action dispatched to the tab the menu was opened on.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TabAction(pub segmented_button::Entity, pub Action);
impl MenuAction for TabAction {
type Message = Message;
fn message(&self) -> Message {
Message::TabContextAction(self.0, self.1)
}
}
pub fn context_menu(
config: &Config,
key_binds: &HashMap<KeyBind, Action>,
entity: segmented_button::Entity,
link: Option<String>,
monitors: (bool, bool, bool),
) -> Element<'a, Message> {
let find_key = |action: &Action| -> String {
for (key_bind, key_action) in key_binds {
if action == key_action {
return key_bind.to_string();
}
}
String::new()
};
fn key_style(theme: &cosmic::Theme) -> TextStyle {
let mut color = theme.cosmic().background(theme.transparent).component.on;
color.alpha *= 0.75;
TextStyle {
color: Some(color.into()),
..Default::default()
}
}
let menu_item = |label, action| {
let key = find_key(&action);
menu_button(vec![
widget::text(label).into(),
space::horizontal().into(),
widget::text(key)
.class(theme::Text::Custom(key_style))
.into(),
])
.on_press(Message::TabContextAction(entity, action))
) -> Vec<widget::menu::Tree<Message>> {
let item =
|label: String, action: Action| MenuItem::Button(label, None, TabAction(entity, action));
let check = |label: String, value: bool, action: Action| {
MenuItem::CheckBox(label, None, value, TabAction(entity, action))
};
let menu_checkbox = |label, value, action| {
menu_button(vec![
widget::text(label).into(),
widget::space::horizontal().into(),
widget::toggler(value)
.on_toggle(move |_| Message::TabContextAction(entity, action))
.size(16.0)
.into(),
])
.on_press(Message::TabContextAction(entity, action))
};
let mut rows = vec![
Element::from(menu_item(fl!("copy"), Action::Copy)),
Element::from(menu_item(fl!("paste"), Action::Paste)),
Element::from(menu_item(fl!("select-all"), Action::SelectAll)),
Element::from(divider::horizontal::light()),
Element::from(menu_item(fl!("clear-scrollback"), Action::ClearScrollback)),
Element::from(menu_item(fl!("save-output"), Action::SaveOutput)),
Element::from(divider::horizontal::light()),
Element::from(menu_checkbox(
let mut items = vec![
item(fl!("new-tab"), Action::TabNew),
item(fl!("rename-tab"), Action::TabRename),
MenuItem::Divider,
item(fl!("copy"), Action::Copy),
item(fl!("paste"), Action::Paste),
item(fl!("select-all"), Action::SelectAll),
MenuItem::Divider,
item(fl!("clear-scrollback"), Action::ClearScrollback),
item(fl!("save-output"), Action::SaveOutput),
MenuItem::Divider,
check(
fl!("monitor-activity"),
monitors.0,
Action::Monitor(MonitorKind::Activity),
)),
Element::from(menu_checkbox(
),
check(
fl!("monitor-silence"),
monitors.1,
Action::Monitor(MonitorKind::Silence),
)),
Element::from(menu_checkbox(
),
check(
fl!("monitor-process-finished"),
monitors.2,
Action::Monitor(MonitorKind::ProcessFinished),
)),
Element::from(divider::horizontal::light()),
Element::from(menu_item(
fl!("split-horizontal"),
Action::PaneSplitHorizontal,
)),
Element::from(menu_item(fl!("split-vertical"), Action::PaneSplitVertical)),
Element::from(menu_item(
fl!("pane-toggle-maximize"),
Action::PaneToggleMaximized,
)),
Element::from(divider::horizontal::light()),
Element::from(menu_item(fl!("new-tab"), Action::TabNew)),
Element::from(menu_item(fl!("rename-tab"), Action::TabRename)),
Element::from(menu_item(fl!("menu-settings"), Action::Settings)),
Element::from(menu_item(fl!("menu-about"), Action::About)),
),
MenuItem::Divider,
item(fl!("split-horizontal"), Action::PaneSplitHorizontal),
item(fl!("split-vertical"), Action::PaneSplitVertical),
item(fl!("pane-toggle-maximize"), Action::PaneToggleMaximized),
MenuItem::Divider,
MenuItem::CheckBox(
fl!("window-header"),
None,
config.show_headerbar,
TabAction(entity, Action::ShowHeaderBar(!config.show_headerbar)),
),
MenuItem::Divider,
];
#[cfg(feature = "password_manager")]
{
rows.push(Element::from(menu_item(
fl!("menu-password-manager"),
Action::PasswordManager,
)));
}
rows.push(Element::from(menu_checkbox(
fl!("show-headerbar"),
config.show_headerbar,
Action::ShowHeaderBar(!config.show_headerbar),
)));
//If we have a link
//prepend the Open Link item
items.push(item(fl!("menu-password-manager"), Action::PasswordManager));
items.push(item(fl!("menu-settings"), Action::Settings));
items.push(item(fl!("menu-about"), Action::About));
// If we have a link, prepend the link items
if link.is_some() {
rows.insert(
0,
Element::from(menu_item(fl!("open-link"), Action::LaunchUrlByMenu)),
);
rows.insert(
1,
Element::from(menu_item(fl!("copy-link"), Action::CopyUrlByMenu)),
);
rows.insert(2, Element::from(divider::horizontal::light()));
items.insert(0, item(fl!("open-link"), Action::LaunchUrlByMenu));
items.insert(1, item(fl!("copy-link"), Action::CopyUrlByMenu));
items.insert(2, MenuItem::Divider);
}
let content = widget::menu::menu_column::MenuColumn::with_children(rows);
widget::container(content)
.padding(1)
//TODO: move style to libcosmic
.style(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background(theme.transparent).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())),
border: Border {
radius: cosmic.radius_s().map(|x| x + 1.0).into(),
width: 1.0,
color: component.divider.into(),
},
..Default::default()
}
})
.width(Length::Fixed(360.0))
.into()
let key_binds: HashMap<KeyBind, TabAction> = key_binds
.iter()
.map(|(key_bind, action)| (key_bind.clone(), TabAction(entity, *action)))
.collect();
widget::menu::items(&key_binds, items)
}
pub fn color_scheme_menu<'a>(

View file

@ -116,7 +116,7 @@ pub struct TerminalBox<'a, Message> {
show_headerbar: bool,
pane_border_radius: Option<Radius>,
click_timing: Duration,
context_menu: Option<Point>,
context_menu_open: bool,
on_context_menu: Option<Box<dyn Fn(Option<MenuState>) -> Message + 'a>>,
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
opacity: Option<f32>,
@ -143,7 +143,7 @@ where
show_headerbar: true,
pane_border_radius: None,
click_timing: Duration::from_millis(500),
context_menu: None,
context_menu_open: false,
on_context_menu: None,
on_mouse_enter: None,
opacity: None,
@ -188,8 +188,8 @@ where
self
}
pub fn context_menu(mut self, position: Point) -> Self {
self.context_menu = Some(position);
pub fn context_menu_open(mut self, open: bool) -> Self {
self.context_menu_open = open;
self
}
@ -253,7 +253,7 @@ where
if !state.is_focused {
return InputMethod::Disabled;
}
if self.context_menu.is_some() {
if self.context_menu_open {
return InputMethod::Disabled;
}
@ -1392,11 +1392,11 @@ where
}
// Update context menu state
if let Some(on_context_menu) = &self.on_context_menu {
match self.context_menu {
Some(_) => {
match self.context_menu_open {
true => {
shell.publish(on_context_menu(None));
}
None => {
false => {
if *button == Button::Right {
let x = p.x - self.padding.left;
let y = p.y - self.padding.top;
@ -1414,15 +1414,7 @@ where
None,
);
let link = get_hyperlink(&terminal, location);
let abs = cosmic::iced::Point::new(
layout.bounds().x + p.x,
layout.bounds().y + p.y,
);
shell.publish(on_context_menu(Some(MenuState {
position: Some(abs),
local_position: Some(p),
link,
})));
shell.publish(on_context_menu(Some(MenuState { link })));
}
}
}