1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
//! The `WindowInner` struct is the internal representation of a `Window` used by the window manager.
//!
//! In comparison, the `Window` struct is application-facing, meaning it is used by (owned by)
//! and exposed directly to applications or tasks that wish to display content.
//!
//! The `WindowInner` in the window_manager-facing version of the `Window`,
//! and each `Window` contains a reference to its `WindowInner`.
//!
//! The window manager typically holds `Weak` references to a `WindowInner` struct,
//! which allows it to control the window itself and handle non-application-related components of the window,
//! such as the title bar, border, etc.
//!
//! It also allows the window manager to control the window, e.g., move, hide, show, or resize it
//! in a way that applications may not be able to do.
#![no_std]
extern crate mpmc;
extern crate event_types;
extern crate framebuffer;
extern crate shapes;
use mpmc::Queue;
use event_types::{Event};
use framebuffer::{Framebuffer, AlphaPixel};
use shapes::{Coord, Rectangle};
// The title bar height, in number of pixels
pub const DEFAULT_TITLE_BAR_HEIGHT: usize = 16;
// left, right, bottom border size, in number of pixels
pub const DEFAULT_BORDER_SIZE: usize = 2;
/// Whether a window is moving (being dragged by the mouse).
pub enum WindowMovingStatus {
/// The window is not in motion.
Stationary,
/// The window is currently in motion.
/// The enclosed `Coord` represents the initial position of the window before it started moving.
Moving(Coord),
}
/// The `WindowInner` struct is the internal system-facing representation of a window.
/// Its members and functions describe the size, state, and events related to window handling,
/// including elements like:
/// * The underlying virtual framebuffer to which the window is rendered,
/// * THe location and dimensions of the window in the final screen,
/// * The window's title bar, buttons, and borders,
/// * Queues for events that have been received by this window, and more.
///
/// The window manager directly interacts with instances of `WindowInner` rather than `Window`,
/// and the application tasks should not have direct access to this struct for correctness reasons.
/// See the crate-level documentation for more details about how to use this
/// and how it differs from `Window`.
pub struct WindowInner {
/// The position of the top-left corner of the window,
/// expressed relative to the top-left corner of the screen.
coordinate: Coord,
/// The width of the border in pixels.
/// By default, there is a border on the left, right, and bottom edges of the window.
pub border_size: usize,
/// The height of title bar in pixels.
/// By default, there is one title bar at the top edge of the window.
pub title_bar_height: usize,
/// The producer side of this window's event queue.
/// Entities that want to send events to this window (or the application that owns this window)
/// should push events onto this queue.
///
/// The corresponding consumer for this event queue is found in the `Window` struct
/// that created and owns this `WindowInner` instance.
event_producer: Queue<Event>, // event output used by window manager
/// The virtual framebuffer that is used exclusively for rendering only this window.
framebuffer: Framebuffer<AlphaPixel>,
/// Whether a window is moving or stationary.
///
/// TODO: FIXME (kevinaboos): this should be private, and window moving logic should be moved into this crate.
pub moving: WindowMovingStatus,
}
impl WindowInner {
/// Creates a new `WindowInner` object backed by the given `framebuffer`
/// and that will be rendered at the given `coordinate` relative to the screen.
pub fn new(
coordinate: Coord,
framebuffer: Framebuffer<AlphaPixel>,
event_producer: Queue<Event>,
) -> WindowInner {
WindowInner {
coordinate,
border_size: DEFAULT_BORDER_SIZE,
title_bar_height: DEFAULT_TITLE_BAR_HEIGHT,
event_producer,
framebuffer,
moving: WindowMovingStatus::Stationary,
}
}
/// Returns `true` if the given `coordinate` (relative to the top-left corner of this window)
/// is within the bounds of this window.
pub fn contains(&self, coordinate: Coord) -> bool {
self.framebuffer.contains(coordinate)
}
/// Gets the size of a window in pixels
pub fn get_size(&self) -> (usize, usize) {
self.framebuffer.get_size()
}
/// Gets the top-left position of the window relative to the top-left of the screen
pub fn get_position(&self) -> Coord {
self.coordinate
}
/// Sets the top-left position of the window relative to the top-left of the screen
pub fn set_position(&mut self, coordinate: Coord) {
self.coordinate = coordinate;
}
/// Returns an immutable reference to this window's virtual Framebuffer.
pub fn framebuffer(&self) -> &Framebuffer<AlphaPixel> {
&self.framebuffer
}
/// Returns a mutable reference to this window's virtual Framebuffer.
pub fn framebuffer_mut(&mut self) -> &mut Framebuffer<AlphaPixel> {
&mut self.framebuffer
}
/// Returns the pixel value at the given `coordinate`,
/// if the `coordinate` is within the window's bounds.
pub fn get_pixel(&self, coordinate: Coord) -> Option<AlphaPixel> {
self.framebuffer.get_pixel(coordinate)
}
/// Returns the size of the Window border in pixels.
/// There is a border drawn on the left, right, and bottom edges.
pub fn get_border_size(&self) -> usize {
self.border_size
}
/// Returns the size of the Window title bar in pixels.
/// There is a title bar drawn on the top edge of the Window.
pub fn get_title_bar_height(&self) -> usize {
self.title_bar_height
}
/// Returns the position and dimensions of the Window's content region,
/// i.e., the area within the window excluding the title bar and border.
///
/// The returned `Rectangle` is expressed relative to this Window's position.
pub fn content_area(&self) -> Rectangle {
let (window_width, window_height) = self.get_size();
// There is one title bar on top, and a border on the left, right, and bottom
let top_left = Coord::new(self.border_size as isize, self.title_bar_height as isize);
let bottom_right = Coord::new((window_width - self.border_size) as isize, (window_height - self.border_size) as isize);
Rectangle { top_left, bottom_right }
}
/// Resizes and moves this window to fit the given `Rectangle` that describes its new position.
pub fn resize(&mut self, new_position: Rectangle) -> Result<(), &'static str> {
// First, perform the actual resize of the inner window
self.coordinate = new_position.top_left;
self.framebuffer = Framebuffer::new(new_position.width(), new_position.height(), None)?;
// Second, send a resize event to that application window (the `Window` object)
// so it knows to refresh its display.
// Rather than send the total size of the whole window,
// we instead send the size and position of the inner content area of the window.
// This prevents the application from thinking it can render over the area
// that contains this window's title bar or border.
self.send_event(Event::new_window_resize_event(self.content_area()))
.map_err(|_e| "Failed to enqueue the resize event; window event queue was full.")?;
Ok(())
}
/// Sends the given `event` to this window.
///
/// If the event queue was full, `Err(event)` is returned.
pub fn send_event(&self, event: Event) -> Result<(), Event> {
self.event_producer.push(event)
}
}