use crate::prelude::*;
pub use self::builder::WindowBuilder as Builder;
pub fn popup <A, P> (
interface : &mut Interface <A, P>,
parent_id : NodeId,
title : String,
size : Size,
activate_pointer : bool,
appearances : Option <Appearances>,
border : Option <Border>
) -> [NodeId; 3] where
A : Application,
P : Presentation
{
let layout = layout::Free::middle_center (size);
let mut builder = Builder::<A, P>::new (interface, &parent_id, title).layout (layout);
set_option!(builder, title_appearances, appearances.clone());
set_option!(builder, frame_appearances, appearances);
set_option!(builder, border, border);
let [container_id, titlebar_id, contents_id] = builder.build();
let _ = interface.action (&container_id, Action::Focus);
if activate_pointer {
let bind_pointer = Box::new (|controller : &mut Controller|
controller.add_bindings (&bindings::Builder::<A>::new()
.buttons (vec![(
controls::button::Builtin::ActivatePointer.into(),
input::button::Mouse::Mouse1.into()
).into()])
.build()
)
);
let _ = interface.action (&contents_id, Action::ModifyController (bind_pointer));
}
[container_id, titlebar_id, contents_id]
}
#[expect(clippy::too_many_arguments)]
pub fn popup_dialog <A, P> (
interface : &mut Interface <A, P>,
parent_id : NodeId,
title : String,
size : Size,
buttons : Vec <(String, application::CallbackId)>,
bindings : &Bindings <A>,
activate_pointer : bool,
appearances : Option <Appearances>,
border : Option <Border>
) -> [NodeId; 3] where
A : Application + 'static,
P : Presentation
{
debug_assert!(!buttons.is_empty());
let [container_id, titlebar_id, contents_id] =
popup (interface, parent_id, title, size, activate_pointer, appearances, border);
let switch_off = {
let trigger = switch::Trigger::new::<A> (
controls::button::Builtin::FormSubmitCallback.into(), None);
switch::Builder::default()
.style_fg ((switch::State::Off, State::Enabled), Color::WHITE)
.style_bg ((switch::State::Off, State::Enabled), Color::RED)
.style_fg ((switch::State::On, State::Enabled), Color::RED)
.style_bg ((switch::State::On, State::Enabled), Color::WHITE)
.style_fg ((switch::State::Off, State::Focused), Color::RED)
.style_bg ((switch::State::Off, State::Focused), Color::WHITE)
.style_fg ((switch::State::On, State::Focused), Color::WHITE)
.style_bg ((switch::State::On, State::Focused), Color::RED)
.enter (switch::State::On, trigger)
};
for (label, callback_id) in buttons {
let size = Size {
width: ((label.len() as u32) + 2).into(),
height: 3.into()
};
let layout = layout::Variant::from (layout::Free::tile (size));
let actions = button::Builder::<A>::new (interface.elements(), &contents_id)
.bindings (bindings)
.callback_id (callback_id)
.switch (switch_off.clone().label (label).build())
.frame_border (Border::eascii_lines())
.frame_layout (layout.into())
.build_actions();
let _ = interface.actions (actions);
}
{ let anchor = Alignment {
horizontal: alignment::Horizontal::Center,
vertical: alignment::Vertical::Bottom
};
let actions = frame::free::rearrange_absolute (
interface.elements(), &contents_id, anchor, (0, 1),
Orientation::Horizontal, 1, false);
let _ = interface.actions (actions);
}
{ let selection = Selection { loop_: true, .. Selection::default() };
let actions = menu::Builder::<A>::new (interface.elements(), &container_id)
.bindings (bindings)
.selection (selection)
.build_actions();
let _ = interface.actions (actions);
}
let _ = interface.action (&container_id, Action::Focus);
[container_id, titlebar_id, contents_id]
}
pub fn set_title (
elements : &Tree <Element>,
window_id : &NodeId,
title : String,
action_buffer : &mut Vec <(NodeId, Action)>
) {
let titlebar_id = elements.children_ids (window_id).unwrap().next().unwrap();
let textbox_id = elements.children_ids (titlebar_id).unwrap().next().unwrap();
textbox::set_contents (elements, textbox_id, title, action_buffer);
}
mod builder {
use derive_builder::Builder;
use crate::prelude::*;
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private, name="build_"),
setter(strip_option))]
struct Window <'a, A : Application, P : Presentation> {
interface : &'a mut Interface <A, P>,
parent_id : &'a NodeId,
title : String,
#[builder(default)]
title_appearances : Appearances,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
border : Option <Border>,
#[builder(default)]
frame_appearances : Appearances,
#[builder(default)]
layout : layout::Free,
#[builder(default)]
name : Option <String>,
#[builder(default)]
orientation : (Orientation, Area)
}
impl <'a, A : Application, P : Presentation> WindowBuilder <'a, A, P> {
pub const fn new (
interface : &'a mut Interface <A, P>,
parent_id : &'a NodeId,
title : String
) -> Self {
WindowBuilder {
interface: Some (interface),
parent_id: Some (parent_id),
title: Some (title),
title_appearances: None,
bindings: None,
border: None,
frame_appearances: None,
layout: None,
name: None,
orientation: None
}
}
pub fn build (self) -> [NodeId; 3] {
let Window {
interface, parent_id, title, title_appearances, bindings, border,
frame_appearances, layout, name, orientation
} = self.build_()
.map_err(|err| log::error!("window builder error: {err:?}"))
.unwrap();
let container_id = {
let mut builder = frame::free::Builder::<A>::new (
interface.elements(), parent_id
) .appearances (AppearancesBuilder::transparent().build())
.layout (layout)
.orientation ((Orientation::Vertical, Area::Interior));
set_option!(builder, name, name);
let frame = builder.build_element();
interface.create_singleton (parent_id, frame, CreateOrder::Append)
};
let titlebar_id = {
let scroll = Scroll {
alignment: Alignment::middle_center(),
.. Scroll::default_tile_absolute()
};
let layout = layout::Variant::from (
layout::Tiled::Absolute (std::num::NonZeroU32::new (3).unwrap()));
let mut builder = textbox::Builder::<A>::new (
interface.elements(), &container_id
) .appearances (title_appearances)
.frame_appearances (frame_appearances.clone())
.frame_layout (layout.into())
.scroll (scroll)
.text (title);
set_option!(builder, frame_border, border.clone());
build_and_return_node_id!(interface, builder)
};
let _ = interface.action (&titlebar_id, Action::Disable);
let contents_id = {
let mut builder = frame::tiled::Builder::<A>::new (
interface.elements(), &container_id
) .appearances (frame_appearances)
.orientation (orientation);
set_option!(builder, bindings, bindings);
set_option!(builder, border, border);
build_and_return_node_id!(interface, builder)
};
[container_id, titlebar_id, contents_id]
}
}
}