Open leonardosalsi opened 5 days ago
Did you follow the examples? You provided code looks like the basic full_screen example. I tested it on a different OS like (mac, ubunntu, fedora, windows under wsl) and everything was good.
Do you catch the resize event in your app? As I can understand iced provide the resize event within the app starts, after that if widget's subscription initialized, widget handle it and resize inner pty instance (number of rows and cols).
Ciao Harzu, thanks for your reply!
This is the event handler
Message::TerminalEvent(iced_term::Event::CommandReceived(session_id, cmd)) => {
if let Some(terminal) = self.right_panel.view.terminal_drawer.terminals.get_mut(&session_id) {
match terminal.terminal.update(cmd) {
Action::Redraw => {}
Action::Shutdown => {}
Action::ChangeTitle(_) => {}
Action::Ignore => {}
}
}
Task::none()
}
I do catch the event Action::Redraw
and do nothing with it, since I do not need an action. In your example, you use the events Action::Shutdown
and Action::ChangeTitle
. Is there something I am missing when redrawing?
It seems like I need the example for reproducing this behaviour, sorry. You can also look at the split view example.
How I can understand based on your screenshot you lost some events because as I can see container color was filled but content wasn't, it means that backend wasn't receive the resize event.
I can show you the relevant files.
This is where the terminals are defined
use crate::actions::Action;
use crate::backend::{Backend, BackendCommand};
use crate::bindings::{Binding, BindingAction, BindingsLayout, InputKind};
use crate::font::TermFont;
use crate::settings::{BackendSettings, FontSettings, Settings, ThemeSettings};
use crate::theme::{ColorPalette, Theme};
use crate::AlacrittyEvent;
use iced::widget::canvas::Cache;
use tokio::sync::mpsc::Sender;
#[derive(Debug, Clone)]
pub enum Event {
CommandReceived(u64, Command),
}
#[derive(Debug, Clone)]
pub enum Command {
InitBackend(Sender<AlacrittyEvent>),
ChangeTheme(Box<ColorPalette>),
ChangeFont(FontSettings),
AddBindings(Vec<(Binding<InputKind>, BindingAction)>),
ProcessBackendCommand(BackendCommand),
}
pub struct Terminal {
pub id: u64,
pub(crate) font: TermFont,
pub(crate) theme: Theme,
pub(crate) cache: Cache,
pub(crate) bindings: BindingsLayout,
pub(crate) backend: Option<Backend>,
backend_settings: BackendSettings,
}
impl Terminal {
pub fn new(id: u64, settings: Settings) -> Self {
Self {
id,
font: TermFont::new(settings.font),
theme: Theme::new(settings.theme),
bindings: BindingsLayout::default(),
cache: Cache::default(),
backend_settings: settings.backend,
backend: None,
}
}
pub fn widget_id(&self) -> iced::widget::text_input::Id {
iced::widget::text_input::Id::new(self.id.to_string())
}
pub fn update(&mut self, cmd: Command) -> Action {
let mut action = Action::Ignore;
match cmd {
Command::InitBackend(sender) => {
self.backend = Some(
Backend::new(
self.id,
sender,
self.backend_settings.clone(),
self.font.measure,
)
.unwrap_or_else(|_| {
panic!("init pty with ID: {} is failed", self.id);
}),
);
},
Command::ChangeTheme(color_pallete) => {
self.theme = Theme::new(ThemeSettings::new(color_pallete));
action = Action::Redraw;
self.sync_and_redraw();
},
Command::ChangeFont(font_settings) => {
self.font = TermFont::new(font_settings);
if let Some(ref mut backend) = self.backend {
action = backend.process_command(BackendCommand::Resize(
None,
Some(self.font.measure),
));
if action == Action::Redraw {
self.redraw();
}
}
},
Command::AddBindings(bindings) => {
self.bindings.add_bindings(bindings);
},
Command::ProcessBackendCommand(c) => {
if let Some(ref mut backend) = self.backend {
action = backend.process_command(c);
if action == Action::Redraw {
self.redraw();
}
}
},
}
action
}
fn sync_and_redraw(&mut self) {
if let Some(ref mut backend) = self.backend {
backend.sync();
self.redraw();
}
}
fn redraw(&mut self) {
self.cache.clear();
}
}
They are stored in an auxiliary class, which is used by the following file
use iced::{window, Background, Border, Color, Length, Padding, Radians, Shadow, Size, Subscription, Task, Theme, Vector};
use iced::futures::StreamExt;
use iced::gradient::Linear;
use iced::widget::{container, row, stack, text};
use iced::widget::container::Style;
use iced_term::actions::Action;
use iced_term::Event;
use crate::backend::yanaxi::Yanaxi;
use crate::ui::components::left_panel::image_button::ImageButtonMessage;
use crate::ui::components::left_panel::left_panel_menu::LeftPanelMenuMessage;
use crate::ui::components::left_panel::left_panel_menu_entry::LeftPanelMenuEntryMessage;
use crate::ui::components::left_panel::left_panel_topbar::LeftPanelTopBarMessage;
use crate::ui::components::right_panel::right_panel_view::RightPanelViewMessage;
use crate::ui::components::settings_overlay::settings_theme::SettingsThemeMessage;
use crate::ui::components::terminal_drawer::terminal_drawer::TerminalDrawerMessage;
use crate::ui::components::settings_overlay::theme_preview::ThemePreviewMessage;
use crate::ui::components::terminal_drawer::terminal::TerminalMessage;
use crate::ui::left_panel::{LeftPanel, LeftPanelMessage};
use crate::ui::overlay::{Overlay, OverlayMessage, OverlayView};
use crate::ui::overlays::contexts_overlay::ContextsOverlayMessage;
use crate::ui::right_panel::{RightPanel, RightPanelMessage};
use crate::ui::overlays::settings_overlay::SettingsOverlayMessage;
use crate::ui::util::color_ext::ColorExtensions;
use crate::ui::util::theme::{Convert, YanaxiTheme};
use crate::ui::util::theming::window_background_style;
use crate::ui::util::view::{MenuEntryName, MenuGroupName};
const TERMINAL_FONT: &[u8] = include_bytes!("../../resources/fonts/MesloLGS.ttf");
pub struct YanaxiUI {
yanaxi: Yanaxi,
theme: YanaxiTheme,
left_panel: LeftPanel,
right_panel: RightPanel,
overlay: Overlay,
show_overlay: bool,
window_id: window::Id
}
#[derive(Debug, Clone)]
pub enum Message{
LeftPanel(LeftPanelMessage),
RightPanel(RightPanelMessage),
Overlay(OverlayMessage),
TerminalDrawer(TerminalDrawerMessage),
OpenWindow(window::Id),
TerminalDrawerStoreSize(Size),
Close(window::Id),
TerminalEvent(Event),
LoadTerminalFont(Result<(), iced::font::Error>)
}
const MIN_LEFTMENU_WIDTH: f32 = 200.0;
const MAX_LEFTMENU_WIDTH: f32 = 450.0;
impl YanaxiUI {
pub(crate) fn new(yanaxi: Yanaxi) -> (Self, Task<Message>) {
let theme = yanaxi.app_data.user_settings.theme;
let window_settings = window::Settings {
min_size: Option::from(Size {
width: 500.0,
height: 400.0,
}),
exit_on_close_request: true,
..window::Settings::default()
};
let (id, open) = window::open(window_settings);
(
Self {
yanaxi,
theme,
left_panel: LeftPanel::new(),
right_panel: RightPanel::new(theme),
overlay: Overlay::new(),
show_overlay: false,
window_id: id
},
Task::batch(vec![
open.map(Message::OpenWindow),
iced::font::load(TERMINAL_FONT).map(Message::LoadTerminalFont)
])
)
}
pub(crate) fn title(&self, window_id: window::Id) -> String {
String::from("Yanaxi")
}
pub fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::LeftPanel(left_panel_message) => {
match left_panel_message {
LeftPanelMessage::TopBar(ref top_bar_message) => {
match top_bar_message {
LeftPanelTopBarMessage::SettingsButton(settings_button_message) => {
match settings_button_message {
ImageButtonMessage::Press => {
self.show_overlay = !self.show_overlay;
self.overlay.update(OverlayMessage::SwitchView(OverlayView::Settings));
//self.right_panel.update(RightPanelMessage::SwitchView(MenuEntryName::Settings));
//self.left_panel.menu.update(LeftPanelMenuMessage::LeftPanelMenuEntry(LeftPanelMenuEntryMessage::Select(MenuGroupName::None, MenuEntryName::None)));
Task::none()
}
_ => {
self.left_panel.update(left_panel_message);
Task::none()
}
}
}
LeftPanelTopBarMessage::ContextButton(context_button_message) => {
match context_button_message {
ImageButtonMessage::Press => {
self.show_overlay = !self.show_overlay;
self.overlay.update(OverlayMessage::SwitchView(OverlayView::Contexts));
//self.right_panel.update(RightPanelMessage::SwitchView(MenuEntryName::Contexts));
//self.left_panel.menu.update(LeftPanelMenuMessage::LeftPanelMenuEntry(LeftPanelMenuEntryMessage::Select(MenuGroupName::None, MenuEntryName::None)));
Task::none()
}
_ => {
self.left_panel.update(left_panel_message);
Task::none()
}
}
},
}
}
LeftPanelMessage::LeftPanelMenu(left_panel_menu_message) => {
match left_panel_menu_message {
LeftPanelMenuMessage::LeftPanelMenuEntry(left_panel_menu_entry_message) => {
match left_panel_menu_entry_message {
LeftPanelMenuEntryMessage::Select(group, entry) => {
self.right_panel.update(RightPanelMessage::SwitchView(entry));
self.left_panel.menu.update(LeftPanelMenuMessage::LeftPanelMenuEntry(left_panel_menu_entry_message));
Task::none()
}
_ => {
self.left_panel.menu.update(LeftPanelMenuMessage::LeftPanelMenuEntry(left_panel_menu_entry_message));
Task::none()
}
}
}
}
}
_ => {
self.left_panel.update(left_panel_message);
Task::none()
}
}
},
Message::RightPanel(right_panel_message) => {
match right_panel_message {
RightPanelMessage::RightPanelView(right_panel_view_message) => {
match right_panel_view_message {
_ => {
self.right_panel.view.update(right_panel_view_message);
Task::none()
}
}
}
_ => {
self.right_panel.update(right_panel_message);
Task::none()
}
}
},
Message::TerminalDrawer(_terminal_drawer_message) => {
self.right_panel.view.terminal_drawer.update(_terminal_drawer_message);
window::get_size(self.window_id).map(Message::TerminalDrawerStoreSize)
}
Message::TerminalDrawerStoreSize(size) => {
self.right_panel.view.terminal_drawer.update(TerminalDrawerMessage::StoreWindowHeight(size));
Task::none()
}
Message::OpenWindow(window_id) => {
Task::none()
}
Message::Close(window_id) => {
if window_id == self.window_id {
iced::exit()
} else {
Task::none()
}
}
Message::TerminalEvent(iced_term::Event::CommandReceived(session_id, cmd)) => {
if let Some(terminal) = self.right_panel.view.terminal_drawer.terminals.get_mut(&session_id) {
terminal.terminal.update(cmd);
}
Task::none()
}
Message::Overlay(message) => {
match message {
OverlayMessage::Settings(_message) => {
match _message {
SettingsOverlayMessage::SettingsTheme(SettingsThemeMessage::ThemePreview(ThemePreviewMessage::Select(theme))) => {
self.theme = theme;
self.yanaxi.app_data.change_theme(theme);
self.right_panel.view.terminal_drawer.update(TerminalDrawerMessage::Terminal(TerminalMessage::Theme(theme)));
}
}
}
OverlayMessage::Contexts(_message) => {
match _message {
ContextsOverlayMessage::SelectContext(context) => {
println!("Selected context {}", context.name);
//TODO Connect to cluster
}
_ => {
self.overlay.contexts.update(_message);
}
}
}
OverlayMessage::SwitchView(_) => {}
OverlayMessage::Close => {
self.show_overlay = false;
self.overlay.update(message);
}
}
Task::none()
}
Message::LoadTerminalFont(_) => {
Task::none()
}
}
}
pub fn view(&self, window_id: window::Id) -> iced::Element<'_, Message> {
let mut stack = stack![];
let left = self.left_panel.view().map(Message::LeftPanel);
let right= self.right_panel.view().map(Message::RightPanel);
stack = stack.push(row![left, right]);
if self.show_overlay {
let overlay = self.overlay.view().map(Message::Overlay);
stack = stack.push(overlay);
}
container(stack)
.height(Length::Fill)
.padding(0)
.style(|theme: &Theme| window_background_style(theme))
.into()
}
pub fn theme(&self, window_id: window::Id) -> Theme {
self.yanaxi.app_data.user_settings.theme.convert()
}
pub fn subscription(&self) -> Subscription<Message> {
let mut subscriptions = vec![];
let window_close_subscription = window::close_events().map(Message::Close);
subscriptions.push(window_close_subscription);
let left_panel_subscription = self.left_panel.subscription().map(Message::LeftPanel);
subscriptions.push(left_panel_subscription);
let terminal_drawer_subscription = self.right_panel.view.terminal_drawer.subscription().map(Message::TerminalDrawer);
subscriptions.push(terminal_drawer_subscription);
for (id, terminal) in &self.right_panel.view.terminal_drawer.terminals {
let terminal_subscription = iced_term::Subscription::new(terminal.terminal.id);
let terminal_event_stream = terminal_subscription.event_stream();
let subscription = Subscription::run_with_id(terminal.terminal.id, terminal_event_stream)
.map(Message::TerminalEvent);
subscriptions.push(subscription);
}
Subscription::batch(subscriptions)
}
}
P.S: I just noticed that when I resize my application, there is some resizing also happening in the terminal, but again with a weird margin.
For example, when i use the terminal with default window size, it looks like this
When i resize, the terminal itself also seems to resize
I experimented with the full_screen
example and tried to implement something more complex than a single container.
fn view(&self) -> Element<Event, Theme, iced::Renderer> {
let stack = stack![
row![
self.left(),
self.right(),
]
];
container(stack)
.height(Length::Fill)
.padding(0)
.into()
}
fn left(&self) -> Element<Event, Theme, iced::Renderer> {
container(text!("left side"))
.padding(10)
.width(Length::Fixed(200.0))
.height(Length::Fill)
.into()
}
fn right(&self) -> Element<Event, Theme, iced::Renderer> {
let column = column![
container(text("top")).height(Length::Fixed(500.0)),
self.term_view()
];
column.into()
}
fn term_view(&self) -> Element<Event, Theme, iced::Renderer> {
container(TerminalView::show(&self.term).map(Event::Terminal))
.width(Length::Fill)
.height(Length::Fill)
.into()
}
It worked as expected.
The widget is essentially a container with a payload that fills the parent dimensions.
pub fn show(term: &'a Terminal) -> Element<'_, Event> {
container(Self { term })
.width(Length::Fill)
.height(Length::Fill)
.style(|_| term.theme.container_style())
.into()
}
Resizing is handled by comparing the current dimension with those from widget state within the receiving any of ICED events.
let layout_size = layout.bounds().size();
if state.size != layout_size && self.term.backend.is_some() {
state.size = layout_size;
let cmd = Command::ProcessBackendCommand(BackendCommand::Resize(
Some(layout_size),
None,
));
shell.publish(Event::CommandReceived(self.term.id, cmd));
}
If they are not equal widget_view sends the Resize
event with the new dimension to the application. If the application handles it and forwards it to the terminal.update()
method, the terminal backend have to handle it and recalculate number of rows and cols. This method is worked in a lot of cases including the complex (a lot of cases that I tested, of course), so as I can understand the problem could be happen in a several cases:
CommandReceived
event. It is not your case too.f.e
container(
container(
container(TerminalView::show(&self.term).map(Event::Terminal))
.width(Length::Fill)
.height(Length::Fill)
)
.width(Length::Fixed(400.0))
).width(Length::Fill)
.height(Length::Fill)
.into()
I re-checked everything, according to my implementation the terminal is embedded always such that it has to take up its parents dimensions with .width(Length::Fill)
and .height(Length::Fill)
.
I will refactor my code nontheless to make sure that everything is set up correctly.
If you were not able to reproduce the error under similar enough circumstances, I guess you can close the issue, since the problem must lie in the way I embed the terminal.
Thank you very much still, I got a lot of crucial infos from this thread :)
I am currently working on a project with
iced 0.13.1
and useiced_term 0.5.0
to embed a terminal. I instantiate a terminal, which functionally works, and wrap the output ofTerminalView::show
with a container which is defined to take up its parent's dimensions.Now I have the issue, that I cannot 'use' the full width for the terminal. The following screenshot shows the issue I'm currently having.
We can see that, according to the background color used by the terminal, the terminal itself does extend as expected. But whenever I write, i encounter some 'unwritable' area on the right-hand side (as seen by the command and the highlighted text) that does not render any characters. The text is not wrapping, but continuing, as seen from the way the command is cut off.
I followed the example for the fullscreen terminal and compared this implementation with mine extensively, but was not able to find any kind of setting that would fix this.
For context, this is the setup of the terminal itself
Perhaps I missed something, but a hint would be very helpful.