Single Window Mode
A single-window mode is an AppCUI application that hosts exactly one window, docked to fill the desktop. Start it with App::single_window(factory). The result is a SingleWindowAppBuilder that you configure and then start with .run().
This mode is meant for tools that do not need a window list, a custom desktop, or overlapping frames. The factory is invoked once, after the runtime is created, and must return a non-modal window (T implements Control, WindowControl, and NotModalWindow). Closing that window ends the application.
The factory can be a closure or a function:
- A closure — use this when you build the window inline, add children, or
newtakes arguments (|| MyWin::new("Title")):
fn main() -> Result<(), appcui::system::Error> {
App::single_window(|| {
let mut win = window!("title:'Demo',d:f");
win.add(label!("'Hello World !',a:c,w:13,h:1"));
win
})
.size(Size::new(40, 10))
.run()
}
- A function or constructor with no arguments — pass
MyWin::new(no parentheses).fn() -> TimplementsFnOnce() -> T, so the constructor is invoked later, after the runtime exists. Do not writeMyWin::new()here; that would create the window immediately.
fn main() -> Result<(), appcui::system::Error> {
App::single_window(MyWin::new).size(Size::new(40, 10)).run()
}
A free function works the same way: App::single_window(hello_world_window).
The layout you pass to the window is ignored. AppCUI replaces it so that the window occupies the entire visible desktop (leaving room for the application bar and command bar when they are enabled). Using d:f (docked, fill) in the window! macro makes that intent explicit.
Read more about creating windows and handling their events in the Window section, and about this layout in Single Window Apps.
Additional constructor methods
Besides the methods described in Builder the following methods are available for a single-window mode setup:
.app_bar()to enable the application top app bar. Read more in Application bar..command_bar()to enable the application command bar. Read more in Command bar..theme(custom_theme)to set up a custom theme or another predefined theme. Read more on themes in the Themes section..timers_count(count)to set up the number of timers that can be used in the application (if not specified the default value is 4)
There is no .window(...) method on this builder. The one window is the factory passed to App::single_window(...). There is also no .desktop(...) method; a custom desktop can only be set in multi-window mode.
Window behavior
The single window is created with the FixedPosition flag. It cannot be moved or resized, and it has no maximize or resize grip. The visible area is the terminal minus the app bar (top) and the command bar (bottom), if those bars are enabled.
Closing the window (the close button, Escape, or close()) closes the application. You can intercept that with WindowEvents::on_cancel and return ActionRequest::Deny if you need a confirmation dialog.
Because there is only one window, desktop actions such as Tab / Ctrl+Tab window cycling and window hotkeys have no effect.
Remarks
- Single-window mode is the opposite of multi-window mode. Here you cannot register a second window, use a custom desktop, or allow the window to move or resize.
.desktop(...)is not available on this builder. A custom desktop can only be set in multi-window mode, viaApp::new().desktop(...).- The
Sizeablewindow flag is not allowed. If it is set, the code panics:// the following line will panic App::single_window(|| window!("Test,a:c,flags: Sizeable")).run() - The factory passed to
App::single_window(...)must return a non-modal window. Open modal dialogs later from the running window, not from the factory.
Example
The following example is a small calculator that fills the terminal. Closing the window asks for confirmation.
use appcui::prelude::*;
#[Window(events = ButtonEvents+WindowEvents)]
struct MyWindow {
info: Handle<Label>,
number: Handle<TextField>,
}
impl MyWindow {
fn new() -> Self {
let mut w = Self {
base: window!("title:'Square root',d:f"),
info: Handle::None,
number: Handle::None,
};
w.number = w.add(textfield!("l:1,t:1,r:1,h:1"));
w.info = w.add(label!("'',l:1,t:3,r:1,h:1"));
w.add(button!("Compute,x:50%,y:100%,w:20,p:b"));
w
}
}
impl ButtonEvents for MyWindow {
fn on_pressed(&mut self, _handle: Handle<Button>) -> EventProcessStatus {
let value: Option<f64> = if let Some(txt) = self.control(self.number) {
txt.text().parse().ok()
} else {
None
};
let h = self.info;
if let (Some(v), Some(i)) = (value, self.control_mut(h)) {
i.set_caption(format!("SQRT({})={}", v, v.sqrt()).as_str());
}
EventProcessStatus::Processed
}
}
impl WindowEvents for MyWindow {
fn on_cancel(&mut self) -> ActionRequest {
if dialogs::validate("Close", "Do you want to close the application ?") {
ActionRequest::Allow
} else {
ActionRequest::Deny
}
}
}
fn main() -> Result<(), appcui::system::Error> {
App::single_window(MyWindow::new).size(Size::new(40, 10)).run()
}