use crate::view::root::Cydonia;
use bezel::{
gpui::{
self, AnyElement, App, Context, Entity, Focusable as _, KeyBinding, SharedString, Window,
actions, div, prelude::*, px,
},
theme::{TextStyle, Theme, Typeset},
ui::{
input::TextField,
popover,
widgets::{ButtonStyle, Buttons},
},
};
actions!(cydonia_header, [CommitInfo, DismissInfo]);
const INFO_CONTEXT: &str = "CydoniaBoardInfo";
const LABEL_WIDTH: f32 = 34.;
pub fn init(cx: &mut App) {
cx.bind_keys([
KeyBinding::new("enter", CommitInfo, Some(INFO_CONTEXT)),
KeyBinding::new("escape", DismissInfo, Some(INFO_CONTEXT)),
]);
}
pub(crate) struct BoardInfo {
pub board: String,
pub name: Entity<TextField>,
pub key: Entity<TextField>,
pub error: Option<SharedString>,
}
fn seed(content: String, placeholder: &'static str, cx: &mut App) -> Entity<TextField> {
let field = cx.new(|cx| {
TextField::new(cx)
.with_key_context(INFO_CONTEXT)
.with_placeholder(placeholder)
});
field.update(cx, |field, cx| field.set_content(content, cx));
field
}
impl Cydonia {
pub(crate) fn toggle_info(&mut self, board: &str, window: &mut Window, cx: &mut Context<Self>) {
let shut_by_this_press = std::mem::take(&mut self.info_pressed);
if shut_by_this_press || self.info.is_some() {
self.info = None;
cx.notify();
return;
}
self.open_info(board, window, cx);
}
pub(crate) fn open_info(&mut self, board: &str, window: &mut Window, cx: &mut Context<Self>) {
self.commit(cx);
self.menu = None;
self.renaming = None;
let Some((name, key)) = self
.workspace
.read(cx)
.board_at(board)
.map(|board| (board.name.clone(), board.key.clone()))
else {
cx.notify();
return;
};
let name = seed(name, "name this board…", cx);
let key = seed(key, "KEY", cx);
window.focus(&name.read(cx).focus_handle(cx), cx);
self.info = Some(BoardInfo {
board: board.to_owned(),
name,
key,
error: None,
});
cx.notify();
}
pub(crate) fn commit_info(&mut self, _: &CommitInfo, _: &mut Window, cx: &mut Context<Self>) {
let Some(info) = self.info.as_ref() else {
return;
};
let board = info.board.clone();
let name = info.name.read(cx).content().trim().to_owned();
let key = info.key.read(cx).content().trim().to_owned();
let filed = self.workspace.update(cx, |workspace, cx| {
workspace.edit_board(&board, name, &key, cx)
});
match filed {
Ok(()) => self.info = None,
Err(why) => {
if let Some(info) = self.info.as_mut() {
info.error = Some(why.into());
}
}
}
cx.notify();
}
pub(crate) fn dismiss_info(&mut self, _: &DismissInfo, _: &mut Window, cx: &mut Context<Self>) {
self.info = None;
cx.notify();
}
pub(crate) fn info_panel(&self, board: &str, cx: &mut Context<Self>) -> Option<AnyElement> {
let info = self.info.as_ref().filter(|info| info.board == board)?;
let theme = Theme::of(cx).clone();
let card = popover::popover_card(&theme)
.w(px(280.))
.p(px(12.))
.flex()
.flex_col()
.gap(px(10.))
.child(self.info_row("Name", info.name.clone(), cx))
.child(self.info_row("Key", info.key.clone(), cx))
.children(info.error.clone().map(|why| {
div()
.text_style(TextStyle::Caption)
.text_color(theme.danger)
.child(why)
}))
.child(
div()
.flex()
.flex_row()
.justify_end()
.gap(px(8.))
.child(
theme
.button("Cancel", ButtonStyle::Ghost, None)
.id("info-cancel")
.on_click(cx.listener(|this, _, window, cx| {
this.dismiss_info(&DismissInfo, window, cx)
})),
)
.child(
theme
.button("Save", ButtonStyle::Prominent, None)
.id("info-save")
.on_click(cx.listener(|this, _, window, cx| {
this.commit_info(&CommitInfo, window, cx)
})),
),
)
.on_mouse_down_out(
cx.listener(|this, _, window, cx| this.dismiss_info(&DismissInfo, window, cx)),
)
.into_any_element();
Some(popover::anchored_menu_below(
SharedString::from("board-info"),
card,
None,
))
}
fn info_row(&self, label: &str, field: Entity<TextField>, cx: &Context<Self>) -> AnyElement {
let theme = Theme::of(cx).clone();
div()
.flex()
.flex_row()
.items_center()
.gap(px(8.))
.child(
div()
.flex_none()
.w(px(LABEL_WIDTH))
.text_style(TextStyle::Caption)
.text_color(theme.text_muted)
.child(label.to_owned()),
)
.child(div().flex_1().min_w_0().child(field))
.into_any_element()
}
}