Skip to content
This repository has been archived by the owner on Aug 6, 2023. It is now read-only.

feat: Add clean option to block #244

Closed
wants to merge 1 commit into from
Closed

Conversation

heyrict
Copy link

@heyrict heyrict commented Mar 14, 2020

Related to: #211

I find the need to make a pop-up dialog in my program, but drawing widgets over drawn buffer will not clean all the underlying cells. Thus I add a reset_area function to buffer.rs to reset cells in a certain Rect. This will make it possible to implement a dialog.

I also adds a clean option to Block to clean the cells first before rendering. Other widgets are untouched.

Screenshot from 2020-03-14 13-39-03

Here is a minimal-example to create a dialog:

content = frame.size();

// The main view
frame.render_widget(
    MainViewWidget::new(self.app),
    content,
);

// The dialogue
if self.app.show_save_model {
    let modal_messages: [Text, _] = ...;
    let modal_buttons: [Text, _] = ...;
    let messages_blk: Block = ...;
    let buttons_blk: Block = ...;
    let (margin_x, margin_y) = calculate_margins(...);

    let layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints([Constraint::Length(6), Constraint::Length(2)].as_ref())
        .vertical_margin(margin_y)
        .horizontal_margin(margin_x)
        .split(content);

    frame.render_widget(
        Paragraph::new(modal_messages.iter()).block(messages_blk.clean(true)),
        layout[0],
    )
    frame.render_widget(
        Buttons::new(buttons_group.iter()).block(buttons_blk.clean(true)),
        layout[1],
    )
}

Comment on lines +94 to +98
pub fn clean(mut self, clean: bool) -> Block<'a> {
self.clean = clean;
self
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn clean(mut self, clean: bool) -> Block<'a> {
self.clean = clean;
self
}
pub fn clean(mut sel) -> Block<'a> {
self.clean = true;
self
}

Maybe this is better?

@extrawurst
Copy link
Contributor

extrawurst commented Mar 17, 2020

I just stumbled on the same challenge. I for now took a different route:

pub struct Clear<T: Widget>(T);

impl<T: Widget> Clear<T> {
    pub fn new(w: T) -> Self {
        Self(w)
    }
}

impl<T: Widget> Widget for Clear<T> {
    fn draw(&mut self, area: Rect, buf: &mut Buffer) {
        if area.width < 2 || area.height < 2 {
            return;
        }

        for x in area.left()..area.right() {
            for y in area.top()..area.bottom() {
                buf.get_mut(x, y).reset();
            }
        }

        self.0.draw(area, buf);
    }
}

this Clear Widget can wrap any other widget and clears out the buffer before rendering the wrapped widget.

see example use:

        Clear::new(
            Paragraph::new(txt.iter())
                .block(Block::default().title("Popup").borders(Borders::ALL))
                .alignment(Alignment::Center),
        )
        .render(f, Rect::new(20, 30, 100, 10));

But yea I think it shows how necessary a official solution is!

@fdehau
Copy link
Owner

fdehau commented Mar 21, 2020

I think that @extrawurst solution might be preferable because it works with all widgets. To make it behave as any other widget we might want to have it render to the same area instead of wrapping a widget instance.

let area = Rect::new(20, 30, 100, 10);
let popup = Paragraph::new(txt.iter())
   .block(Block::default().title("Popup").borders(Borders::ALL))
   .alignment(Alignment::Center);
f.render_widget(Clear, area);
f.render_widget(popup, area);

What do you think ?

@heyrict
Copy link
Author

heyrict commented Mar 21, 2020

So do I! @extrawurst can you make a PR ?

@extrawurst
Copy link
Contributor

Sure I can do that!

@heyrict
Copy link
Author

heyrict commented Mar 25, 2020

Closed in favor of #251

@heyrict heyrict closed this Mar 25, 2020
@manishsingh10895
Copy link

I think that @extrawurst solution might be preferable because it works with all widgets. To make it behave as any other widget we might want to have it render to the same area instead of wrapping a widget instance.

let area = Rect::new(20, 30, 100, 10);
let popup = Paragraph::new(txt.iter())
   .block(Block::default().title("Popup").borders(Borders::ALL))
   .alignment(Alignment::Center);
f.render_widget(Clear, area);
f.render_widget(popup, area);

What do you think ?

This works pretty good, but can you help me with, rendering it on the top, like z-index in css ?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants