Update libcosmic/iced

This commit is contained in:
Jeremy Soller 2024-10-22 11:55:58 -06:00
parent bd80ac7e09
commit 5b0d05d2cb
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
6 changed files with 840 additions and 881 deletions

1335
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,6 @@ rust-embed = "8"
[dependencies.cosmic-files] [dependencies.cosmic-files]
git = "https://github.com/pop-os/cosmic-files.git" git = "https://github.com/pop-os/cosmic-files.git"
default-features = false default-features = false
features = ["winit"]
[dependencies.cosmic-syntax-theme] [dependencies.cosmic-syntax-theme]
git = "https://github.com/pop-os/cosmic-syntax-theme.git" git = "https://github.com/pop-os/cosmic-syntax-theme.git"
@ -63,11 +62,7 @@ wgpu = ["libcosmic/wgpu", "cosmic-files/wgpu"]
inherits = "release" inherits = "release"
debug = true debug = true
[patch.crates-io]
# https://github.com/alexcrichton/filetime/pull/104
filetime = { git = "https://github.com/jackpot51/filetime" }
# [patch.'https://github.com/pop-os/libcosmic'] # [patch.'https://github.com/pop-os/libcosmic']
# libcosmic = { path = "../libcosmic" } # libcosmic = { path = "../libcosmic" }
# cosmic-config = { path = "../libcosmic/cosmic-config" } # cosmic-config = { path = "../libcosmic/cosmic-config" }
# cosmic-theme = { path = "../libcosmic/cosmic-theme" } # cosmic-theme = { path = "../libcosmic/cosmic-theme" }

View file

@ -1,6 +1,5 @@
use cosmic_text::{ use cosmic_text::{
Align, Attrs, AttrsList, BufferLine, Family, FontSystem, LayoutLine, LineEnding, ShapeBuffer, Align, Attrs, AttrsList, BufferLine, Family, FontSystem, LayoutLine, LineEnding, Shaping, Wrap,
Shaping, Wrap,
}; };
use std::collections::HashMap; use std::collections::HashMap;

View file

@ -4,7 +4,7 @@ use cosmic::widget::menu::action::MenuAction;
use cosmic::widget::menu::key_bind::KeyBind; use cosmic::widget::menu::key_bind::KeyBind;
use cosmic::widget::segmented_button::Entity; use cosmic::widget::segmented_button::Entity;
use cosmic::{ use cosmic::{
app::{message, Command, Core, Settings}, app::{message, Core, Settings, Task},
cosmic_config::{self, CosmicConfigEntry}, cosmic_config::{self, CosmicConfigEntry},
cosmic_theme, executor, cosmic_theme, executor,
font::Font, font::Font,
@ -13,9 +13,7 @@ use cosmic::{
clipboard, event, clipboard, event,
futures::{self, SinkExt}, futures::{self, SinkExt},
keyboard::{self, Modifiers}, keyboard::{self, Modifiers},
subscription, stream, window, Alignment, Background, Color, Length, Limits, Point, Subscription,
widget::text,
window, Alignment, Background, Color, Length, Limits, Point,
}, },
style, theme, style, theme,
widget::{self, button, icon, nav_bar, segmented_button}, widget::{self, button, icon, nav_bar, segmented_button},
@ -322,6 +320,7 @@ pub enum Message {
ConfigState(ConfigState), ConfigState(ConfigState),
CloseFile, CloseFile,
CloseProject(usize), CloseProject(usize),
CloseWindow(window::Id),
Copy, Copy,
Cut, Cut,
DefaultFont(usize), DefaultFont(usize),
@ -338,7 +337,7 @@ pub enum Message {
FindSearchValueChanged(String), FindSearchValueChanged(String),
FindUseRegex(bool), FindUseRegex(bool),
FindWrapAround(bool), FindWrapAround(bool),
Focus, Focus(window::Id),
GitProjectStatus(Vec<(String, PathBuf, Vec<GitStatus>)>), GitProjectStatus(Vec<(String, PathBuf, Vec<GitStatus>)>),
GitStage(PathBuf, PathBuf), GitStage(PathBuf, PathBuf),
GitUnstage(PathBuf, PathBuf), GitUnstage(PathBuf, PathBuf),
@ -670,7 +669,7 @@ impl App {
} }
} }
fn update_config(&mut self) -> Command<Message> { fn update_config(&mut self) -> Task<Message> {
//TODO: provide iterator over data //TODO: provide iterator over data
let entities: Vec<_> = self.tab_model.iter().collect(); let entities: Vec<_> = self.tab_model.iter().collect();
for entity in entities { for entity in entities {
@ -681,7 +680,7 @@ impl App {
cosmic::app::command::set_theme(self.config.app_theme.theme()) cosmic::app::command::set_theme(self.config.app_theme.theme())
} }
fn save_config(&mut self) -> Command<Message> { fn save_config(&mut self) -> Task<Message> {
if let Some(ref config_handler) = self.config_handler { if let Some(ref config_handler) = self.config_handler {
if let Err(err) = self.config.write_entry(config_handler) { if let Err(err) = self.config.write_entry(config_handler) {
log::error!("failed to save config: {}", err); log::error!("failed to save config: {}", err);
@ -698,7 +697,7 @@ impl App {
} }
} }
fn update_dialogs(&mut self) -> Command<Message> { fn update_dialogs(&mut self) -> Task<Message> {
match self.dialog_page_opt { match self.dialog_page_opt {
Some(DialogPage::PromptSaveClose(entity)) => { Some(DialogPage::PromptSaveClose(entity)) => {
if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity) { if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity) {
@ -730,16 +729,16 @@ impl App {
} }
None => {} None => {}
} }
Command::none() Task::none()
} }
fn update_focus(&self) -> Command<Message> { fn update_focus(&self) -> Task<Message> {
if self.core.window.show_context { if self.core.window.show_context {
match self.context_page { match self.context_page {
ContextPage::ProjectSearch => { ContextPage::ProjectSearch => {
widget::text_input::focus(self.project_search_id.clone()) widget::text_input::focus(self.project_search_id.clone())
} }
_ => Command::none(), _ => Task::none(),
} }
} else if self.find_opt.is_some() { } else if self.find_opt.is_some() {
widget::text_input::focus(self.find_search_id.clone()) widget::text_input::focus(self.find_search_id.clone())
@ -783,7 +782,7 @@ impl App {
match expand_opt { match expand_opt {
Some(id) => { Some(id) => {
//TODO: can this be optimized? //TODO: can this be optimized?
// Command not used becuase opening a folder just returns Command::none // Task not used becuase opening a folder just returns Task::none
let _ = self.on_nav_select(id); let _ = self.on_nav_select(id);
} }
None => { None => {
@ -796,7 +795,7 @@ impl App {
} }
// Call this any time the tab changes // Call this any time the tab changes
pub fn update_tab(&mut self) -> Command<Message> { pub fn update_tab(&mut self) -> Task<Message> {
self.update_nav_bar_active(); self.update_nav_bar_active();
let title = match self.active_tab() { let title = match self.active_tab() {
@ -811,8 +810,12 @@ impl App {
}; };
let window_title = format!("{title} - {}", fl!("cosmic-text-editor")); let window_title = format!("{title} - {}", fl!("cosmic-text-editor"));
Command::batch([ Task::batch([
self.set_window_title(window_title, self.main_window_id()), if let Some(window_id) = self.core.main_window_id() {
self.set_window_title(window_title, window_id)
} else {
Task::none()
},
self.update_focus(), self.update_focus(),
]) ])
} }
@ -844,7 +847,7 @@ impl App {
.padding(0) .padding(0)
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_x(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
} }
@ -878,7 +881,7 @@ impl App {
}); });
} }
widget::settings::view_column(vec![widget::settings::view_section("") widget::settings::view_column(vec![widget::settings::section()
.add( .add(
widget::settings::item::builder(fl!("word-count")) widget::settings::item::builder(fl!("word-count"))
.control(widget::text(word_count.to_string())), .control(widget::text(word_count.to_string())),
@ -911,10 +914,10 @@ impl App {
cosmic_theme.warning_color(), cosmic_theme.warning_color(),
) )
}; };
let added = || widget::text("[+]").style(theme::Text::Color(success_color.into())); let added = || widget::text("[+]").class(theme::Text::Color(success_color.into()));
let deleted = let deleted =
|| widget::text("[-]").style(theme::Text::Color(destructive_color.into())); || widget::text("[-]").class(theme::Text::Color(destructive_color.into()));
let modified = || widget::text("[*]").style(theme::Text::Color(warning_color.into())); let modified = || widget::text("[*]").class(theme::Text::Color(warning_color.into()));
let mut items = let mut items =
Vec::with_capacity(project_status.len().saturating_mul(3).saturating_add(1)); Vec::with_capacity(project_status.len().saturating_mul(3).saturating_add(1));
@ -979,7 +982,7 @@ impl App {
widget::row::with_children(vec![ widget::row::with_children(vec![
icon.into(), icon.into(),
widget::text(text.clone()).into(), widget::text(text.clone()).into(),
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
widget::button::standard(fl!("stage")) widget::button::standard(fl!("stage"))
.on_press(Message::GitStage( .on_press(Message::GitStage(
project_path.clone(), project_path.clone(),
@ -987,7 +990,7 @@ impl App {
)) ))
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(spacing.space_xs), .spacing(spacing.space_xs),
) )
.on_press(Message::PrepareGitDiff( .on_press(Message::PrepareGitDiff(
@ -995,7 +998,7 @@ impl App {
item.path.clone(), item.path.clone(),
false, false,
)) ))
.style(theme::Button::AppletMenu) .class(theme::Button::AppletMenu)
.width(Length::Fill) .width(Length::Fill)
.into(), .into(),
); );
@ -1020,7 +1023,7 @@ impl App {
widget::row::with_children(vec![ widget::row::with_children(vec![
icon.into(), icon.into(),
widget::text(text.clone()).into(), widget::text(text.clone()).into(),
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
widget::button::standard(fl!("unstage")) widget::button::standard(fl!("unstage"))
.on_press(Message::GitUnstage( .on_press(Message::GitUnstage(
project_path.clone(), project_path.clone(),
@ -1028,7 +1031,7 @@ impl App {
)) ))
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(spacing.space_xs), .spacing(spacing.space_xs),
) )
.on_press(Message::PrepareGitDiff( .on_press(Message::PrepareGitDiff(
@ -1036,7 +1039,7 @@ impl App {
item.path.clone(), item.path.clone(),
true, true,
)) ))
.style(theme::Button::AppletMenu) .class(theme::Button::AppletMenu)
.width(Length::Fill) .width(Length::Fill)
.into(), .into(),
); );
@ -1047,7 +1050,8 @@ impl App {
if !unstaged_items.is_empty() { if !unstaged_items.is_empty() {
items.push( items.push(
widget::settings::view_section(fl!("unstaged-changes")) widget::settings::section()
.title(fl!("unstaged-changes"))
.add(widget::column::with_children(unstaged_items)) .add(widget::column::with_children(unstaged_items))
.into(), .into(),
); );
@ -1055,7 +1059,8 @@ impl App {
if !staged_items.is_empty() { if !staged_items.is_empty() {
items.push( items.push(
widget::settings::view_section(fl!("staged-changes")) widget::settings::section()
.title(fl!("staged-changes"))
.add(widget::column::with_children(staged_items)) .add(widget::column::with_children(staged_items))
.into(), .into(),
); );
@ -1132,17 +1137,15 @@ impl App {
) )
.on_press(Message::OpenSearchResult(file_i, line_i)) .on_press(Message::OpenSearchResult(file_i, line_i))
.width(Length::Fill) .width(Length::Fill)
.style(theme::Button::AppletMenu), .class(theme::Button::AppletMenu),
); );
} }
items.push( items.push(
widget::settings::view_section(format!( widget::settings::section()
"{}", .title(format!("{}", file_search_result.path.display(),))
file_search_result.path.display(), .add(column)
)) .into(),
.add(column)
.into(),
); );
} }
@ -1188,7 +1191,8 @@ impl App {
.iter() .iter()
.position(|font_size| font_size == &self.config.font_size); .position(|font_size| font_size == &self.config.font_size);
widget::settings::view_column(vec![ widget::settings::view_column(vec![
widget::settings::view_section(fl!("appearance")) widget::settings::section()
.title(fl!("appearance"))
.add( .add(
widget::settings::item::builder(fl!("theme")).control(widget::dropdown( widget::settings::item::builder(fl!("theme")).control(widget::dropdown(
&self.app_themes, &self.app_themes,
@ -1231,7 +1235,8 @@ impl App {
), ),
) )
.into(), .into(),
widget::settings::view_section(fl!("keyboard-shortcuts")) widget::settings::section()
.title(fl!("keyboard-shortcuts"))
.add( .add(
widget::settings::item::builder(fl!("enable-vim-bindings")) widget::settings::item::builder(fl!("enable-vim-bindings"))
.toggler(self.config.vim_bindings, Message::VimBindings), .toggler(self.config.vim_bindings, Message::VimBindings),
@ -1265,7 +1270,7 @@ impl Application for App {
} }
/// Creates the application, and optionally emits command on initialize. /// Creates the application, and optionally emits command on initialize.
fn init(core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) { fn init(core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
// Update font name from config // Update font name from config
{ {
let mut font_system = font_system().write().unwrap(); let mut font_system = font_system().write().unwrap();
@ -1396,7 +1401,7 @@ impl Application for App {
.style(theme::SegmentedButton::TabBar) .style(theme::SegmentedButton::TabBar)
.apply(widget::container) .apply(widget::container)
.padding(space_s) .padding(space_s)
.width(Length::Fill); .width(Length::Shrink);
if !self.core().is_condensed() { if !self.core().is_condensed() {
nav = nav.max_width(280); nav = nav.max_width(280);
@ -1406,7 +1411,7 @@ impl Application for App {
nav.apply(widget::scrollable) nav.apply(widget::scrollable)
.apply(widget::container) .apply(widget::container)
.height(Length::Fill) .height(Length::Fill)
.style(theme::Container::custom(nav_bar::nav_bar_style)) .class(theme::Container::custom(nav_bar::nav_bar_style))
.into(), .into(),
) )
} }
@ -1419,13 +1424,13 @@ impl Application for App {
Some(Message::Quit) Some(Message::Quit)
} }
fn on_context_drawer(&mut self) -> Command<Message> { fn on_context_drawer(&mut self) -> Task<Message> {
// Focus correct widget // Focus correct widget
self.update_focus() self.update_focus()
} }
//TODO: currently the first escape unfocuses, and the second calls this function //TODO: currently the first escape unfocuses, and the second calls this function
fn on_escape(&mut self) -> Command<Message> { fn on_escape(&mut self) -> Task<Message> {
if self.core.window.show_context { if self.core.window.show_context {
// Close context drawer if open // Close context drawer if open
self.core.window.show_context = false; self.core.window.show_context = false;
@ -1438,7 +1443,7 @@ impl Application for App {
self.update_focus() self.update_focus()
} }
fn on_nav_select(&mut self, id: nav_bar::Id) -> Command<Message> { fn on_nav_select(&mut self, id: nav_bar::Id) -> Task<Message> {
// Toggle open state and get clone of node data // Toggle open state and get clone of node data
let node_opt = match self.nav_model.data_mut::<ProjectNode>(id) { let node_opt = match self.nav_model.data_mut::<ProjectNode>(id) {
Some(node) => { Some(node) => {
@ -1477,7 +1482,7 @@ impl Application for App {
// folder in condensed mode. // folder in condensed mode.
self.core_mut().nav_bar_set_toggled(true); self.core_mut().nav_bar_set_toggled(true);
Command::none() Task::none()
} }
ProjectNode::File { path, .. } => { ProjectNode::File { path, .. } => {
//TODO: go to already open file if possible //TODO: go to already open file if possible
@ -1520,9 +1525,9 @@ impl Application for App {
let mut column = widget::column::with_capacity(entities.len()).spacing(space_xxs); let mut column = widget::column::with_capacity(entities.len()).spacing(space_xxs);
for entity in entities.iter() { for entity in entities.iter() {
if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(*entity) { if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(*entity) {
let mut row = widget::row::with_capacity(3).align_items(Alignment::Center); let mut row = widget::row::with_capacity(3).align_y(Alignment::Center);
row = row.push(widget::text(tab.title())); row = row.push(widget::text(tab.title()));
row = row.push(widget::horizontal_space(Length::Fill)); row = row.push(widget::horizontal_space());
if let Some(_path) = &tab.path_opt { if let Some(_path) = &tab.path_opt {
row = row.push( row = row.push(
widget::button::standard(fl!("save")) widget::button::standard(fl!("save"))
@ -1562,7 +1567,7 @@ impl Application for App {
} }
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::AppTheme(app_theme) => { Message::AppTheme(app_theme) => {
self.config.app_theme = app_theme; self.config.app_theme = app_theme;
@ -1613,6 +1618,11 @@ impl Application for App {
} }
} }
} }
Message::CloseWindow(window_id) => {
if Some(window_id) == self.core.main_window_id() {
return self.update(Message::Quit);
}
}
Message::Copy => { Message::Copy => {
if let Some(Tab::Editor(tab)) = self.active_tab() { if let Some(Tab::Editor(tab)) = self.active_tab() {
let editor = tab.editor.lock().unwrap(); let editor = tab.editor.lock().unwrap();
@ -1633,7 +1643,7 @@ impl Application for App {
selection_opt selection_opt
}; };
if let Some(selection) = selection_opt { if let Some(selection) = selection_opt {
return Command::batch([ return Task::batch([
clipboard::write(selection), clipboard::write(selection),
self.update(Message::TabChanged(self.tab_model.active())), self.update(Message::TabChanged(self.tab_model.active())),
]); ]);
@ -1830,7 +1840,7 @@ impl Application for App {
self.git_project_status = Some(project_status); self.git_project_status = Some(project_status);
} }
Message::GitStage(project_path, path) => { Message::GitStage(project_path, path) => {
return Command::perform( return Task::perform(
async move { async move {
//TODO: send errors to UI //TODO: send errors to UI
match GitRepository::new(&project_path) { match GitRepository::new(&project_path) {
@ -1861,7 +1871,7 @@ impl Application for App {
); );
} }
Message::GitUnstage(project_path, path) => { Message::GitUnstage(project_path, path) => {
return Command::perform( return Task::perform(
async move { async move {
//TODO: send errors to UI //TODO: send errors to UI
match GitRepository::new(&project_path) { match GitRepository::new(&project_path) {
@ -2107,9 +2117,9 @@ impl Application for App {
if let Some((path, cursor)) = path_cursor_opt { if let Some((path, cursor)) = path_cursor_opt {
if let Some(entity) = self.open_tab(Some(path)) { if let Some(entity) = self.open_tab(Some(path)) {
return Command::batch([ return Task::batch([
//TODO: why must this be done in a command? //TODO: why must this be done in a command?
Command::perform( Task::perform(
async move { message::app(Message::TabSetCursor(entity, cursor)) }, async move { message::app(Message::TabSetCursor(entity, cursor)) },
|x| x, |x| x,
), ),
@ -2119,7 +2129,7 @@ impl Application for App {
} }
} }
Message::Paste => { Message::Paste => {
return clipboard::read(|value_opt| match value_opt { return clipboard::read().map(|value_opt| match value_opt {
Some(value) => message::app(Message::PasteValue(value)), Some(value) => message::app(Message::PasteValue(value)),
None => message::none(), None => message::none(),
}); });
@ -2136,7 +2146,7 @@ impl Application for App {
} }
} }
Message::PrepareGitDiff(project_path, path, staged) => { Message::PrepareGitDiff(project_path, path, staged) => {
return Command::perform( return Task::perform(
async move { async move {
//TODO: send errors to UI //TODO: send errors to UI
match GitRepository::new(&project_path) { match GitRepository::new(&project_path) {
@ -2183,7 +2193,7 @@ impl Application for App {
files: Vec::new(), files: Vec::new(),
}; };
self.project_search_result = Some(project_search_result.clone()); self.project_search_result = Some(project_search_result.clone());
return Command::perform( return Task::perform(
async move { async move {
let task_res = tokio::task::spawn_blocking(move || { let task_res = tokio::task::spawn_blocking(move || {
project_search_result.search_projects(projects); project_search_result.search_projects(projects);
@ -2378,7 +2388,7 @@ impl Application for App {
// Ex. If tab 2 and 3 both have unsaved changes and `PromptSaveClose` is // Ex. If tab 2 and 3 both have unsaved changes and `PromptSaveClose` is
// emitted for tab 2, closing tab 3 should open the dialog for tab 3 in // emitted for tab 2, closing tab 3 should open the dialog for tab 3 in
// order for `Message::Save` to save the correct tab. // order for `Message::Save` to save the correct tab.
return Command::batch([ return Task::batch([
// Focus the tab in case the user is closing an unfocussed tab // Focus the tab in case the user is closing an unfocussed tab
// Otherwise, closing an unfocussed tab would be very confusing // Otherwise, closing an unfocussed tab would be very confusing
self.update(Message::TabActivate(entity)), self.update(Message::TabActivate(entity)),
@ -2542,7 +2552,7 @@ impl Application for App {
Message::UpdateGitProjectStatus => { Message::UpdateGitProjectStatus => {
self.git_project_status = None; self.git_project_status = None;
let projects = self.projects.clone(); let projects = self.projects.clone();
return Command::perform( return Task::perform(
async move { async move {
let mut project_status = Vec::new(); let mut project_status = Vec::new();
for (project_name, project_path) in projects.iter() { for (project_name, project_path) in projects.iter() {
@ -2584,15 +2594,17 @@ impl Application for App {
self.config.vim_bindings = vim_bindings; self.config.vim_bindings = vim_bindings;
return self.save_config(); return self.save_config();
} }
Message::Focus => { Message::Focus(window_id) => {
// focus the text box if context page is not shown if Some(window_id) == self.core.main_window_id() {
if !self.core.window.show_context { // focus the text box if context page is not shown
return self.update_focus(); if !self.core.window.show_context {
return self.update_focus();
}
} }
} }
} }
Command::none() Task::none()
} }
fn context_drawer(&self) -> Option<Element<Message>> { fn context_drawer(&self) -> Option<Element<Message>> {
@ -2629,7 +2641,7 @@ impl Application for App {
tab_column = tab_column.push( tab_column = tab_column.push(
widget::row::with_capacity(2) widget::row::with_capacity(2)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.push( .push(
widget::tab_bar::horizontal(&self.tab_model) widget::tab_bar::horizontal(&self.tab_model)
.button_height(32) .button_height(32)
@ -2644,43 +2656,13 @@ impl Application for App {
button::custom(icon_cache_get("list-add-symbolic", 16)) button::custom(icon_cache_get("list-add-symbolic", 16))
.on_press(Message::NewFile) .on_press(Message::NewFile)
.padding(space_xxs) .padding(space_xxs)
.style(style::Button::Icon), .class(style::Button::Icon),
), ),
); );
let tab_id = self.tab_model.active(); let tab_id = self.tab_model.active();
match self.tab_model.data::<Tab>(tab_id) { match self.tab_model.data::<Tab>(tab_id) {
Some(Tab::Editor(tab)) => { Some(Tab::Editor(tab)) => {
let status = {
let editor = tab.editor.lock().unwrap();
let parser = editor.parser();
match &parser.mode {
ViMode::Normal => {
format!("{}", parser.cmd)
}
ViMode::Insert => "-- INSERT --".to_string(),
ViMode::Extra(extra) => {
format!("{}{}", parser.cmd, extra)
}
ViMode::Replace => "-- REPLACE --".to_string(),
ViMode::Visual => {
format!("-- VISUAL -- {}", parser.cmd)
}
ViMode::VisualLine => {
format!("-- VISUAL LINE -- {}", parser.cmd)
}
ViMode::Command { value } => {
format!(":{value}|")
}
ViMode::Search { value, forwards } => {
if *forwards {
format!("/{value}|")
} else {
format!("?{value}|")
}
}
}
};
let mut text_box = text_box(&tab.editor, self.config.metrics()) let mut text_box = text_box(&tab.editor, self.config.metrics())
.id(self.text_box_id.clone()) .id(self.text_box_id.clone())
.on_changed(Message::TabChanged(tab_id)) .on_changed(Message::TabChanged(tab_id))
@ -2701,11 +2683,39 @@ impl Application for App {
.position(widget::popover::Position::Point(point)); .position(widget::popover::Position::Point(point));
} }
tab_column = tab_column.push(popover); tab_column = tab_column.push(popover);
/*TODO: the status area breaks text box focus if self.config.vim_bindings {
if !status.is_empty() { let status = {
tab_column = tab_column.push(text(status).font(Font::MONOSPACE)); let editor = tab.editor.lock().unwrap();
let parser = editor.parser();
match &parser.mode {
ViMode::Normal => {
format!("{}", parser.cmd)
}
ViMode::Insert => "-- INSERT --".to_string(),
ViMode::Extra(extra) => {
format!("{}{}", parser.cmd, extra)
}
ViMode::Replace => "-- REPLACE --".to_string(),
ViMode::Visual => {
format!("-- VISUAL -- {}", parser.cmd)
}
ViMode::VisualLine => {
format!("-- VISUAL LINE -- {}", parser.cmd)
}
ViMode::Command { value } => {
format!(":{value}|")
}
ViMode::Search { value, forwards } => {
if *forwards {
format!("/{value}|")
} else {
format!("?{value}|")
}
}
}
};
tab_column = tab_column.push(widget::text(status).font(Font::MONOSPACE));
} }
*/
} }
Some(Tab::GitDiff(tab)) => { Some(Tab::GitDiff(tab)) => {
let mut diff_widget = widget::column::with_capacity(tab.diff.hunks.len()); let mut diff_widget = widget::column::with_capacity(tab.diff.hunks.len());
@ -2721,36 +2731,36 @@ impl Application for App {
"{:4} {:4} {}", "{:4} {:4} {}",
old_line, new_line, text old_line, new_line, text
))), ))),
GitDiffLine::Added { new_line, text } => widget::container( GitDiffLine::Added { new_line, text } => {
widget::text::monotext(format!( widget::container(widget::text::monotext(format!(
"{:4} {:4} + {}", "{:4} {:4} + {}",
"", new_line, text "", new_line, text
)), )))
) .style(|_theme| {
.style(theme::Container::Custom(Box::new(|_theme| { //TODO: theme this color
//TODO: theme this color widget::container::Style {
widget::container::Appearance { background: Some(Background::Color(Color::from_rgb8(
background: Some(Background::Color(Color::from_rgb8( 0x00, 0x40, 0x00,
0x00, 0x40, 0x00, ))),
))), ..Default::default()
..Default::default() }
} })
}))), }
GitDiffLine::Deleted { old_line, text } => widget::container( GitDiffLine::Deleted { old_line, text } => {
widget::text::monotext(format!( widget::container(widget::text::monotext(format!(
"{:4} {:4} - {}", "{:4} {:4} - {}",
old_line, "", text old_line, "", text
)), )))
) .style(|_theme| {
.style(theme::Container::Custom(Box::new(|_theme| { //TODO: theme this color
//TODO: theme this color widget::container::Style {
widget::container::Appearance { background: Some(Background::Color(Color::from_rgb8(
background: Some(Background::Color(Color::from_rgb8( 0x40, 0x00, 0x00,
0x40, 0x00, 0x00, ))),
))), ..Default::default()
..Default::default() }
} })
}))), }
}; };
hunk_widget = hunk_widget.push(line_widget.width(Length::Fill)); hunk_widget = hunk_widget.push(line_widget.width(Length::Fill));
} }
@ -2777,7 +2787,7 @@ impl Application for App {
.trailing_icon( .trailing_icon(
button::custom(icon_cache_get("edit-clear-symbolic", 16)) button::custom(icon_cache_get("edit-clear-symbolic", 16))
.on_press(Message::FindSearchValueChanged(String::new())) .on_press(Message::FindSearchValueChanged(String::new()))
.style(style::Button::Icon) .class(style::Button::Icon)
.into(), .into(),
); );
let find_widget = widget::row::with_children(vec![ let find_widget = widget::row::with_children(vec![
@ -2786,8 +2796,8 @@ impl Application for App {
button::custom(icon_cache_get("go-up-symbolic", 16)) button::custom(icon_cache_get("go-up-symbolic", 16))
.on_press(Message::FindPrevious) .on_press(Message::FindPrevious)
.padding(space_xxs) .padding(space_xxs)
.style(style::Button::Icon), .class(style::Button::Icon),
fl!("find-previous"), widget::text::body(fl!("find-previous")),
widget::tooltip::Position::Top, widget::tooltip::Position::Top,
) )
.into(), .into(),
@ -2795,19 +2805,19 @@ impl Application for App {
button::custom(icon_cache_get("go-down-symbolic", 16)) button::custom(icon_cache_get("go-down-symbolic", 16))
.on_press(Message::FindNext) .on_press(Message::FindNext)
.padding(space_xxs) .padding(space_xxs)
.style(style::Button::Icon), .class(style::Button::Icon),
fl!("find-next"), widget::text::body(fl!("find-next")),
widget::tooltip::Position::Top, widget::tooltip::Position::Top,
) )
.into(), .into(),
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
button::custom(icon_cache_get("window-close-symbolic", 16)) button::custom(icon_cache_get("window-close-symbolic", 16))
.on_press(Message::Find(None)) .on_press(Message::Find(None))
.padding(space_xxs) .padding(space_xxs)
.style(style::Button::Icon) .class(style::Button::Icon)
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.padding(space_xxs) .padding(space_xxs)
.spacing(space_xxs); .spacing(space_xxs);
@ -2824,7 +2834,7 @@ impl Application for App {
.trailing_icon( .trailing_icon(
button::custom(icon_cache_get("edit-clear-symbolic", 16)) button::custom(icon_cache_get("edit-clear-symbolic", 16))
.on_press(Message::FindReplaceValueChanged(String::new())) .on_press(Message::FindReplaceValueChanged(String::new()))
.style(style::Button::Icon) .class(style::Button::Icon)
.into(), .into(),
); );
let replace_widget = widget::row::with_children(vec![ let replace_widget = widget::row::with_children(vec![
@ -2833,8 +2843,8 @@ impl Application for App {
button::custom(icon_cache_get("replace-symbolic", 16)) button::custom(icon_cache_get("replace-symbolic", 16))
.on_press(Message::FindReplace) .on_press(Message::FindReplace)
.padding(space_xxs) .padding(space_xxs)
.style(style::Button::Icon), .class(style::Button::Icon),
fl!("replace"), widget::text::body(fl!("replace")),
widget::tooltip::Position::Top, widget::tooltip::Position::Top,
) )
.into(), .into(),
@ -2842,13 +2852,13 @@ impl Application for App {
button::custom(icon_cache_get("replace-all-symbolic", 16)) button::custom(icon_cache_get("replace-all-symbolic", 16))
.on_press(Message::FindReplaceAll) .on_press(Message::FindReplaceAll)
.padding(space_xxs) .padding(space_xxs)
.style(style::Button::Icon), .class(style::Button::Icon),
fl!("replace-all"), widget::text::body(fl!("replace-all")),
widget::tooltip::Position::Top, widget::tooltip::Position::Top,
) )
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.padding(space_xxs) .padding(space_xxs)
.spacing(space_xxs); .spacing(space_xxs);
@ -2857,26 +2867,17 @@ impl Application for App {
column = column.push( column = column.push(
widget::row::with_children(vec![ widget::row::with_children(vec![
widget::checkbox( widget::checkbox(fl!("case-sensitive"), self.config.find_case_sensitive)
fl!("case-sensitive"), .on_toggle(Message::FindCaseSensitive)
self.config.find_case_sensitive, .into(),
Message::FindCaseSensitive, widget::checkbox(fl!("use-regex"), self.config.find_use_regex)
) .on_toggle(Message::FindUseRegex)
.into(), .into(),
widget::checkbox( widget::checkbox(fl!("wrap-around"), self.config.find_wrap_around)
fl!("use-regex"), .on_toggle(Message::FindWrapAround)
self.config.find_use_regex, .into(),
Message::FindUseRegex,
)
.into(),
widget::checkbox(
fl!("wrap-around"),
self.config.find_wrap_around,
Message::FindWrapAround,
)
.into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.padding(space_xxs) .padding(space_xxs)
.spacing(space_xxs), .spacing(space_xxs),
); );
@ -2899,14 +2900,14 @@ impl Application for App {
} }
} }
fn subscription(&self) -> subscription::Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
struct WatcherSubscription; struct WatcherSubscription;
struct ConfigSubscription; struct ConfigSubscription;
struct ConfigStateSubscription; struct ConfigStateSubscription;
struct ThemeSubscription; struct ThemeSubscription;
subscription::Subscription::batch([ Subscription::batch([
event::listen_with(|event, status| match event { event::listen_with(|event, status, window_id| match event {
event::Event::Keyboard(keyboard::Event::KeyPressed { modifiers, key, .. }) => { event::Event::Keyboard(keyboard::Event::KeyPressed { modifiers, key, .. }) => {
match status { match status {
event::Status::Ignored => Some(Message::Key(modifiers, key)), event::Status::Ignored => Some(Message::Key(modifiers, key)),
@ -2916,20 +2917,15 @@ impl Application for App {
event::Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { event::Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
Some(Message::Modifiers(modifiers)) Some(Message::Modifiers(modifiers))
} }
event::Event::Window(id, window::Event::Focused) if id == window::Id::MAIN => { event::Event::Window(window::Event::Focused) => Some(Message::Focus(window_id)),
Some(Message::Focus) event::Event::Window(window::Event::CloseRequested) => {
} Some(Message::CloseWindow(window_id))
event::Event::Window(id, window::Event::CloseRequested)
if id == window::Id::MAIN =>
{
Some(Message::Quit)
} }
_ => None, _ => None,
}), }),
subscription::channel( Subscription::run_with_id(
TypeId::of::<WatcherSubscription>(), TypeId::of::<WatcherSubscription>(),
100, stream::channel(100, |mut output| async move {
|mut output| async move {
let watcher_res = { let watcher_res = {
let mut output = output.clone(); let mut output = output.clone();
//TODO: debounce //TODO: debounce
@ -2986,7 +2982,7 @@ impl Application for App {
loop { loop {
time::sleep(time::Duration::new(1, 0)).await; time::sleep(time::Duration::new(1, 0)).await;
} }
}, }),
), ),
cosmic_config::config_subscription( cosmic_config::config_subscription(
TypeId::of::<ConfigSubscription>(), TypeId::of::<ConfigSubscription>(),
@ -3026,7 +3022,7 @@ impl Application for App {
}), }),
match &self.dialog_opt { match &self.dialog_opt {
Some(dialog) => dialog.subscription(), Some(dialog) => dialog.subscription(),
None => subscription::Subscription::none(), None => Subscription::none(),
}, },
]) ])
} }

View file

@ -5,7 +5,6 @@ use cosmic::widget::menu::{items as menu_items, root as menu_root, Item as MenuI
use cosmic::{ use cosmic::{
iced::{widget::column, widget::horizontal_rule, Background, Length}, iced::{widget::column, widget::horizontal_rule, Background, Length},
iced_core::Border, iced_core::Border,
theme,
widget::{ widget::{
self, horizontal_space, self, horizontal_space,
menu::{menu_button, ItemHeight, ItemWidth, MenuBar, Tree as MenuTree}, menu::{menu_button, ItemHeight, ItemWidth, MenuBar, Tree as MenuTree},
@ -31,7 +30,7 @@ pub fn context_menu<'a>(
} }
menu_button(vec![ menu_button(vec![
widget::text(menu_label).into(), widget::text(menu_label).into(),
horizontal_space(Length::Fill).into(), horizontal_space().into(),
widget::text(key).into(), widget::text(key).into(),
]) ])
.on_press(Message::TabContextAction(entity, menu_action)) .on_press(Message::TabContextAction(entity, menu_action))
@ -48,10 +47,10 @@ pub fn context_menu<'a>(
)) ))
.padding(1) .padding(1)
//TODO: move style to libcosmic //TODO: move style to libcosmic
.style(theme::Container::custom(|theme| { .style(|theme| {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
let component = &cosmic.background.component; let component = &cosmic.background.component;
widget::container::Appearance { widget::container::Style {
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())),
@ -62,7 +61,7 @@ pub fn context_menu<'a>(
}, },
..Default::default() ..Default::default()
} }
})) })
.width(Length::Fixed(240.0)) .width(Length::Fixed(240.0))
.into() .into()
} }

View file

@ -17,10 +17,10 @@ use cosmic::{
renderer::{self, Quad, Renderer as _}, renderer::{self, Quad, Renderer as _},
widget::{ widget::{
self, self,
operation::{self, Operation, OperationOutputWrapper}, operation::{self, Operation},
tree, Id, Widget, tree, Id, Widget,
}, },
Border, Shell, Border, Radians, Shell,
}, },
theme::Theme, theme::Theme,
Renderer, Renderer,
@ -265,7 +265,7 @@ where
tree: &mut widget::Tree, tree: &mut widget::Tree,
_layout: Layout<'_>, _layout: Layout<'_>,
_renderer: &Renderer, _renderer: &Renderer,
operation: &mut dyn Operation<OperationOutputWrapper<Message>>, operation: &mut dyn Operation,
) { ) {
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
@ -653,7 +653,7 @@ where
editor.set_redraw(false); editor.set_redraw(false);
state.scale_factor.set(scale_factor); state.scale_factor.set(scale_factor);
*handle_opt = Some(image::Handle::from_pixels( *handle_opt = Some(image::Handle::from_rgba(
image_w as u32, image_w as u32,
image_h as u32, image_h as u32,
pixels_u8, pixels_u8,
@ -662,7 +662,7 @@ where
let image_position = layout.position() + [self.padding.left, self.padding.top].into(); let image_position = layout.position() + [self.padding.left, self.padding.top].into();
if let Some(ref handle) = *handle_opt { if let Some(ref handle) = *handle_opt {
let image_size = image::Renderer::dimensions(renderer, handle); let image_size = image::Renderer::measure_image(renderer, handle);
let scaled_size = Size::new(scaled_w as f32, scaled_h as f32); let scaled_size = Size::new(scaled_w as f32, scaled_h as f32);
log::debug!( log::debug!(
"text_box image {:?} scaled {:?} position {:?}", "text_box image {:?} scaled {:?} position {:?}",
@ -670,11 +670,13 @@ where
scaled_size, scaled_size,
image_position image_position
); );
image::Renderer::draw( image::Renderer::draw_image(
renderer, renderer,
handle.clone(), handle.clone(),
image::FilterMethod::Nearest, image::FilterMethod::Nearest,
Rectangle::new(image_position, scaled_size), Rectangle::new(image_position, scaled_size),
Radians(0.0),
1.0,
[0.0; 4], [0.0; 4],
); );
} }
@ -884,6 +886,11 @@ where
let (buffer_size, buffer_scroll) = let (buffer_size, buffer_scroll) =
editor.with_buffer(|buffer| (buffer.size(), buffer.scroll())); editor.with_buffer(|buffer| (buffer.size(), buffer.scroll()));
let last_changed = editor.changed(); let last_changed = editor.changed();
//TODO: better handling of status line update
let (last_parser_mode, last_parser_cmd) = {
let parser = editor.parser();
(parser.mode.clone(), parser.cmd)
};
let mut font_system = font_system().write().unwrap(); let mut font_system = font_system().write().unwrap();
let mut editor = editor.borrow_with(font_system.raw()); let mut editor = editor.borrow_with(font_system.raw());
@ -1191,8 +1198,12 @@ where
_ => (), _ => (),
} }
if editor.changed() != last_changed { if let Some(on_changed) = &self.on_changed {
if let Some(on_changed) = &self.on_changed { //TODO: better handling of status line update
let parser = editor.parser();
if editor.changed() != last_changed
|| (&parser.mode, &parser.cmd) != (&last_parser_mode, &last_parser_cmd)
{
shell.publish(on_changed.clone()); shell.publish(on_changed.clone());
} }
} }