mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2026-01-22 09:15:55 +00:00
239 lines
6.4 KiB
Rust
239 lines
6.4 KiB
Rust
use crate::Result;
|
|
use crate::popup::PopupShell;
|
|
use layer_shika_domain::dimensions::LogicalRect;
|
|
use layer_shika_domain::value_objects::handle::PopupHandle;
|
|
use layer_shika_domain::value_objects::output_target::OutputTarget;
|
|
use layer_shika_domain::value_objects::popup_behavior::ConstraintAdjustment;
|
|
use layer_shika_domain::value_objects::popup_config::PopupConfig;
|
|
use layer_shika_domain::value_objects::popup_position::{
|
|
Alignment, AnchorPoint, Offset, PopupPosition,
|
|
};
|
|
use layer_shika_domain::value_objects::popup_size::PopupSize;
|
|
|
|
/// Type state indicating the builder is not bound to a shell
|
|
pub struct Unbound;
|
|
|
|
/// Type state indicating the builder is bound to a shell
|
|
pub struct Bound {
|
|
shell: PopupShell,
|
|
}
|
|
|
|
/// Builder for configuring popups
|
|
///
|
|
/// The builder uses phantom types to ensure compile-time safety:
|
|
/// - [`PopupBuilder<Unbound>`] - Configuration only, cannot show popups
|
|
/// - [`PopupBuilder<Bound>`] - Has shell reference, can show popups
|
|
///
|
|
/// # Example
|
|
/// ```rust,ignore
|
|
/// shell.on("Main", "open_menu", |control| {
|
|
/// let popup_handle = control.popups().builder("MenuPopup")
|
|
/// .at_cursor()
|
|
/// .grab(true)
|
|
/// .close_on("menu_closed")
|
|
/// .show()?;
|
|
/// });
|
|
/// ```
|
|
pub struct PopupBuilder<State = Unbound> {
|
|
state: State,
|
|
config: PopupConfig,
|
|
}
|
|
|
|
impl PopupBuilder<Unbound> {
|
|
/// Creates a new popup builder for the specified component
|
|
///
|
|
/// This builder is unbound and cannot show popups directly.
|
|
/// Use [`PopupShell::builder`] to create a bound builder that can call `.show()`.
|
|
#[must_use]
|
|
pub fn new(component: impl Into<String>) -> Self {
|
|
Self {
|
|
state: Unbound,
|
|
config: PopupConfig::new(component),
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub(crate) fn with_shell(self, shell: PopupShell) -> PopupBuilder<Bound> {
|
|
PopupBuilder {
|
|
state: Bound { shell },
|
|
config: self.config,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<State> PopupBuilder<State> {
|
|
#[must_use]
|
|
pub fn position(mut self, position: PopupPosition) -> Self {
|
|
self.config.position = position;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn at_cursor(self) -> Self {
|
|
self.position(PopupPosition::Cursor {
|
|
offset: Offset::default(),
|
|
})
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn at_position(self, x: f32, y: f32) -> Self {
|
|
self.position(PopupPosition::Absolute { x, y })
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn centered(self) -> Self {
|
|
self.position(PopupPosition::Centered {
|
|
offset: Offset::default(),
|
|
})
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn relative_to_rect(
|
|
self,
|
|
rect: LogicalRect,
|
|
anchor: AnchorPoint,
|
|
alignment: Alignment,
|
|
) -> Self {
|
|
self.position(PopupPosition::Element {
|
|
rect,
|
|
anchor,
|
|
alignment,
|
|
})
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn offset(mut self, x: f32, y: f32) -> Self {
|
|
match &mut self.config.position {
|
|
PopupPosition::Absolute { x: abs_x, y: abs_y } => {
|
|
*abs_x += x;
|
|
*abs_y += y;
|
|
}
|
|
PopupPosition::Cursor { offset }
|
|
| PopupPosition::Centered { offset }
|
|
| PopupPosition::RelativeToParent { offset, .. } => {
|
|
offset.x += x;
|
|
offset.y += y;
|
|
}
|
|
PopupPosition::Element { .. } => {
|
|
self.config.position = PopupPosition::Cursor {
|
|
offset: Offset { x, y },
|
|
};
|
|
}
|
|
}
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn size(mut self, size: PopupSize) -> Self {
|
|
self.config.size = size;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn fixed_size(self, width: f32, height: f32) -> Self {
|
|
self.size(PopupSize::Fixed { width, height })
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn min_size(self, width: f32, height: f32) -> Self {
|
|
self.size(PopupSize::Minimum { width, height })
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn max_size(self, width: f32, height: f32) -> Self {
|
|
self.size(PopupSize::Maximum { width, height })
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn content_sized(self) -> Self {
|
|
self.size(PopupSize::Content)
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn grab(mut self, enable: bool) -> Self {
|
|
self.config.behavior.grab = enable;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn modal(mut self, enable: bool) -> Self {
|
|
self.config.behavior.modal = enable;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn close_on_click_outside(mut self) -> Self {
|
|
self.config.behavior.close_on_click_outside = true;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn close_on_escape(mut self) -> Self {
|
|
self.config.behavior.close_on_escape = true;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn constraint_adjustment(mut self, adjustment: ConstraintAdjustment) -> Self {
|
|
self.config.behavior.constraint_adjustment = adjustment;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn on_output(mut self, target: OutputTarget) -> Self {
|
|
self.config.output = target;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn on_primary(self) -> Self {
|
|
self.on_output(OutputTarget::Primary)
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn on_active(self) -> Self {
|
|
self.on_output(OutputTarget::Active)
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn parent(mut self, parent: PopupHandle) -> Self {
|
|
self.config.parent = Some(parent);
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn z_index(mut self, index: i32) -> Self {
|
|
self.config.z_index = index;
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn close_on(mut self, callback_name: impl Into<String>) -> Self {
|
|
self.config.close_callback = Some(callback_name.into());
|
|
self
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn resize_on(mut self, callback_name: impl Into<String>) -> Self {
|
|
self.config.resize_callback = Some(callback_name.into());
|
|
self
|
|
}
|
|
|
|
/// Builds the configuration without showing the popup
|
|
///
|
|
/// Returns a [`PopupConfig`] that can be shown later using [`PopupShell::show`].
|
|
#[must_use]
|
|
pub fn build(self) -> PopupConfig {
|
|
self.config
|
|
}
|
|
}
|
|
|
|
impl PopupBuilder<Bound> {
|
|
/// Shows the popup with the configured settings
|
|
///
|
|
/// This method is only available on builders created via [`PopupShell::builder`],
|
|
/// ensuring at compile time that the builder has access to a shell.
|
|
pub fn show(self) -> Result<PopupHandle> {
|
|
self.state.shell.show(self.config)
|
|
}
|
|
}
|