pub use super::render::register_input_coordinator_chrome;
use super::render::register_context_manager_chrome;
use super::settings::ChromeSettings;
use super::state::ChromeState;
use super::types::{ChromeAction, ChromeHit, ChromeRenderKind, ChromeView, ResizeCorner};
use crate::core::types::Rect;
use crate::docking::panels::DockPanel;
use crate::input::{Sense, WidgetKind};
use crate::layout::{ChromeNode, CompositeKind, CompositeRegistration, LayoutManager, LayoutNodeId, WidgetNode};
use crate::render::RenderContext;
use crate::types::WidgetId;
pub fn register_layout_manager_chrome<P: DockPanel>(
layout: &mut LayoutManager<P>,
render: &mut dyn RenderContext,
parent: LayoutNodeId,
id: impl Into<WidgetId>,
view: &ChromeView<'_>,
settings: &ChromeSettings,
kind: &ChromeRenderKind,
) -> Option<ChromeNode> {
let id: WidgetId = id.into();
let rect = layout.rect_for_chrome()?;
let mut state = std::mem::take(&mut layout.chrome_widget_state);
let layer = layout.compute_layer_for(parent);
let node_id = layout.tree_mut().add_widget(parent, WidgetNode { id: id.clone(), kind: WidgetKind::Chrome, rect, sense: Sense::NONE });
{
use crate::layout::{ChromeWindowControl as CC, EventBuilder};
let d = layout.dispatcher_mut();
d.on_prefix(format!("{}:tab_close:", id.0), EventBuilder::ChromeTabCloseFromSuffix);
d.on_prefix(format!("{}:tab:", id.0), EventBuilder::ChromeTabFromSuffix);
d.on_exact(format!("{}:new_tab", id.0), EventBuilder::ChromeNewTab);
d.on_exact(format!("{}:menu", id.0), EventBuilder::ChromeControl(CC::Menu));
d.on_exact(format!("{}:new_win", id.0), EventBuilder::ChromeControl(CC::NewWindow));
d.on_exact(format!("{}:close_win", id.0), EventBuilder::ChromeControl(CC::CloseWindow));
d.on_exact(format!("{}:min", id.0), EventBuilder::ChromeControl(CC::Minimize));
d.on_exact(format!("{}:max", id.0), EventBuilder::ChromeControl(CC::MaximizeRestore));
d.on_exact(format!("{}:close", id.0), EventBuilder::ChromeControl(CC::CloseApp));
}
state.sync_hover_from_layout(layout, id.0.as_str());
register_context_manager_chrome(
layout.ctx_mut(), render, id.clone(), rect, &mut state, view, settings, kind, &layer,
);
layout.push_composite_registration(CompositeRegistration {
kind: CompositeKind::Chrome,
slot_id: id.0.clone(),
widget_id: id.clone(),
frame_rect: rect,
});
layout.chrome_widget_state = state;
Some(ChromeNode(node_id))
}
fn tab_w(label: &str, state: &ChromeState, i: usize, padding_h: f64, close_size: f64) -> f64 {
if let Some(&w) = state.tab_widths.get(i) {
return w;
}
let text_w = label.len() as f64 * 7.0;
padding_h + text_w + close_size + padding_h
}
pub fn chrome_hit_test(
state: &ChromeState,
view: &ChromeView<'_>,
settings: &ChromeSettings,
kind: &ChromeRenderKind,
rect: Rect,
point: (f64, f64),
) -> ChromeHit {
let (px, py) = point;
let style = settings.style.as_ref();
let h = style.chrome_height();
let bw = style.border_zone();
let w = rect.width;
let lx = rect.x;
let rx = rect.x + w;
let ty = rect.y;
let by = rect.y + h;
if py < rect.y || py > rect.y + h {
return ChromeHit::None;
}
if px < lx + bw && py < ty + bw {
return ChromeHit::ResizeCorner(ResizeCorner::TopLeft);
}
if px > rx - bw && py < ty + bw {
return ChromeHit::ResizeCorner(ResizeCorner::TopRight);
}
if px < lx + bw && py > by - bw {
return ChromeHit::ResizeCorner(ResizeCorner::BottomLeft);
}
if px > rx - bw && py > by - bw {
return ChromeHit::ResizeCorner(ResizeCorner::BottomRight);
}
if py < ty + bw { return ChromeHit::ResizeTop; }
if py > by - bw { return ChromeHit::ResizeBottom; }
if px < lx + bw { return ChromeHit::ResizeLeft; }
if px > rx - bw { return ChromeHit::ResizeRight; }
if matches!(kind, ChromeRenderKind::Custom(_)) {
return ChromeHit::None;
}
let bp_close_x = rect.x + w - 46.0;
let bp_maximize_x = rect.x + w - 92.0;
let bp_minimize_x = rect.x + w - 138.0;
let effective_close_window = view.show_close_window_btn || view.show_new_window_btn;
let mut cursor = bp_minimize_x;
let bp_cw_left = if effective_close_window {
cursor -= 36.0; Some(cursor)
} else { None };
let bp_menu_left = if view.show_menu_btn {
cursor -= 36.0; Some(cursor)
} else { None };
let bp_nw_left = if view.show_new_window_btn {
cursor -= 36.0; Some(cursor)
} else { None };
let show_tabs = !matches!(kind, ChromeRenderKind::WindowControlsOnly);
let show_controls = !matches!(kind, ChromeRenderKind::Minimal);
if show_controls {
if px >= bp_close_x { return ChromeHit::CloseBtn; }
if px >= bp_maximize_x { return ChromeHit::MaxBtn; }
if px >= bp_minimize_x { return ChromeHit::MinBtn; }
if show_tabs {
if let Some(left) = bp_cw_left {
if effective_close_window && px >= left && px < left + 36.0 {
return ChromeHit::CloseWindowBtn;
}
}
if let Some(left) = bp_menu_left {
if px >= left && px < left + 36.0 { return ChromeHit::Menu; }
}
if let Some(left) = bp_nw_left {
if px >= left && px < left + 36.0 { return ChromeHit::NewWindowBtn; }
}
}
}
if show_tabs {
let padding_h = style.tab_padding_h();
let close_size = style.tab_close_size();
let tab_gap = style.tab_gap();
let mut x = rect.x + 4.0; for (i, tab) in view.tabs.iter().enumerate() {
let tw = tab_w(tab.label, state, i, padding_h, close_size);
if px >= x && px < x + tw {
if tab.closable {
let close_bw = close_size;
let cx = x + tw - close_bw;
if px >= cx {
return ChromeHit::CloseTab(i);
}
}
return ChromeHit::Tab(i);
}
x += tw + tab_gap;
}
if view.show_new_tab_btn && px >= x && px < x + 28.0 {
return ChromeHit::NewTab;
}
let x_after_new_tab = x + 28.0;
let drag_end = bp_nw_left.or(bp_menu_left).or(bp_cw_left).unwrap_or(bp_minimize_x);
if px >= x_after_new_tab && px < drag_end {
return ChromeHit::Drag;
}
} else {
if px < bp_minimize_x {
return ChromeHit::Drag;
}
}
ChromeHit::None
}
pub fn handle_chrome_action(hit: ChromeHit) -> ChromeAction {
match hit {
ChromeHit::Tab(i) => ChromeAction::SelectTab(i),
ChromeHit::CloseTab(i) => ChromeAction::CloseTab(i),
ChromeHit::NewTab => ChromeAction::NewTab,
ChromeHit::NewWindowBtn => ChromeAction::NewWindow,
ChromeHit::Menu => ChromeAction::OpenMenu,
ChromeHit::Drag => ChromeAction::WindowDragStart,
ChromeHit::MinBtn => ChromeAction::Minimize,
ChromeHit::MaxBtn => ChromeAction::MaximizeRestore,
ChromeHit::CloseBtn => ChromeAction::CloseApp,
ChromeHit::CloseWindowBtn => ChromeAction::CloseWindow,
ChromeHit::ResizeCorner(_)
| ChromeHit::ResizeTop
| ChromeHit::ResizeBottom
| ChromeHit::ResizeLeft
| ChromeHit::ResizeRight => ChromeAction::BeginResize(hit),
ChromeHit::None => ChromeAction::None,
}
}