chore: update libcosmic

keyboard input has changed a lot in iced, but I think I've made the proper adjustments here. vertical splits don't seem to work, but that didn't seem to work before either. Also, there is a black border on the right and bottom of the first tab of the terminal. I'm not sure why yet, but it seems like it is probably related to the scrollbar
This commit is contained in:
Ashley Wulber 2024-02-05 14:18:19 -05:00
parent adad338e1f
commit 20beeb608a
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
6 changed files with 669 additions and 714 deletions

1139
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,10 @@ rust-embed = "6"
paste = "1.0" paste = "1.0"
palette = "0.7" palette = "0.7"
[dependencies.smol_str]
version = "0.2.1"
features = ["serde"]
[dependencies.cosmic-text] [dependencies.cosmic-text]
git = "https://github.com/pop-os/cosmic-text.git" git = "https://github.com/pop-os/cosmic-text.git"
features = ["shape-run-cache"] features = ["shape-run-cache"]
@ -37,9 +41,6 @@ wgpu = ["libcosmic/wgpu"]
[patch.crates-io] [patch.crates-io]
# https://github.com/rust-lang/libc/pull/3512 # https://github.com/rust-lang/libc/pull/3512
libc = { git = "https://gitlab.redox-os.org/redox-os/liblibc.git", branch = "redox_0.2.151" } libc = { git = "https://gitlab.redox-os.org/redox-os/liblibc.git", branch = "redox_0.2.151" }
smithay-client-toolkit = { git = "https://github.com/pop-os/client-toolkit", branch = "wayland-resize" }
# https://github.com/gfx-rs/wgpu/pull/4959
wgpu = { git = "https://github.com/pop-os/wgpu", branch = "v0.18" }
[profile.release-with-debug] [profile.release-with-debug]
inherits = "release" inherits = "release"

View file

@ -1,4 +1,7 @@
use cosmic::iced::keyboard::{KeyCode, Modifiers}; use cosmic::{
iced::keyboard::{Key, Modifiers},
iced_core::keyboard::key::Named,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt}; use std::{collections::HashMap, fmt};
@ -15,12 +18,12 @@ pub enum Modifier {
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] #[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct KeyBind { pub struct KeyBind {
pub modifiers: Vec<Modifier>, pub modifiers: Vec<Modifier>,
pub key_code: KeyCode, pub key: Key,
} }
impl KeyBind { impl KeyBind {
pub fn matches(&self, modifiers: Modifiers, key_code: KeyCode) -> bool { pub fn matches(&self, modifiers: Modifiers, key: Key) -> bool {
self.key_code == key_code self.key == key
&& modifiers.logo() == self.modifiers.contains(&Modifier::Super) && modifiers.logo() == self.modifiers.contains(&Modifier::Super)
&& modifiers.control() == self.modifiers.contains(&Modifier::Ctrl) && modifiers.control() == self.modifiers.contains(&Modifier::Ctrl)
&& modifiers.alt() == self.modifiers.contains(&Modifier::Alt) && modifiers.alt() == self.modifiers.contains(&Modifier::Alt)
@ -33,7 +36,7 @@ impl fmt::Display for KeyBind {
for modifier in self.modifiers.iter() { for modifier in self.modifiers.iter() {
write!(f, "{:?} + ", modifier)?; write!(f, "{:?} + ", modifier)?;
} }
write!(f, "{:?}", self.key_code) write!(f, "{:?}", self.key)
} }
} }
@ -42,11 +45,11 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
let mut key_binds = HashMap::new(); let mut key_binds = HashMap::new();
macro_rules! bind { macro_rules! bind {
([$($modifier:ident),+ $(,)?], $key_code:ident, $action:ident) => {{ ([$($modifier:ident),+ $(,)?], $key:expr, $action:ident) => {{
key_binds.insert( key_binds.insert(
KeyBind { KeyBind {
modifiers: vec![$(Modifier::$modifier),+], modifiers: vec![$(Modifier::$modifier),+],
key_code: KeyCode::$key_code, key: $key,
}, },
Action::$action, Action::$action,
); );
@ -54,51 +57,55 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
} }
// Standard key bindings // Standard key bindings
bind!([Ctrl, Shift], A, SelectAll); bind!([Ctrl, Shift], Key::Character("A".into()), SelectAll);
bind!([Ctrl, Shift], C, Copy); bind!([Ctrl, Shift], Key::Character("C".into()), Copy);
bind!([Ctrl, Shift], F, Find); bind!([Ctrl, Shift], Key::Character("F".into()), Find);
bind!([Ctrl, Shift], N, WindowNew); bind!([Ctrl, Shift], Key::Character("N".into()), WindowNew);
bind!([Ctrl, Shift], Q, WindowClose); bind!([Ctrl, Shift], Key::Character("Q".into()), WindowClose);
bind!([Ctrl, Shift], T, TabNew); bind!([Ctrl, Shift], Key::Character("T".into()), TabNew);
bind!([Ctrl, Shift], V, Paste); bind!([Ctrl, Shift], Key::Character("V".into()), Paste);
bind!([Ctrl, Shift], W, TabClose); bind!([Ctrl, Shift], Key::Character("W".into()), TabClose);
// Ctrl+Alt+D splits horizontally, Ctrl+Alt+R splits vertically, Ctrl+Shift+X maximizes split // Ctrl+Alt+D splits horizontally, Ctrl+Alt+R splits vertically, Ctrl+Shift+X maximizes split
//TODO: Adjust bindings as desired by UX //TODO: Adjust bindings as desired by UX
bind!([Ctrl, Alt], D, PaneSplitHorizontal); bind!([Ctrl, Alt], Key::Character("d".into()), PaneSplitHorizontal);
bind!([Ctrl, Alt], R, PaneSplitVertical); bind!([Ctrl, Alt], Key::Character("r".into()), PaneSplitVertical);
bind!([Ctrl, Shift], X, PaneToggleMaximized); bind!(
[Ctrl, Shift],
Key::Character("X".into()),
PaneToggleMaximized
);
// Ctrl+Tab and Ctrl+Shift+Tab cycle through tabs // Ctrl+Tab and Ctrl+Shift+Tab cycle through tabs
// Ctrl+Tab is not a special key for terminals and is free to use // Ctrl+Tab is not a special key for terminals and is free to use
bind!([Ctrl], Tab, TabNext); bind!([Ctrl], Key::Named(Named::Tab), TabNext);
bind!([Ctrl, Shift], Tab, TabPrev); bind!([Ctrl, Shift], Key::Named(Named::Tab), TabPrev);
// Ctrl+Shift+# activates tabs by index // Ctrl+Shift+# activates tabs by index
bind!([Ctrl, Shift], Key1, TabActivate0); bind!([Ctrl, Shift], Key::Character("!".into()), TabActivate0);
bind!([Ctrl, Shift], Key2, TabActivate1); bind!([Ctrl, Shift], Key::Character("@".into()), TabActivate1);
bind!([Ctrl, Shift], Key3, TabActivate2); bind!([Ctrl, Shift], Key::Character("#".into()), TabActivate2);
bind!([Ctrl, Shift], Key4, TabActivate3); bind!([Ctrl, Shift], Key::Character("$".into()), TabActivate3);
bind!([Ctrl, Shift], Key5, TabActivate4); bind!([Ctrl, Shift], Key::Character("%".into()), TabActivate4);
bind!([Ctrl, Shift], Key6, TabActivate5); bind!([Ctrl, Shift], Key::Character("^".into()), TabActivate5);
bind!([Ctrl, Shift], Key7, TabActivate6); bind!([Ctrl, Shift], Key::Character("&".into()), TabActivate6);
bind!([Ctrl, Shift], Key8, TabActivate7); bind!([Ctrl, Shift], Key::Character("*".into()), TabActivate7);
bind!([Ctrl, Shift], Key9, TabActivate8); bind!([Ctrl, Shift], Key::Character("(".into()), TabActivate8);
// Ctrl+0, Ctrl+-, and Ctrl+= are not special keys for terminals and are free to use // Ctrl+0, Ctrl+-, and Ctrl+= are not special keys for terminals and are free to use
bind!([Ctrl], Key0, ZoomReset); bind!([Ctrl], Key::Character("0".into()), ZoomReset);
bind!([Ctrl], Minus, ZoomOut); bind!([Ctrl], Key::Character("-".into()), ZoomOut);
bind!([Ctrl], Equals, ZoomIn); bind!([Ctrl], Key::Character("=".into()), ZoomIn);
// Ctrl+Arrows and Ctrl+HJKL move between splits // Ctrl+Arrows and Ctrl+HJKL move between splits
bind!([Ctrl, Shift], Left, PaneFocusLeft); bind!([Ctrl, Shift], Key::Named(Named::ArrowLeft), PaneFocusLeft);
bind!([Ctrl, Shift], H, PaneFocusLeft); bind!([Ctrl, Shift], Key::Character("H".into()), PaneFocusLeft);
bind!([Ctrl, Shift], Down, PaneFocusDown); bind!([Ctrl, Shift], Key::Named(Named::ArrowDown), PaneFocusDown);
bind!([Ctrl, Shift], J, PaneFocusDown); bind!([Ctrl, Shift], Key::Character("J".into()), PaneFocusDown);
bind!([Ctrl, Shift], Up, PaneFocusUp); bind!([Ctrl, Shift], Key::Named(Named::ArrowUp), PaneFocusUp);
bind!([Ctrl, Shift], K, PaneFocusUp); bind!([Ctrl, Shift], Key::Character("K".into()), PaneFocusUp);
bind!([Ctrl, Shift], Right, PaneFocusRight); bind!([Ctrl, Shift], Key::Named(Named::ArrowRight), PaneFocusRight);
bind!([Ctrl, Shift], L, PaneFocusRight); bind!([Ctrl, Shift], Key::Character("L".into()), PaneFocusRight);
key_binds key_binds
} }

View file

@ -12,7 +12,7 @@ use cosmic::{
advanced::graphics::text::font_system, advanced::graphics::text::font_system,
clipboard, event, clipboard, event,
futures::SinkExt, futures::SinkExt,
keyboard::{Event as KeyEvent, KeyCode, Modifiers}, keyboard::{Event as KeyEvent, Key, Modifiers},
subscription::{self, Subscription}, subscription::{self, Subscription},
window, Alignment, Color, Event, Length, Limits, Padding, Point, window, Alignment, Color, Event, Length, Limits, Padding, Point,
}, },
@ -249,7 +249,7 @@ pub enum Message {
DefaultDimFontWeight(usize), DefaultDimFontWeight(usize),
DefaultBoldFontWeight(usize), DefaultBoldFontWeight(usize),
DefaultZoomStep(usize), DefaultZoomStep(usize),
Key(Modifiers, KeyCode), Key(Modifiers, Key),
Find(bool), Find(bool),
FindNext, FindNext,
FindPrevious, FindPrevious,
@ -1017,9 +1017,9 @@ impl Application for App {
log::warn!("failed to find zoom step with index {}", index); log::warn!("failed to find zoom step with index {}", index);
} }
}, },
Message::Key(modifiers, key_code) => { Message::Key(modifiers, key) => {
for (key_bind, action) in self.key_binds.iter() { for (key_bind, action) in self.key_binds.iter() {
if key_bind.matches(modifiers, key_code) { if key_bind.matches(modifiers, key.clone()) {
return self.update(action.message(None)); return self.update(action.message(None));
} }
} }
@ -1631,10 +1631,9 @@ impl Application for App {
Subscription::batch([ Subscription::batch([
event::listen_with(|event, _status| match event { event::listen_with(|event, _status| match event {
Event::Keyboard(KeyEvent::KeyPressed { Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => {
key_code, Some(Message::Key(modifiers, key))
modifiers, }
}) => Some(Message::Key(modifiers, key_code)),
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => { Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
Some(Message::Modifiers(modifiers)) Some(Message::Modifiers(modifiers))
} }

View file

@ -6,6 +6,7 @@ use cosmic::{
widget::{column, horizontal_rule, horizontal_space}, widget::{column, horizontal_rule, horizontal_space},
Alignment, Background, Length, Alignment, Background, Length,
}, },
iced_core::Border,
theme, theme,
widget::{ widget::{
self, self,
@ -95,9 +96,12 @@ pub fn context_menu<'a>(
icon_color: Some(component.on.into()), icon_color: Some(component.on.into()),
text_color: Some(component.on.into()), text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())), background: Some(Background::Color(component.base.into())),
border_radius: 8.0.into(), border: Border {
border_width: 1.0, radius: 8.0.into(),
border_color: component.divider.into(), width: 1.0,
color: component.divider.into(),
},
..Default::default()
} }
})) }))
.width(Length::Fixed(240.0)) .width(Length::Fixed(240.0))

View file

@ -10,12 +10,13 @@ use cosmic::{
iced::{ iced::{
advanced::graphics::text::Raw, advanced::graphics::text::Raw,
event::{Event, Status}, event::{Event, Status},
keyboard::{Event as KeyEvent, KeyCode, Modifiers}, keyboard::{Event as KeyEvent, Key, Modifiers},
mouse::{self, Button, Event as MouseEvent, ScrollDelta}, mouse::{self, Button, Event as MouseEvent, ScrollDelta},
Color, Element, Length, Padding, Point, Rectangle, Size, Vector, Color, Element, Length, Padding, Point, Rectangle, Size, Vector,
}, },
iced_core::{ iced_core::{
clipboard::Clipboard, clipboard::Clipboard,
keyboard::key::Named,
layout::{self, Layout}, layout::{self, Layout},
renderer::{self, Quad, Renderer as _}, renderer::{self, Quad, Renderer as _},
text::Renderer as _, text::Renderer as _,
@ -24,7 +25,7 @@ use cosmic::{
operation::{self, Operation, OperationOutputWrapper}, operation::{self, Operation, OperationOutputWrapper},
tree, Id, Widget, tree, Id, Widget,
}, },
Shell, Border, Shell,
}, },
theme::Theme, theme::Theme,
Renderer, Renderer,
@ -109,7 +110,7 @@ where
TerminalBox::new(terminal) TerminalBox::new(terminal)
} }
impl<'a, Message> Widget<Message, Renderer> for TerminalBox<'a, Message> impl<'a, Message> Widget<Message, cosmic::Theme, Renderer> for TerminalBox<'a, Message>
where where
Message: Clone, Message: Clone,
{ {
@ -121,12 +122,8 @@ where
tree::State::new(State::new()) tree::State::new(State::new())
} }
fn width(&self) -> Length { fn size(&self) -> Size<Length> {
Length::Fill Size::new(Length::Fill, Length::Fill)
}
fn height(&self) -> Length {
Length::Fill
} }
fn layout( fn layout(
@ -160,7 +157,7 @@ where
let height = layout_lines as f32 * buffer.metrics().line_height; let height = layout_lines as f32 * buffer.metrics().line_height;
let size = Size::new(limits.max().width, height); let size = Size::new(limits.max().width, height);
layout::Node::new(limits.resolve(size)) layout::Node::new(limits.resolve(Length::Fill, Length::Fill, size))
}) })
} }
@ -255,9 +252,7 @@ where
view_position, view_position,
Size::new(view_w as f32 + scrollbar_w, view_h as f32), Size::new(view_w as f32 + scrollbar_w, view_h as f32),
), ),
border_radius: 0.0.into(), ..Default::default()
border_width: 0.0,
border_color: Color::TRANSPARENT,
}, },
Color::new( Color::new(
background_color.r() as f32 / 255.0, background_color.r() as f32 / 255.0,
@ -335,9 +330,7 @@ where
self.view_position + $pos_offset, self.view_position + $pos_offset,
Size::new($width, $style_line_height), Size::new($width, $style_line_height),
), ),
border_radius: 0.0.into(), ..Default::default()
border_width: 0.0,
border_color: Color::TRANSPARENT,
} }
}; };
($pos_offset:expr, $style_line_height:expr) => { ($pos_offset:expr, $style_line_height:expr) => {
@ -544,9 +537,12 @@ where
renderer.fill_quad( renderer.fill_quad(
Quad { Quad {
bounds: scrollbar_draw, bounds: scrollbar_draw,
border_radius: (scrollbar_draw.width / 2.0).into(), border: Border {
border_width: 0.0, radius: (scrollbar_draw.width / 2.0).into(),
border_color: Color::TRANSPARENT, width: 0.0,
color: Color::TRANSPARENT,
},
..Default::default()
}, },
scrollbar_color, scrollbar_color,
); );
@ -582,69 +578,70 @@ where
let mut status = Status::Ignored; let mut status = Status::Ignored;
match event { match event {
Event::Keyboard(KeyEvent::KeyPressed { Event::Keyboard(KeyEvent::KeyPressed {
key_code, key: Key::Named(named),
modifiers, modifiers,
..
}) if state.is_focused => { }) if state.is_focused => {
let mod_no = calculate_modifier_number(state); let mod_no = calculate_modifier_number(state);
let escape_code = match key_code { let escape_code = match named {
KeyCode::Insert => csi("2", "~", mod_no), Named::Insert => csi("2", "~", mod_no),
KeyCode::Delete => csi("3", "~", mod_no), Named::Delete => csi("3", "~", mod_no),
KeyCode::PageUp => csi("5", "~", mod_no), Named::PageUp => csi("5", "~", mod_no),
KeyCode::PageDown => csi("6", "~", mod_no), Named::PageDown => csi("6", "~", mod_no),
KeyCode::Up => { Named::ArrowUp => {
if is_app_cursor { if is_app_cursor {
ss3("A", mod_no) ss3("A", mod_no)
} else { } else {
csi("A", "", mod_no) csi("A", "", mod_no)
} }
} }
KeyCode::Down => { Named::ArrowDown => {
if is_app_cursor { if is_app_cursor {
ss3("B", mod_no) ss3("B", mod_no)
} else { } else {
csi("B", "", mod_no) csi("B", "", mod_no)
} }
} }
KeyCode::Right => { Named::ArrowRight => {
if is_app_cursor { if is_app_cursor {
ss3("C", mod_no) ss3("C", mod_no)
} else { } else {
csi("C", "", mod_no) csi("C", "", mod_no)
} }
} }
KeyCode::Left => { Named::ArrowLeft => {
if is_app_cursor { if is_app_cursor {
ss3("D", mod_no) ss3("D", mod_no)
} else { } else {
csi("D", "", mod_no) csi("D", "", mod_no)
} }
} }
KeyCode::Home => { Named::End => {
if is_app_cursor { if is_app_cursor {
ss3("H", mod_no) ss3("H", mod_no)
} else { } else {
csi("H", "", mod_no) csi("H", "", mod_no)
} }
} }
KeyCode::End => { Named::Home => {
if is_app_cursor { if is_app_cursor {
ss3("F", mod_no) ss3("F", mod_no)
} else { } else {
csi("F", "", mod_no) csi("F", "", mod_no)
} }
} }
KeyCode::F1 => ss3("P", mod_no), Named::F1 => ss3("P", mod_no),
KeyCode::F2 => ss3("Q", mod_no), Named::F2 => ss3("Q", mod_no),
KeyCode::F3 => ss3("R", mod_no), Named::F3 => ss3("R", mod_no),
KeyCode::F4 => ss3("S", mod_no), Named::F4 => ss3("S", mod_no),
KeyCode::F5 => csi("15", "~", mod_no), Named::F5 => csi("15", "~", mod_no),
KeyCode::F6 => csi("17", "~", mod_no), Named::F6 => csi("17", "~", mod_no),
KeyCode::F7 => csi("18", "~", mod_no), Named::F7 => csi("18", "~", mod_no),
KeyCode::F8 => csi("19", "~", mod_no), Named::F8 => csi("19", "~", mod_no),
KeyCode::F9 => csi("20", "~", mod_no), Named::F9 => csi("20", "~", mod_no),
KeyCode::F10 => csi("21", "~", mod_no), Named::F10 => csi("21", "~", mod_no),
KeyCode::F11 => csi("23", "~", mod_no), Named::F11 => csi("23", "~", mod_no),
KeyCode::F12 => csi("24", "~", mod_no), Named::F12 => csi("24", "~", mod_no),
_ => None, _ => None,
}; };
if let Some(escape_code) = escape_code { if let Some(escape_code) = escape_code {
@ -657,13 +654,13 @@ where
//Also special handle Ctrl-_ to behave like xterm //Also special handle Ctrl-_ to behave like xterm
//Let CharacterRecieved event handle Ctrl keys if possible //Let CharacterRecieved event handle Ctrl keys if possible
let alt_prefix = if modifiers.alt() { "\x1B" } else { "" }; let alt_prefix = if modifiers.alt() { "\x1B" } else { "" };
match key_code { match named {
KeyCode::Enter if !modifiers.control() => { Named::Enter if !modifiers.control() => {
terminal terminal
.input_scroll(format!("{}{}", alt_prefix, "\x0D").as_bytes().to_vec()); .input_scroll(format!("{}{}", alt_prefix, "\x0D").as_bytes().to_vec());
status = Status::Captured; status = Status::Captured;
} }
KeyCode::Escape if !modifiers.control() => { Named::Escape if !modifiers.control() => {
//Escape with any modifier will cancel selection //Escape with any modifier will cancel selection
let had_selection = { let had_selection = {
let mut term = terminal.term.lock(); let mut term = terminal.term.lock();
@ -678,39 +675,40 @@ where
} }
status = Status::Captured; status = Status::Captured;
} }
KeyCode::Backspace if !modifiers.control() => { Named::Backspace if !modifiers.control() => {
let code = "\x7f"; let code = "\x7f";
terminal terminal
.input_scroll(format!("{}{}", alt_prefix, code).as_bytes().to_vec()); .input_scroll(format!("{}{}", alt_prefix, code).as_bytes().to_vec());
status = Status::Captured; status = Status::Captured;
} }
KeyCode::Tab => { Named::Tab => {
let code = if modifiers.shift() { "\x1b[Z" } else { "\x09" }; let code = if modifiers.shift() { "\x1b[Z" } else { "\x09" };
terminal terminal
.input_scroll(format!("{}{}", alt_prefix, code).as_bytes().to_vec()); .input_scroll(format!("{}{}", alt_prefix, code).as_bytes().to_vec());
status = Status::Captured; status = Status::Captured;
} }
KeyCode::Underline if modifiers.control() => {
terminal.input_scroll(b"\x1F".as_slice());
status = Status::Captured;
}
_ => {} _ => {}
} }
} }
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => { Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
state.modifiers = modifiers; state.modifiers = modifiers;
} }
Event::Keyboard(KeyEvent::CharacterReceived(character)) if state.is_focused => { Event::Keyboard(KeyEvent::KeyPressed {
text,
modifiers,
key,
..
}) if state.is_focused => {
//Tab and Delete is handled by the KeyPress event //Tab and Delete is handled by the KeyPress event
let character = text.and_then(|c| c.chars().next()).unwrap_or_default();
if character as u32 == 9 || character as u32 == 127 { if character as u32 == 9 || character as u32 == 127 {
return status; return status;
} }
match ( match (
state.modifiers.logo(), modifiers.logo(),
state.modifiers.control(), modifiers.control(),
state.modifiers.alt(), modifiers.alt(),
state.modifiers.shift(), modifiers.shift(),
) { ) {
(true, _, _, _) => { (true, _, _, _) => {
// Ignore super // Ignore super
@ -739,7 +737,14 @@ where
} }
} }
(false, true, _, true) => { (false, true, _, true) => {
// Ignore ctrl+shift //This is normally Ctrl+Minus, but since that
//is taken by zoom, we send that code for
//Ctrl+Underline instead, like xterm and
//gnome-terminal
if key == Key::Character("_".into()) {
terminal.input_scroll(b"\x1F".as_slice());
status = Status::Captured;
}
} }
(false, false, true, _) => { (false, false, true, _) => {
if !character.is_control() { if !character.is_control() {
@ -1010,7 +1015,7 @@ fn shade(color: cosmic_text::Color, is_focused: bool) -> cosmic_text::Color {
} }
} }
impl<'a, Message> From<TerminalBox<'a, Message>> for Element<'a, Message, Renderer> impl<'a, Message> From<TerminalBox<'a, Message>> for Element<'a, Message, cosmic::Theme, Renderer>
where where
Message: Clone + 'a, Message: Clone + 'a,
{ {