use eframe::egui::{self, Key, KeyboardShortcut, Modifiers, RichText, Ui};
use egui_richedit::{Mark, Model};
use crate::i18n::t;
use crate::{Block, FlowModel, PageEditor};
pub fn marks<M: Model<Paragraph = Vec<usize>>>(
ui: &mut Ui,
editor: &PageEditor,
model: &M,
) -> Option<Mark> {
let mut pressed = None;
ui.add_enabled_ui(editor.selection().is_some(), |ui| {
ui.horizontal(|ui| pressed = mark_buttons(ui, editor, model));
});
pressed
}
#[derive(Default)]
pub struct Pressed {
pub mark: Option<Mark>,
pub block: Option<Block>,
}
pub fn text(ui: &mut Ui, editor: &PageEditor, model: &FlowModel<'_>) -> Pressed {
let mut pressed = Pressed::default();
ui.add_enabled_ui(editor.selection().is_some(), |ui| {
ui.horizontal(|ui| {
pressed.mark = mark_buttons(ui, editor, model);
ui.separator();
for (block, label, name) in [
(Block::Body, "\u{b6}", t("Body text")),
(Block::Heading(1), "H1", t("Heading 1")),
(Block::Heading(2), "H2", t("Heading 2")),
(Block::Heading(3), "H3", t("Heading 3")),
(Block::Bullets, "\u{2022}", t("Bulleted list")),
(Block::Numbers, "1.", t("Numbered list")),
] {
let lit = editor
.selection()
.is_some_and(|selection| model.is_block(selection, block));
if ui
.selectable_label(lit, label)
.on_hover_text(name)
.clicked()
{
pressed.block = Some(block);
}
}
});
});
pressed
}
fn mark_facts(mark: Mark) -> (&'static str, Option<Key>) {
match mark {
Mark::Bold => (t("Bold"), Some(Key::B)),
Mark::Italic => (t("Italic"), Some(Key::I)),
Mark::Underline => (t("Underline"), Some(Key::U)),
Mark::Strike => (t("Strikethrough"), None),
}
}
fn mark_buttons<M: Model<Paragraph = Vec<usize>>>(
ui: &mut Ui,
editor: &PageEditor,
model: &M,
) -> Option<Mark> {
let mut pressed = None;
for mark in Mark::ALL {
let lit = editor.marked(model, mark) == Some(true);
let (name, key) = mark_facts(mark);
let label = match mark {
Mark::Bold => RichText::new(t("B")).strong(),
Mark::Italic => RichText::new(t("I")).italics(),
Mark::Underline => RichText::new(t("U")).underline(),
Mark::Strike => RichText::new(t("S")).strikethrough(),
};
let hint = match key {
Some(key) => format!(
"{name} ({})",
ui.ctx()
.format_shortcut(&KeyboardShortcut::new(Modifiers::COMMAND, key))
),
None => name.to_owned(),
};
if ui
.selectable_label(lit, label)
.on_hover_text(hint)
.clicked()
{
pressed = Some(mark);
}
}
pressed
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Command {
Mark(Mark),
Block(Block),
}
const BLOCKS: [Block; 6] = [
Block::Body,
Block::Heading(1),
Block::Heading(2),
Block::Heading(3),
Block::Bullets,
Block::Numbers,
];
fn block_name(block: Block) -> &'static str {
match block {
Block::Body => t("Body text"),
Block::Heading(1) => t("Heading 1"),
Block::Heading(2) => t("Heading 2"),
Block::Heading(_) => t("Heading 3"),
Block::Bullets => t("Bulleted list"),
Block::Numbers => t("Numbered list"),
}
}
#[derive(Default)]
pub struct MenuState {
enabled: bool,
blocks: bool,
marks: Vec<Mark>,
kinds: Vec<Block>,
}
impl MenuState {
pub fn of(editor: &PageEditor, model: &FlowModel<'_>, blocks: bool) -> Self {
let Some(selection) = editor.selection() else {
return Self {
blocks,
..Self::default()
};
};
Self {
enabled: true,
blocks,
marks: Mark::ALL
.into_iter()
.filter(|mark| editor.marked(model, *mark) == Some(true))
.collect(),
kinds: if blocks {
BLOCKS
.into_iter()
.filter(|block| model.is_block(selection, *block))
.collect()
} else {
Vec::new()
},
}
}
pub fn offering_blocks(blocks: bool) -> Self {
Self {
blocks,
..Self::default()
}
}
}
pub fn menu(ui: &mut Ui, state: &MenuState) -> Option<Command> {
let mut chosen = None;
for mark in Mark::ALL {
let (name, key) = mark_facts(mark);
let mut button = egui::Button::new(name).selected(state.marks.contains(&mark));
if let Some(key) = key {
button = button.shortcut_text(
ui.ctx()
.format_shortcut(&KeyboardShortcut::new(Modifiers::COMMAND, key)),
);
}
if ui.add_enabled(state.enabled, button).clicked() {
chosen = Some(Command::Mark(mark));
ui.close();
}
}
if state.blocks {
ui.separator();
for block in BLOCKS {
let button =
egui::Button::new(block_name(block)).selected(state.kinds.contains(&block));
if ui.add_enabled(state.enabled, button).clicked() {
chosen = Some(Command::Block(block));
ui.close();
}
}
}
chosen
}