mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Moving Windows in StackView (#200)
* Inital draft of reposition code. * throwing it all in git so I can show @gycos the mess I made * Cleaning up the example and removing that garbage getter function from StackView. More thought needs to be applied to that API. * modified stackview to locally draw background, currently every frame. Adjusted surrounding elements to mirror this change. * fixed dirty flag on stackView to use a cell. Also added dirty events on window resize. * Final code cleanup. * Fixed all highlighted issues in PR #200
This commit is contained in:
parent
22e2360aa1
commit
9e591ef635
56
examples/position.rs
Normal file
56
examples/position.rs
Normal file
@ -0,0 +1,56 @@
|
||||
extern crate cursive;
|
||||
|
||||
use cursive::Cursive;
|
||||
use cursive::view::Position;
|
||||
use cursive::views::LayerPosition;
|
||||
use cursive::views::TextView;
|
||||
|
||||
/// Moves top layer by the specified amount
|
||||
fn move_top(c: &mut Cursive, x_in: isize, y_in: isize) {
|
||||
// Step 1. Get the current position of the layer.
|
||||
let s = c.screen_mut();
|
||||
let l = LayerPosition::FromFront(0);
|
||||
|
||||
let (x, y) = s.offset().pair();
|
||||
|
||||
// Step 2. add the specifed amount
|
||||
// (unsigned math in Rust is a mess.)
|
||||
let x = if x_in < 0 {
|
||||
x - (-x_in) as usize
|
||||
} else {
|
||||
x + x_in as usize
|
||||
};
|
||||
let y = if y_in < 0 {
|
||||
y - (-y_in) as usize
|
||||
} else {
|
||||
y + y_in as usize
|
||||
};
|
||||
|
||||
// convert the new x and y into a position
|
||||
let p = Position::absolute((x, y));
|
||||
|
||||
// Step 3. Apply the new position
|
||||
s.reposition_layer(l, p);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut siv = Cursive::new();
|
||||
siv.set_fps(60);
|
||||
|
||||
// We can quit by pressing `q`
|
||||
siv.add_global_callback('q', Cursive::quit);
|
||||
// Next Gen FPS Controls.
|
||||
siv.add_global_callback('w', |s| move_top(s, 0, -1));
|
||||
siv.add_global_callback('a', |s| move_top(s, -1, 0));
|
||||
siv.add_global_callback('s', |s| move_top(s, 0, 1));
|
||||
siv.add_global_callback('d', |s| move_top(s, 1, 0));
|
||||
|
||||
// Add window to fly around.
|
||||
siv.add_layer(TextView::new(
|
||||
"Press w,a,s,d to move the window.\n\
|
||||
Press q to quit the application.",
|
||||
));
|
||||
|
||||
// Run the event loop
|
||||
siv.run();
|
||||
}
|
@ -9,8 +9,8 @@ use std::path::Path;
|
||||
use std::sync::mpsc;
|
||||
use theme;
|
||||
use vec::Vec2;
|
||||
use view::{self, AnyView, Finder, View};
|
||||
use views;
|
||||
use view::{self, AnyView, Finder, Position, View};
|
||||
use views::{self, LayerPosition};
|
||||
|
||||
/// Identifies a screen in the cursive root.
|
||||
pub type ScreenId = usize;
|
||||
@ -416,9 +416,14 @@ impl Cursive {
|
||||
|
||||
/// Convenient method to remove a layer from the current screen.
|
||||
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
|
||||
let result = self.screen_mut().pop_layer();
|
||||
self.clear();
|
||||
result
|
||||
self.screen_mut().pop_layer()
|
||||
}
|
||||
|
||||
/// Convenient stub forwarding layer repositioning.
|
||||
pub fn reposition_layer(
|
||||
&mut self, layer: LayerPosition, position: Position
|
||||
) {
|
||||
self.screen_mut().reposition_layer(layer, position);
|
||||
}
|
||||
|
||||
// Handles a key event when it was ignored by the current view
|
||||
@ -460,6 +465,15 @@ impl Cursive {
|
||||
let printer =
|
||||
Printer::new(self.screen_size(), &self.theme, &self.backend);
|
||||
|
||||
let selected = self.menubar.receive_events();
|
||||
|
||||
// Print the stackview background before the menubar
|
||||
let offset = if self.menubar.autohide { 0 } else { 1 };
|
||||
let id = self.active_screen;
|
||||
let sv_printer = printer.offset((0, offset), !selected);
|
||||
|
||||
self.screens[id].draw_bg(&sv_printer);
|
||||
|
||||
// Draw the currently active screen
|
||||
// If the menubar is active, nothing else can be.
|
||||
// Draw the menubar?
|
||||
@ -472,12 +486,9 @@ impl Cursive {
|
||||
self.menubar.draw(&printer);
|
||||
}
|
||||
|
||||
let selected = self.menubar.receive_events();
|
||||
|
||||
let offset = if self.menubar.autohide { 0 } else { 1 };
|
||||
let printer = printer.offset((0, offset), !selected);
|
||||
let id = self.active_screen;
|
||||
self.screens[id].draw(&printer);
|
||||
// finally draw stackview layers
|
||||
// using variables from above
|
||||
self.screens[id].draw_fg(&sv_printer);
|
||||
}
|
||||
|
||||
/// Returns `true` until [`quit(&mut self)`] is called.
|
||||
|
@ -3,6 +3,7 @@ use With;
|
||||
use direction::Direction;
|
||||
use event::{Event, EventResult};
|
||||
use std::any::Any;
|
||||
use std::cell;
|
||||
use std::ops::Deref;
|
||||
use theme::ColorStyle;
|
||||
use vec::Vec2;
|
||||
@ -15,6 +16,9 @@ pub struct StackView {
|
||||
// Store layers from back to front.
|
||||
layers: Vec<Child>,
|
||||
last_size: Vec2,
|
||||
// Flag indicates if undrawn areas of the background are exposed
|
||||
// and therefore need redrawing.
|
||||
bg_dirty: cell::Cell<bool>,
|
||||
}
|
||||
|
||||
enum Placement {
|
||||
@ -147,6 +151,7 @@ impl StackView {
|
||||
StackView {
|
||||
layers: Vec::new(),
|
||||
last_size: Vec2::zero(),
|
||||
bg_dirty: cell::Cell::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,6 +230,7 @@ impl StackView {
|
||||
|
||||
/// Remove the top-most layer.
|
||||
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
|
||||
self.bg_dirty.set(true);
|
||||
self.layers.pop().map(|child| child.view.unwrap())
|
||||
}
|
||||
|
||||
@ -280,6 +286,65 @@ impl StackView {
|
||||
pub fn move_to_back(&mut self, layer: LayerPosition) {
|
||||
self.move_layer(layer, LayerPosition::FromBack(0));
|
||||
}
|
||||
|
||||
/// Moves a layer to a new position on the screen.
|
||||
///
|
||||
/// Has no effect on fullscreen layers
|
||||
/// Has no effect if layer is not found
|
||||
pub fn reposition_layer(
|
||||
&mut self, layer: LayerPosition, position: Position
|
||||
) {
|
||||
let i = self.get_index(layer);
|
||||
let child = match self.layers.get_mut(i) {
|
||||
Some(i) => i,
|
||||
None => return,
|
||||
};
|
||||
match child.placement {
|
||||
Placement::Floating(_) => {
|
||||
child.placement = Placement::Floating(position);
|
||||
self.bg_dirty.set(true);
|
||||
}
|
||||
Placement::Fullscreen => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Background drawing
|
||||
/// Drawing functions are split into forground and background to
|
||||
/// ease inserting layers under the stackview but above it's background
|
||||
/// you probably just want to call draw()
|
||||
pub fn draw_bg(&self, printer: &Printer) {
|
||||
// If the background is dirty draw a new background
|
||||
if self.bg_dirty.get() {
|
||||
for y in 0..printer.size.y {
|
||||
printer.with_color(ColorStyle::background(), |printer| {
|
||||
printer.print_hline((0, y), printer.size.x, " ");
|
||||
});
|
||||
}
|
||||
|
||||
// set background as clean, so we don't need to do this every frame
|
||||
self.bg_dirty.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Forground drawing
|
||||
/// Drawing functions are split into forground and background to
|
||||
/// ease inserting layers under the stackview but above it's background
|
||||
/// you probably just want to call draw()
|
||||
pub fn draw_fg(&self, printer: &Printer) {
|
||||
let last = self.layers.len();
|
||||
printer.with_color(ColorStyle::primary(), |printer| {
|
||||
for (i, (v, offset)) in
|
||||
StackPositionIterator::new(self.layers.iter(), printer.size)
|
||||
.enumerate()
|
||||
{
|
||||
v.view.draw(&printer.sub_printer(
|
||||
offset,
|
||||
v.size,
|
||||
i + 1 == last,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct StackPositionIterator<R: Deref<Target = Child>, I: Iterator<Item = R>> {
|
||||
@ -325,22 +390,17 @@ impl<R: Deref<Target = Child>, I: Iterator<Item = R>> Iterator
|
||||
|
||||
impl View for StackView {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
let last = self.layers.len();
|
||||
printer.with_color(ColorStyle::primary(), |printer| {
|
||||
for (i, (v, offset)) in
|
||||
StackPositionIterator::new(self.layers.iter(), printer.size)
|
||||
.enumerate()
|
||||
{
|
||||
v.view.draw(&printer.sub_printer(
|
||||
offset,
|
||||
v.size,
|
||||
i + 1 == last,
|
||||
));
|
||||
}
|
||||
});
|
||||
// This function is included for compat with the view trait,
|
||||
// it should behave the same as calling them seperately, but does
|
||||
// not pause to let you insert in between the layers.
|
||||
self.draw_bg(printer);
|
||||
self.draw_fg(printer);
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
if event == Event::WindowResize {
|
||||
self.bg_dirty.set(true);
|
||||
}
|
||||
// Use the stack position iterator to get the offset of the top layer.
|
||||
// TODO: save it instead when drawing?
|
||||
match StackPositionIterator::new(
|
||||
|
Loading…
Reference in New Issue
Block a user