use crate::mosaic::project::WindowLayout;
use crate::mosaic::widget::{Pane, Widget};
use crate::prelude::*;
use super::{LayoutEvent, Mosaic};
impl<W: Widget<C, M>, C, M> Mosaic<W, C, M> {
pub fn switch_layout(&mut self, index: usize) -> Option<String> {
if index >= self.layouts.len() {
return None;
}
if let Some(current_tree) = self.tile_tree.take()
&& let Some(current_layout) = self.layouts.get_mut(self.active_layout_index)
{
current_layout.tree = current_tree;
}
self.active_layout_index = index;
if let Some(layout) = self.layouts.get(index) {
self.tile_tree = Some(layout.tree.clone());
self.layout_modified = false;
Some(layout.layout_name.clone())
} else {
None
}
}
pub fn create_layout(
&mut self,
name: impl Into<String>,
default_tree: egui_tiles::Tree<Pane<W>>,
) {
self.layouts
.push(WindowLayout::new(default_tree, name.into()));
}
pub fn save_current_layout(&mut self) -> Option<String> {
if let Some(current_tree) = self.tile_tree.clone()
&& let Some(current_layout) = self.layouts.get_mut(self.active_layout_index)
{
current_layout.tree = current_tree;
self.layout_modified = false;
Some(current_layout.layout_name.clone())
} else {
None
}
}
pub fn delete_current_layout(&mut self) -> Option<String> {
if self.layouts.len() <= 1 {
return None;
}
let deleted_name = self
.layouts
.get(self.active_layout_index)
.map(|layout| layout.layout_name.clone())
.unwrap_or_default();
self.layouts.remove(self.active_layout_index);
if self.active_layout_index >= self.layouts.len() {
self.active_layout_index = self.layouts.len().saturating_sub(1);
}
if let Some(layout) = self.layouts.get(self.active_layout_index) {
self.tile_tree = Some(layout.tree.clone());
}
self.editing_layout_index = None;
self.layout_name_edit_buffer.clear();
self.layout_modified = false;
Some(deleted_name)
}
pub fn rename_layout(&mut self, index: usize, name: String) {
if let Some(layout) = self.layouts.get_mut(index) {
layout.layout_name = name;
}
}
pub fn reset_layout(&mut self, default_tree: egui_tiles::Tree<Pane<W>>) {
self.tile_tree = Some(default_tree.clone());
if let Some(layout) = self.layouts.get_mut(self.active_layout_index) {
layout.tree = default_tree;
}
self.layout_modified = true;
}
pub fn active_layout_name(&self) -> &str {
self.layouts
.get(self.active_layout_index)
.map(|layout| layout.layout_name.as_str())
.unwrap_or("Default")
}
pub fn layout_count(&self) -> usize {
self.layouts.len()
}
pub fn active_layout_index(&self) -> usize {
self.active_layout_index
}
pub fn layouts(&self) -> &[WindowLayout<W>] {
&self.layouts
}
pub fn load_layouts(&mut self, layouts: Vec<WindowLayout<W>>, active_index: usize) {
self.layouts = layouts;
self.active_layout_index = active_index.min(self.layouts.len().saturating_sub(1));
if let Some(layout) = self.layouts.get(self.active_layout_index) {
self.tile_tree = Some(layout.tree.clone());
}
self.editing_layout_index = None;
self.layout_name_edit_buffer.clear();
self.layout_modified = false;
}
pub fn save_layouts(&mut self) -> (Vec<WindowLayout<W>>, usize) {
if let Some(current_tree) = self.tile_tree.clone()
&& let Some(current_layout) = self.layouts.get_mut(self.active_layout_index)
{
current_layout.tree = current_tree;
}
(self.layouts.clone(), self.active_layout_index)
}
pub fn render_layout_section(
&mut self,
ui: &mut egui::Ui,
default_tree_fn: impl Fn() -> egui_tiles::Tree<Pane<W>>,
) -> Vec<LayoutEvent> {
let mut events = Vec::new();
if self.editing_layout_index.is_some() {
self.render_layout_name_editor(ui, &mut events);
} else {
self.render_layout_menu(ui, &mut events, default_tree_fn);
}
events
}
fn render_layout_name_editor(&mut self, ui: &mut egui::Ui, events: &mut Vec<LayoutEvent>) {
ui.horizontal(|ui| {
ui.add(
egui::TextEdit::singleline(&mut self.layout_name_edit_buffer).desired_width(120.0),
);
if ui.button("❌").clicked() {
self.editing_layout_index = None;
self.layout_name_edit_buffer.clear();
}
if ui.button("✅").clicked() {
if !self.layout_name_edit_buffer.trim().is_empty()
&& let Some(index) = self.editing_layout_index
{
let new_name = self.layout_name_edit_buffer.trim().to_string();
self.rename_layout(index, new_name.clone());
events.push(LayoutEvent::Renamed(new_name));
}
self.editing_layout_index = None;
self.layout_name_edit_buffer.clear();
}
});
}
fn render_layout_menu(
&mut self,
ui: &mut egui::Ui,
events: &mut Vec<LayoutEvent>,
default_tree_fn: impl Fn() -> egui_tiles::Tree<Pane<W>>,
) {
let light_green = egui::Color32::from_rgb(144, 238, 144);
let layout_name = self.active_layout_name().to_string();
let mut switch_to_layout: Option<usize> = None;
let mut start_editing_layout: Option<(usize, String)> = None;
let mut create_new = false;
let mut save_current = false;
let mut delete_current = false;
let mut reset_layout = false;
ui.menu_button(egui::RichText::new("📐 Layout").color(light_green), |ui| {
if !self.layouts.is_empty() {
if ui.button("Save Layout").clicked() {
save_current = true;
ui.close();
}
if ui.button("New Layout").clicked() {
create_new = true;
ui.close();
}
if ui.button("Reset Layout").clicked() {
reset_layout = true;
ui.close();
}
let can_delete = self.layouts.len() > 1;
if ui
.add_enabled(can_delete, egui::Button::new("Delete Layout"))
.clicked()
{
delete_current = true;
ui.close();
}
ui.separator();
for (index, layout) in self.layouts.iter().enumerate() {
let is_active = index == self.active_layout_index;
ui.horizontal(|ui| {
let display_name = if is_active {
format!("● {}", layout.layout_name)
} else {
layout.layout_name.clone()
};
if ui.selectable_label(is_active, &display_name).clicked() && !is_active {
switch_to_layout = Some(index);
}
if ui.small_button("✏").on_hover_text("Rename").clicked() {
start_editing_layout = Some((index, layout.layout_name.clone()));
}
});
}
} else {
ui.label("No project loaded");
}
});
let layout_display = if self.layout_modified {
format!("{} *", layout_name)
} else {
layout_name.clone()
};
let text_color = if ui.visuals().dark_mode {
egui::Color32::from_rgb(246, 247, 245)
} else {
egui::Color32::from_rgb(25, 25, 24)
};
ui.label(egui::RichText::new(&layout_display).color(text_color));
if ui
.small_button("✏")
.on_hover_text("Edit layout name")
.clicked()
{
self.editing_layout_index = Some(self.active_layout_index);
self.layout_name_edit_buffer = layout_name;
}
if let Some(index) = switch_to_layout
&& let Some(name) = self.switch_layout(index)
{
events.push(LayoutEvent::Switched(name));
}
if let Some((index, name)) = start_editing_layout {
self.editing_layout_index = Some(index);
self.layout_name_edit_buffer = name;
}
if create_new {
let new_name = format!("Layout {}", self.layouts.len() + 1);
self.create_layout(new_name.clone(), default_tree_fn());
events.push(LayoutEvent::Created(new_name));
}
if save_current && let Some(name) = self.save_current_layout() {
events.push(LayoutEvent::Saved(name));
}
if delete_current && let Some(name) = self.delete_current_layout() {
events.push(LayoutEvent::Deleted(name));
}
if reset_layout {
self.reset_layout(default_tree_fn());
events.push(LayoutEvent::Reset);
}
}
}