use std::fmt::Debug;
use std::iter::once;
use std::{fmt, vec};
use egui::{Context, ModifierNames, Modifiers, WidgetText};
use crate::{icon_text, icons, Icon};
#[derive(Clone)]
pub enum IconTextItem {
Icon(Icon),
Text(WidgetText),
}
impl Debug for IconTextItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Icon(icon) => write!(f, "Icon({})", icon.id),
Self::Text(text) => write!(f, "Text({})", text.text()),
}
}
}
impl IconTextItem {
pub fn icon(icon: Icon) -> Self {
Self::Icon(icon)
}
pub fn text(text: impl Into<WidgetText>) -> Self {
Self::Text(text.into())
}
}
#[derive(Default, Debug, Clone)]
pub struct IconText(pub Vec<IconTextItem>);
impl From<String> for IconText {
fn from(value: String) -> Self {
Self(vec![IconTextItem::Text(value.into())])
}
}
impl From<&str> for IconText {
fn from(value: &str) -> Self {
Self(vec![IconTextItem::Text(value.into())])
}
}
impl From<Icon> for IconText {
fn from(icon: Icon) -> Self {
Self(vec![IconTextItem::Icon(icon)])
}
}
impl From<IconTextItem> for IconText {
fn from(value: IconTextItem) -> Self {
Self(vec![value])
}
}
impl IconText {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn icon(&mut self, icon: Icon) {
self.0.push(IconTextItem::Icon(icon));
}
pub fn text(&mut self, text: impl Into<WidgetText>) {
self.0.push(IconTextItem::Text(text.into()));
}
pub fn add(&mut self, item: impl Into<Self>) {
self.0.extend(item.into().0);
}
}
#[macro_export]
macro_rules! icon_text {
($($item:expr),* $(,)?) => {{
let mut icon_text = $crate::IconText::new();
$(icon_text.add($item);)*
icon_text
}};
}
fn is_mac(ctx: &Context) -> bool {
matches!(
ctx.os(),
egui::os::OperatingSystem::Mac | egui::os::OperatingSystem::IOS
)
}
pub fn maybe_plus(ctx: &Context) -> IconText {
if is_mac(ctx) {
IconText::default()
} else {
icon_text!("+")
}
}
pub fn shortcut_with_icon(
ctx: &Context,
modifiers: Modifiers,
icon: impl Into<IconText>,
) -> IconText {
icon_text!(modifiers_text(modifiers, ctx), maybe_plus(ctx), icon.into())
}
pub fn modifiers_text(modifiers: Modifiers, ctx: &egui::Context) -> IconText {
let is_mac = is_mac(ctx);
let names = if is_mac {
let mut names = ModifierNames::SYMBOLS;
names.concat = "";
names
} else {
let mut names = ModifierNames::NAMES;
names.concat = "+";
names
};
let text = names.format(&modifiers, is_mac);
let mut icon_text = IconText::new();
if is_mac {
for char in text.chars() {
if char == '⌘' {
icon_text.add(IconTextItem::icon(icons::COMMAND));
} else if char == '⌃' {
icon_text.add(IconTextItem::icon(icons::CONTROL));
} else if char == '⇧' {
icon_text.add(IconTextItem::icon(icons::SHIFT));
} else if char == '⌥' {
icon_text.add(IconTextItem::icon(icons::OPTION));
} else {
return text.into();
}
}
icon_text
} else {
let mut vec: Vec<_> = text
.split('+')
.map(IconTextItem::text)
.flat_map(|item| once(item).chain(once(IconTextItem::text("+"))))
.collect();
vec.pop(); IconText(vec)
}
}
pub struct MouseButtonText(pub egui::PointerButton);
impl From<MouseButtonText> for IconText {
fn from(value: MouseButtonText) -> Self {
match value.0 {
egui::PointerButton::Primary => icons::LEFT_MOUSE_CLICK.into(),
egui::PointerButton::Secondary => icons::RIGHT_MOUSE_CLICK.into(),
egui::PointerButton::Middle => "middle mouse button".into(),
egui::PointerButton::Extra1 => "extra 1 mouse button".into(),
egui::PointerButton::Extra2 => "extra 2 mouse button".into(),
}
}
}