Compare commits
No commits in common. "936e57b63eeeee0ac16b3a4e6ae6e78179e352b5" and "85da7f90f2a562ea8c8d526b61463f034167bc0d" have entirely different histories.
936e57b63e
...
85da7f90f2
16 changed files with 426 additions and 388 deletions
581
Cargo.lock
generated
581
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "cosmic-edit"
|
||||
version = "1.9.0"
|
||||
version = "1.8.0"
|
||||
authors = ["Jeremy Soller <jeremy@system76.com>"]
|
||||
edition = "2024"
|
||||
license = "GPL-3.0-only"
|
||||
|
|
|
|||
6
debian/changelog
vendored
6
debian/changelog
vendored
|
|
@ -1,9 +1,3 @@
|
|||
cosmic-edit (1.9.0) noble; urgency=medium
|
||||
|
||||
* Epoch 1.9.0 version update
|
||||
|
||||
-- Michael Murphy <michael@mmurphy.dev> Mon, 14 Sep 2026 21:54:06 +0200
|
||||
|
||||
cosmic-edit (1.8.0) noble; urgency=medium
|
||||
|
||||
* Epoch 1.8.0 version update
|
||||
|
|
|
|||
5
debian/rules
vendored
5
debian/rules
vendored
|
|
@ -1,12 +1,15 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
export DESTDIR = debian/cosmic-edit
|
||||
export VENDOR ?= 1
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_clean:
|
||||
ischroot || just vendor
|
||||
if ! ischroot && test "${VENDOR}" = "1"; then \
|
||||
just vendor; \
|
||||
fi
|
||||
|
||||
override_dh_auto_build:
|
||||
just build-vendored
|
||||
|
|
|
|||
6
debian/source/options
vendored
6
debian/source/options
vendored
|
|
@ -1,6 +1,4 @@
|
|||
tar-ignore=.github
|
||||
tar-ignore=target/.rustc_info.json
|
||||
tar-ignore=target/CACHEDIR.TAG
|
||||
tar-ignore=target/debug
|
||||
tar-ignore=target/release
|
||||
tar-ignore=.vscode
|
||||
tar-ignore=vendor
|
||||
tar-ignore=target
|
||||
|
|
@ -60,7 +60,7 @@ default-zoom-step = Nagyítási mérték
|
|||
### Keyboard shortcuts
|
||||
|
||||
keyboard-shortcuts = Gyorsbillentyűk
|
||||
enable-vim-bindings = Vim-billentyűkombinációk engedélyezése
|
||||
enable-vim-bindings = Vim billentyűkombinációk engedélyezése
|
||||
# Find
|
||||
find-placeholder = Keresés…
|
||||
find-previous = Előző találat
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
cancel = Semmet
|
||||
cancel = Sefsex
|
||||
undo = Ssemmet
|
||||
save = Sekles
|
||||
quit = Ffeɣ
|
||||
quit = Tuffɣa
|
||||
replace = Semselsi
|
||||
support = Tallelt
|
||||
support = Tallalt
|
||||
settings = Iɣewwaṛen
|
||||
appearance = Timeẓri
|
||||
theme = Asentel
|
||||
|
|
@ -16,7 +16,7 @@ default-size = Tiddi tamezwert
|
|||
zoom-out = Asemẓi
|
||||
view = Wali
|
||||
menu-settings = Iɣewwaṛen…
|
||||
match-desktop = Semṣada akked tnarit
|
||||
match-desktop = Amṣada d tnarit
|
||||
file = Afaylu
|
||||
new-window = Asfaylu amaynut
|
||||
edit = Ẓreg
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ support = Piştgirî
|
|||
quit = Biqedîne
|
||||
find-placeholder = Bibîne...
|
||||
copy = Jê bigire
|
||||
paste = Pê ve bike
|
||||
paste = Pêve bike
|
||||
match-desktop = Bi sermaseyê re li hev bîne
|
||||
select-all = Hemûyan hilbijêre
|
||||
zoom-in = Nêzîk bike
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
settings = Configuracion
|
||||
settings = Paramètres
|
||||
appearance = Aparéncia
|
||||
theme = Tèma
|
||||
match-desktop = Assortir al burèu
|
||||
dark = Escur
|
||||
dark = Fosc
|
||||
light = Clar
|
||||
file = Fichièr
|
||||
close-file = Tampar lo fichièr
|
||||
cancel = Anullar
|
||||
repository = Depaus
|
||||
menu-settings = Configuracion...
|
||||
quit = Quitar
|
||||
replace = Remplaçar
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
cosmic-text-editor = Edytor Tekstu COSMIC
|
||||
comment = Edytor tekstu dla środowiska COSMIC
|
||||
comment = Edytor tekstu pulpitu COSMIC
|
||||
new-document = Nowy dokument
|
||||
open-project = Otwórz projekt
|
||||
todo = TODO
|
||||
|
|
@ -64,7 +64,7 @@ default-zoom-step = Stopniowanie powiększania
|
|||
keyboard-shortcuts = Skróty klawiszowe
|
||||
enable-vim-bindings = Włącz tryb Vim
|
||||
# Find
|
||||
find-placeholder = Wyszukaj…
|
||||
find-placeholder = Szukaj…
|
||||
find-previous = Znajdź poprzedni
|
||||
find-next = Znajdź następny
|
||||
replace-placeholder = Zastąp…
|
||||
|
|
@ -106,8 +106,8 @@ cut = Wytnij
|
|||
copy = Kopiuj
|
||||
paste = Wklej
|
||||
select-all = Zaznacz wszystko
|
||||
find = Wyszukaj
|
||||
find-in-project = Wyszukaj w projekcie…
|
||||
find = Szukaj
|
||||
find-in-project = Szukaj w projekcie…
|
||||
spell-check = Sprawdź ortografię…
|
||||
|
||||
## View
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
cancel = ยกเลิก
|
||||
settings = การตั้งค่า
|
||||
dark = มืด
|
||||
light = สว่าง
|
||||
replace = แทนที่
|
||||
appearance = ลักษณะ
|
||||
37
src/main.rs
37
src/main.rs
|
|
@ -11,7 +11,7 @@ use cosmic::{
|
|||
cosmic_theme, executor,
|
||||
font::Font,
|
||||
iced::{
|
||||
self, Alignment, Background, Color, Length, Limits, Subscription,
|
||||
self, Alignment, Background, Color, Length, Limits, Point, Subscription,
|
||||
advanced::graphics::text::font_system,
|
||||
clipboard, event,
|
||||
futures::{self, SinkExt},
|
||||
|
|
@ -403,7 +403,7 @@ pub enum Message {
|
|||
SaveAsResult(segmented_button::Entity, DialogResult),
|
||||
Scroll(f32),
|
||||
SelectAll,
|
||||
Surface(surface::Action<Message>),
|
||||
Surface(surface::Action),
|
||||
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
||||
SyntaxTheme(usize, bool),
|
||||
TabActivate(segmented_button::Entity),
|
||||
|
|
@ -412,7 +412,7 @@ pub enum Message {
|
|||
TabClose(segmented_button::Entity),
|
||||
TabCloseForce(segmented_button::Entity),
|
||||
TabContextAction(segmented_button::Entity, Action),
|
||||
TabContextMenu(segmented_button::Entity, bool),
|
||||
TabContextMenu(segmented_button::Entity, Option<Point>),
|
||||
TabNext,
|
||||
TabPrev,
|
||||
TabSetCursor(segmented_button::Entity, Cursor),
|
||||
|
|
@ -2713,7 +2713,9 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
Message::Surface(a) => {
|
||||
return cosmic::task::message(cosmic::Action::Surface(a));
|
||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
||||
cosmic::app::Action::Surface(a),
|
||||
));
|
||||
}
|
||||
Message::SystemThemeModeChange(_theme_mode) => {
|
||||
return self.update_config();
|
||||
|
|
@ -2821,15 +2823,15 @@ impl Application for App {
|
|||
Message::TabContextAction(entity, action) => {
|
||||
if let Some(Tab::Editor(tab)) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
// Close context menu
|
||||
tab.context_menu_open = false;
|
||||
tab.context_menu = None;
|
||||
// Run action's message
|
||||
return self.update(action.message(None));
|
||||
}
|
||||
}
|
||||
Message::TabContextMenu(entity, open) => {
|
||||
Message::TabContextMenu(entity, position_opt) => {
|
||||
if let Some(Tab::Editor(tab)) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
// Update context menu
|
||||
tab.context_menu_open = open;
|
||||
tab.context_menu = position_opt;
|
||||
}
|
||||
}
|
||||
Message::TabNext => {
|
||||
|
|
@ -3091,25 +3093,24 @@ impl Application for App {
|
|||
.on_focus(Message::FindFocused(false))
|
||||
.on_auto_scroll(Message::AutoScroll)
|
||||
.on_changed(Message::TabChanged(tab_id))
|
||||
.has_context_menu(tab.context_menu_open)
|
||||
.on_context_menu(move |open| Message::TabContextMenu(tab_id, open));
|
||||
.has_context_menu(tab.context_menu.is_some())
|
||||
.on_context_menu(move |position_opt| {
|
||||
Message::TabContextMenu(tab_id, position_opt)
|
||||
});
|
||||
if self.config.highlight_current_line {
|
||||
text_box = text_box.highlight_current_line();
|
||||
}
|
||||
if self.config.line_numbers {
|
||||
text_box = text_box.line_numbers();
|
||||
}
|
||||
let mut popover = widget::popover(text_box);
|
||||
let has_selection = tab.editor.lock().unwrap().selection() != Selection::None;
|
||||
let mut context_menu = widget::context_menu(
|
||||
text_box,
|
||||
Some(menu::context_menu(&self.key_binds, tab_id, has_selection)),
|
||||
)
|
||||
.on_close(Message::TabContextMenu(tab_id, false))
|
||||
.on_surface_action(Message::Surface);
|
||||
if let Some(window_id) = self.core().main_window_id() {
|
||||
context_menu = context_menu.window_id(window_id);
|
||||
if let Some(point) = tab.context_menu {
|
||||
popover = popover
|
||||
.popup(menu::context_menu(&self.key_binds, tab_id, has_selection))
|
||||
.position(widget::popover::Position::Point(point));
|
||||
}
|
||||
tab_column = tab_column.push(context_menu);
|
||||
tab_column = tab_column.push(popover);
|
||||
if self.config.vim_bindings {
|
||||
let status = {
|
||||
let editor = tab.editor.lock().unwrap();
|
||||
|
|
|
|||
115
src/menu.rs
115
src/menu.rs
|
|
@ -1,15 +1,18 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use cosmic::widget::menu::Item as MenuItem;
|
||||
use cosmic::widget::menu::action::MenuAction;
|
||||
use cosmic::widget::menu::key_bind::KeyBind;
|
||||
use cosmic::{
|
||||
Element,
|
||||
app::Core,
|
||||
iced::{
|
||||
Background, Border, Length, advanced::widget::text::Style as TextStyle, widget::column,
|
||||
},
|
||||
theme,
|
||||
widget::{
|
||||
self,
|
||||
menu::{ItemHeight, ItemWidth},
|
||||
responsive_menu_bar, segmented_button,
|
||||
self, divider,
|
||||
menu::{ItemHeight, ItemWidth, menu_button},
|
||||
responsive_menu_bar, segmented_button, space,
|
||||
},
|
||||
};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::LazyLock};
|
||||
|
|
@ -113,49 +116,81 @@ fn format_recent_menu_path(path: &PathBuf, home_dir_opt: Option<&PathBuf>) -> St
|
|||
truncate_middle(&display, RECENT_MENU_LABEL_MAX_CHARS)
|
||||
}
|
||||
|
||||
/// 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(
|
||||
pub fn context_menu<'a>(
|
||||
key_binds: &HashMap<KeyBind, Action>,
|
||||
entity: segmented_button::Entity,
|
||||
has_selection: bool,
|
||||
) -> Vec<widget::menu::Tree<Message>> {
|
||||
let item =
|
||||
|label: String, action: Action| MenuItem::Button(label, None, TabAction(entity, action));
|
||||
let optional = |label: String, action: Action, enabled: bool| {
|
||||
if enabled {
|
||||
MenuItem::Button(label, None, TabAction(entity, action))
|
||||
} else {
|
||||
MenuItem::ButtonDisabled(label, None, TabAction(entity, action))
|
||||
) -> Element<'a, Message> {
|
||||
fn key_style(theme: &cosmic::Theme) -> TextStyle {
|
||||
// TODO use wayland popups
|
||||
let mut color = theme.cosmic().background(false).component.on;
|
||||
color.alpha *= 0.75;
|
||||
TextStyle {
|
||||
color: Some(color.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
let base_menu_item = |menu_label, menu_action| {
|
||||
let mut key = String::new();
|
||||
for (key_bind, key_action) in key_binds.iter() {
|
||||
if key_action == &menu_action {
|
||||
key = key_bind.to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
menu_button(vec![
|
||||
widget::text(menu_label).into(),
|
||||
space::horizontal().into(),
|
||||
widget::text(key)
|
||||
.class(theme::Text::Custom(key_style))
|
||||
.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,
|
||||
vec![
|
||||
item(fl!("undo"), Action::Undo),
|
||||
item(fl!("redo"), Action::Redo),
|
||||
MenuItem::Divider,
|
||||
optional(fl!("cut"), Action::Cut, has_selection),
|
||||
optional(fl!("copy"), Action::Copy, has_selection),
|
||||
item(fl!("paste"), Action::Paste),
|
||||
item(fl!("select-all"), Action::SelectAll),
|
||||
],
|
||||
let optional_menu_item = |menu_label, menu_action, disabled| {
|
||||
base_menu_item(menu_label, menu_action).on_press_maybe(if disabled {
|
||||
None
|
||||
} else {
|
||||
Some(Message::TabContextAction(entity, menu_action))
|
||||
})
|
||||
};
|
||||
|
||||
let menu_item = |menu_label, menu_action| {
|
||||
base_menu_item(menu_label, menu_action)
|
||||
.on_press(Message::TabContextAction(entity, menu_action))
|
||||
};
|
||||
|
||||
widget::container(
|
||||
cosmic::widget::menu::menu_column::MenuColumn::with_children([
|
||||
menu_item(fl!("undo"), Action::Undo).into(),
|
||||
menu_item(fl!("redo"), Action::Redo).into(),
|
||||
divider::horizontal::light().into(),
|
||||
optional_menu_item(fl!("cut"), Action::Cut, !has_selection).into(),
|
||||
optional_menu_item(fl!("copy"), Action::Copy, !has_selection).into(),
|
||||
menu_item(fl!("paste"), Action::Paste).into(),
|
||||
menu_item(fl!("select-all"), Action::SelectAll).into(),
|
||||
]),
|
||||
)
|
||||
.padding(1)
|
||||
//TODO: move style to libcosmic
|
||||
.style(|theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
let component = &cosmic.background(false).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(240.0))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn menu_bar<'a>(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use cosmic::{iced::advanced::graphics::text::font_system, widget::icon};
|
||||
use cosmic::{
|
||||
iced::{Point, advanced::graphics::text::font_system},
|
||||
widget::icon,
|
||||
};
|
||||
use cosmic_files::mime_icon::{FALLBACK_MIME_ICON, mime_for_path, mime_icon};
|
||||
use cosmic_text::{Attrs, Buffer, Cursor, Edit, Selection, Shaping, SyntaxEditor, ViEditor, Wrap};
|
||||
use regex::Regex;
|
||||
|
|
@ -48,7 +51,7 @@ pub struct EditorTab {
|
|||
pub path_opt: Option<PathBuf>,
|
||||
attrs: Attrs<'static>,
|
||||
pub editor: Mutex<ViEditor<'static, 'static>>,
|
||||
pub context_menu_open: bool,
|
||||
pub context_menu: Option<Point>,
|
||||
pub zoom_adj: i8,
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +75,7 @@ impl EditorTab {
|
|||
path_opt: None,
|
||||
attrs,
|
||||
editor: Mutex::new(ViEditor::new(editor)),
|
||||
context_menu_open: false,
|
||||
context_menu: None,
|
||||
zoom_adj,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ pub struct TextBox<'a, Message> {
|
|||
on_focus: Option<Message>,
|
||||
click_timing: Duration,
|
||||
has_context_menu: bool,
|
||||
on_context_menu: Option<Box<dyn Fn(bool) -> Message + 'a>>,
|
||||
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
||||
highlight_current_line: bool,
|
||||
line_numbers: bool,
|
||||
}
|
||||
|
|
@ -107,7 +107,10 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn on_context_menu(mut self, on_context_menu: impl Fn(bool) -> Message + 'a) -> Self {
|
||||
pub fn on_context_menu(
|
||||
mut self,
|
||||
on_context_menu: impl Fn(Option<Point>) -> Message + 'a,
|
||||
) -> Self {
|
||||
self.on_context_menu = Some(Box::new(on_context_menu));
|
||||
self
|
||||
}
|
||||
|
|
@ -1264,9 +1267,14 @@ where
|
|||
|
||||
// Update context menu state
|
||||
if let Some(on_context_menu) = &self.on_context_menu {
|
||||
shell.publish((on_context_menu)(
|
||||
!self.has_context_menu && matches!(button, Button::Right),
|
||||
));
|
||||
shell.publish((on_context_menu)(if self.has_context_menu {
|
||||
None
|
||||
} else {
|
||||
match button {
|
||||
Button::Right => Some(p),
|
||||
_ => None,
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
shell.capture_event();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue