Update to latest libcosmic, improve tab handling
This commit is contained in:
parent
3dad361105
commit
e2f6e6d879
5 changed files with 645 additions and 528 deletions
976
Cargo.lock
generated
976
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -13,12 +13,11 @@ log = "0.4"
|
||||||
|
|
||||||
[dependencies.cosmic-text]
|
[dependencies.cosmic-text]
|
||||||
git = "https://github.com/pop-os/cosmic-text"
|
git = "https://github.com/pop-os/cosmic-text"
|
||||||
rev = "fedeeea9d307093fac880802e17e107b456e5de9"
|
|
||||||
features = ["syntect"]
|
features = ["syntect"]
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic]
|
||||||
git = "https://github.com/pop-os/libcosmic"
|
git = "https://github.com/pop-os/libcosmic"
|
||||||
rev = "a8ce524baa58f4fb2db2e26a5fc0899b63d688b5"
|
branch = "theme-dark-light-switching"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["winit"]
|
features = ["winit"]
|
||||||
#path = "../libcosmic"
|
#path = "../libcosmic"
|
||||||
|
|
|
||||||
175
src/main.rs
175
src/main.rs
|
|
@ -1,14 +1,16 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
app::{self, Command, Core, Settings},
|
||||||
|
executor,
|
||||||
iced::{
|
iced::{
|
||||||
self, settings,
|
self,
|
||||||
widget::{column, container, horizontal_space, pick_list, row, text},
|
widget::{column, container, horizontal_space, pick_list, row, text},
|
||||||
Alignment, Application, Color, Command, Length,
|
Alignment, Color, Length,
|
||||||
},
|
},
|
||||||
theme::{self, Theme, ThemeType},
|
theme::{self, Theme, ThemeType},
|
||||||
widget::{button, segmented_button, toggler, view_switcher},
|
widget::{button, icon, segmented_button, toggler, view_switcher},
|
||||||
Element,
|
ApplicationExt, Element,
|
||||||
};
|
};
|
||||||
use cosmic_text::{
|
use cosmic_text::{
|
||||||
Attrs, AttrsList, Buffer, Edit, FontSystem, Metrics, SyntaxEditor, SyntaxSystem, Wrap,
|
Attrs, AttrsList, Buffer, Edit, FontSystem, Metrics, SyntaxEditor, SyntaxSystem, Wrap,
|
||||||
|
|
@ -35,12 +37,15 @@ static FONT_SIZES: &'static [Metrics] = &[
|
||||||
Metrics::new(32.0, 44.0), // Title 1
|
Metrics::new(32.0, 44.0), // Title 1
|
||||||
];
|
];
|
||||||
|
|
||||||
fn main() -> cosmic::iced::Result {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||||
|
|
||||||
let mut settings = settings::Settings::default();
|
let settings = Settings::default();
|
||||||
settings.window.min_size = Some((400, 100));
|
//TODO: settings.window.min_size = Some((400, 100));
|
||||||
Window::run(settings)
|
let flags = ();
|
||||||
|
cosmic::app::run::<App>(settings, flags)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
|
|
@ -126,8 +131,8 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct App {
|
||||||
theme: Theme,
|
core: Core,
|
||||||
tab_model: segmented_button::SingleSelectModel,
|
tab_model: segmented_button::SingleSelectModel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +146,7 @@ pub enum Message {
|
||||||
Todo,
|
Todo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl App {
|
||||||
pub fn active_tab(&self) -> Option<&Tab> {
|
pub fn active_tab(&self) -> Option<&Tab> {
|
||||||
self.tab_model.active_data()
|
self.tab_model.active_data()
|
||||||
}
|
}
|
||||||
|
|
@ -149,62 +154,74 @@ impl Window {
|
||||||
pub fn active_tab_mut(&mut self) -> Option<&mut Tab> {
|
pub fn active_tab_mut(&mut self) -> Option<&mut Tab> {
|
||||||
self.tab_model.active_data_mut()
|
self.tab_model.active_data_mut()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Application for Window {
|
|
||||||
type Executor = iced::executor::Default;
|
|
||||||
type Flags = ();
|
|
||||||
type Message = Message;
|
|
||||||
type Theme = Theme;
|
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
|
|
||||||
let mut tab_model = segmented_button::Model::builder().build();
|
|
||||||
|
|
||||||
|
pub fn open_tab(&mut self, path_opt: Option<PathBuf>) {
|
||||||
let mut tab = Tab::new();
|
let mut tab = Tab::new();
|
||||||
if let Some(arg) = env::args().nth(1) {
|
if let Some(path) = path_opt {
|
||||||
tab.open(PathBuf::from(arg));
|
tab.open(path);
|
||||||
}
|
}
|
||||||
|
self.tab_model
|
||||||
tab_model
|
|
||||||
.insert()
|
.insert()
|
||||||
.text(tab.title())
|
.text(tab.title())
|
||||||
.icon("text-x-generic")
|
.icon(icon::from_name("text-x-generic").icon())
|
||||||
.icon_color(None)
|
|
||||||
.data(tab)
|
.data(tab)
|
||||||
.closable()
|
.closable()
|
||||||
.activate();
|
.activate();
|
||||||
|
|
||||||
(
|
|
||||||
Window {
|
|
||||||
theme: Theme::dark(),
|
|
||||||
tab_model,
|
|
||||||
},
|
|
||||||
Command::none(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
pub fn update_title(&mut self) -> Command<Message> {
|
||||||
match self.active_tab() {
|
let title = match self.active_tab() {
|
||||||
Some(tab) => tab.title(),
|
Some(tab) => tab.title(),
|
||||||
None => format!("COSMIC Text Editor"),
|
None => format!("No Open File"),
|
||||||
}
|
};
|
||||||
|
let window_title = format!("{title} - COSMIC Text Editor");
|
||||||
|
self.core.window.header_title = title.clone();
|
||||||
|
self.set_title(window_title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
||||||
|
impl cosmic::Application for App {
|
||||||
|
/// Default async executor to use with the app.
|
||||||
|
type Executor = executor::Default;
|
||||||
|
|
||||||
|
/// Argument received [`cosmic::Application::new`].
|
||||||
|
type Flags = ();
|
||||||
|
|
||||||
|
/// Message type specific to our [`App`].
|
||||||
|
type Message = Message;
|
||||||
|
|
||||||
|
/// The unique application ID to supply to the window manager.
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicTextEditor";
|
||||||
|
|
||||||
|
fn core(&self) -> &Core {
|
||||||
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> iced::Command<Self::Message> {
|
fn core_mut(&mut self) -> &mut Core {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the application, and optionally emits command on initialize.
|
||||||
|
fn init(core: Core, _flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||||
|
let mut tab_model = segmented_button::Model::builder().build();
|
||||||
|
|
||||||
|
let mut app = App { core, tab_model };
|
||||||
|
|
||||||
|
for path in env::args().skip(1) {
|
||||||
|
app.open_tab(Some(PathBuf::from(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = app.update_title();
|
||||||
|
(app, command)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Message) -> Command<Self::Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Open => {
|
Message::Open => {
|
||||||
if let Some(path) = rfd::FileDialog::new().pick_file() {
|
if let Some(path) = rfd::FileDialog::new().pick_file() {
|
||||||
let mut tab = Tab::new();
|
self.open_tab(Some(path));
|
||||||
tab.open(path);
|
return self.update_title();
|
||||||
|
|
||||||
self.tab_model
|
|
||||||
.insert()
|
|
||||||
.text(tab.title())
|
|
||||||
.icon("text-x-generic")
|
|
||||||
.icon_color(None)
|
|
||||||
.data(tab)
|
|
||||||
.closable()
|
|
||||||
.activate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Save => {
|
Message::Save => {
|
||||||
|
|
@ -219,7 +236,7 @@ impl Application for Window {
|
||||||
tab.save();
|
tab.save();
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log::info!("TODO: NO TAB OPEN");
|
log::warn!("TODO: NO TAB OPEN");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,10 +244,32 @@ impl Application for Window {
|
||||||
self.tab_model.text_set(self.tab_model.active(), title);
|
self.tab_model.text_set(self.tab_model.active(), title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::TabActivate(entity) => self.tab_model.activate(entity),
|
Message::TabActivate(entity) => {
|
||||||
Message::TabClose(entity) => self.tab_model.remove(entity),
|
self.tab_model.activate(entity);
|
||||||
|
return self.update_title();
|
||||||
|
}
|
||||||
|
Message::TabClose(entity) => {
|
||||||
|
// Activate closest item
|
||||||
|
if let Some(position) = self.tab_model.position(entity) {
|
||||||
|
if position > 0 {
|
||||||
|
self.tab_model.activate_position(position - 1);
|
||||||
|
} else {
|
||||||
|
self.tab_model.activate_position(position + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove item
|
||||||
|
self.tab_model.remove(entity);
|
||||||
|
|
||||||
|
// If that was the last tab, make a new empty one
|
||||||
|
if self.tab_model.iter().next().is_none() {
|
||||||
|
self.open_tab(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.update_title();
|
||||||
|
}
|
||||||
Message::Todo => {
|
Message::Todo => {
|
||||||
log::info!("TODO");
|
log::warn!("TODO");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,22 +300,16 @@ impl Application for Window {
|
||||||
.on_close(Message::TabClose)
|
.on_close(Message::TabClose)
|
||||||
.width(Length::Shrink);
|
.width(Length::Shrink);
|
||||||
|
|
||||||
let content: Element<_> = column![
|
let active_tab: Element<_> = match self.active_tab() {
|
||||||
menu_bar,
|
Some(tab) => text_box(&tab.editor).padding(8).into(),
|
||||||
column![
|
None => {
|
||||||
tab_bar,
|
log::warn!("TODO: No tab open");
|
||||||
match self.active_tab() {
|
text("no tab active").into()
|
||||||
Some(tab) => {
|
}
|
||||||
text_box(&tab.editor).padding(8)
|
};
|
||||||
}
|
|
||||||
None => {
|
let content: Element<_> =
|
||||||
panic!("TODO: No tab open");
|
column![menu_bar, column![tab_bar, active_tab,].padding([0, 16])].into();
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
.padding([0, 16])
|
|
||||||
]
|
|
||||||
.into();
|
|
||||||
|
|
||||||
// Uncomment to debug layout:
|
// Uncomment to debug layout:
|
||||||
//content.explain(Color::WHITE)
|
//content.explain(Color::WHITE)
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ where
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
_clipboard: &mut dyn Clipboard,
|
_clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
|
_viewport: &Rectangle<f32>,
|
||||||
) -> event::Status {
|
) -> event::Status {
|
||||||
update(
|
update(
|
||||||
event,
|
event,
|
||||||
|
|
@ -276,6 +277,7 @@ where
|
||||||
self.text_size,
|
self.text_size,
|
||||||
self.font.clone(),
|
self.font.clone(),
|
||||||
&self.options,
|
&self.options,
|
||||||
|
&self.on_selected,
|
||||||
self.style.clone(),
|
self.style.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -349,7 +351,7 @@ where
|
||||||
let max_width = match width {
|
let max_width = match width {
|
||||||
Length::Shrink => {
|
Length::Shrink => {
|
||||||
let measure = |label: &str| -> u32 {
|
let measure = |label: &str| -> u32 {
|
||||||
let (width, _) = renderer.measure(
|
let size = renderer.measure(
|
||||||
label,
|
label,
|
||||||
text_size as f32,
|
text_size as f32,
|
||||||
LineHeight::default(),
|
LineHeight::default(),
|
||||||
|
|
@ -358,7 +360,7 @@ where
|
||||||
Shaping::Advanced,
|
Shaping::Advanced,
|
||||||
);
|
);
|
||||||
|
|
||||||
width.round() as u32
|
size.width.round() as u32
|
||||||
};
|
};
|
||||||
|
|
||||||
placeholder.map(measure).unwrap_or(100)
|
placeholder.map(measure).unwrap_or(100)
|
||||||
|
|
@ -495,6 +497,7 @@ pub fn overlay<'a, T, Message, Renderer>(
|
||||||
text_size: Option<u16>,
|
text_size: Option<u16>,
|
||||||
font: Renderer::Font,
|
font: Renderer::Font,
|
||||||
options: &'a [T],
|
options: &'a [T],
|
||||||
|
on_selected: &'a dyn Fn(T) -> Message,
|
||||||
style: <Renderer::Theme as StyleSheet>::Style,
|
style: <Renderer::Theme as StyleSheet>::Style,
|
||||||
) -> Option<overlay::Element<'a, Message, Renderer>>
|
) -> Option<overlay::Element<'a, Message, Renderer>>
|
||||||
where
|
where
|
||||||
|
|
@ -511,7 +514,7 @@ where
|
||||||
|
|
||||||
let width = {
|
let width = {
|
||||||
let measure = |label: &str| -> u32 {
|
let measure = |label: &str| -> u32 {
|
||||||
let (width, _) = renderer.measure(
|
let size = renderer.measure(
|
||||||
label,
|
label,
|
||||||
text_size as f32,
|
text_size as f32,
|
||||||
LineHeight::default(),
|
LineHeight::default(),
|
||||||
|
|
@ -520,7 +523,7 @@ where
|
||||||
Shaping::Advanced,
|
Shaping::Advanced,
|
||||||
);
|
);
|
||||||
|
|
||||||
width.round() as u32
|
size.width.round() as u32
|
||||||
};
|
};
|
||||||
|
|
||||||
let labels = options.iter().map(ToString::to_string);
|
let labels = options.iter().map(ToString::to_string);
|
||||||
|
|
@ -534,7 +537,12 @@ where
|
||||||
&mut state.menu,
|
&mut state.menu,
|
||||||
options,
|
options,
|
||||||
&mut state.hovered_option,
|
&mut state.hovered_option,
|
||||||
&mut state.last_selection,
|
|option| {
|
||||||
|
state.is_open = false;
|
||||||
|
|
||||||
|
(on_selected)(option)
|
||||||
|
},
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.width(width)
|
.width(width)
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,7 @@ where
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
_clipboard: &mut dyn Clipboard,
|
_clipboard: &mut dyn Clipboard,
|
||||||
_shell: &mut Shell<'_, Message>,
|
_shell: &mut Shell<'_, Message>,
|
||||||
|
_viewport: &Rectangle<f32>,
|
||||||
) -> Status {
|
) -> Status {
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
let mut editor = self.editor.lock().unwrap();
|
let mut editor = self.editor.lock().unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue