
* Refactor text layout: don't need &Fonts in all functions * Replace indexing in Fonts with member function * Wrap Fonts in a Mutex * Remove mutex for Font::glyph_info_cache * Remove RwLock around Font::characters * Put FontsImpl and GalleyCache behind the same Mutex * Round font sizes to whole pixels before deduplicating them * Make TextStyle !Copy * Implement user-named TextStyle:s * round font size earlier * Cache fonts based on family and size * Move TextStyle into egui and Style * Remove body_text_style * Query graphics about max texture size and use that as font atlas size * Recreate texture atlas when it is getting full
257 lines
9.5 KiB
Rust
257 lines
9.5 KiB
Rust
use std::collections::BTreeMap;
|
||
|
||
pub struct FontBook {
|
||
filter: String,
|
||
font_id: egui::FontId,
|
||
named_chars: BTreeMap<egui::FontFamily, BTreeMap<char, String>>,
|
||
}
|
||
|
||
impl Default for FontBook {
|
||
fn default() -> Self {
|
||
Self {
|
||
filter: Default::default(),
|
||
font_id: egui::FontId::proportional(20.0),
|
||
named_chars: Default::default(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl super::Demo for FontBook {
|
||
fn name(&self) -> &'static str {
|
||
"🔤 Font Book"
|
||
}
|
||
|
||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||
use super::View as _;
|
||
self.ui(ui);
|
||
});
|
||
}
|
||
}
|
||
|
||
impl super::View for FontBook {
|
||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||
ui.label(format!(
|
||
"The selected font supports {} characters.",
|
||
self.named_chars
|
||
.get(&self.font_id.family)
|
||
.map(|map| map.len())
|
||
.unwrap_or_default()
|
||
));
|
||
|
||
ui.horizontal_wrapped(|ui| {
|
||
ui.spacing_mut().item_spacing.x = 0.0;
|
||
ui.label("You can add more characters by installing additional fonts with ");
|
||
ui.add(egui::Hyperlink::from_label_and_url(
|
||
egui::RichText::new("Context::set_fonts").text_style(egui::TextStyle::Monospace),
|
||
"https://docs.rs/egui/latest/egui/struct.Context.html#method.set_fonts",
|
||
));
|
||
ui.label(".");
|
||
});
|
||
|
||
ui.separator();
|
||
|
||
egui::introspection::font_id_ui(ui, &mut self.font_id);
|
||
|
||
ui.horizontal(|ui| {
|
||
ui.label("Filter:");
|
||
ui.add(egui::TextEdit::singleline(&mut self.filter).desired_width(120.0));
|
||
self.filter = self.filter.to_lowercase();
|
||
if ui.button("x").clicked() {
|
||
self.filter.clear();
|
||
}
|
||
});
|
||
|
||
let filter = &self.filter;
|
||
let named_chars = self
|
||
.named_chars
|
||
.entry(self.font_id.family.clone())
|
||
.or_insert_with(|| available_characters(ui, self.font_id.family.clone()));
|
||
|
||
ui.separator();
|
||
|
||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||
ui.horizontal_wrapped(|ui| {
|
||
ui.spacing_mut().item_spacing = egui::Vec2::splat(2.0);
|
||
|
||
for (&chr, name) in named_chars {
|
||
if filter.is_empty() || name.contains(filter) || *filter == chr.to_string() {
|
||
let button = egui::Button::new(
|
||
egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
|
||
)
|
||
.frame(false);
|
||
|
||
let tooltip_ui = |ui: &mut egui::Ui| {
|
||
ui.label(
|
||
egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
|
||
);
|
||
ui.label(format!("{}\nU+{:X}\n\nClick to copy", name, chr as u32));
|
||
};
|
||
|
||
if ui.add(button).on_hover_ui(tooltip_ui).clicked() {
|
||
ui.output().copied_text = chr.to_string();
|
||
}
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, String> {
|
||
ui.fonts()
|
||
.lock()
|
||
.fonts
|
||
.font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters
|
||
.characters()
|
||
.iter()
|
||
.filter(|chr| !chr.is_whitespace() && !chr.is_ascii_control())
|
||
.map(|&chr| (chr, char_name(chr)))
|
||
.collect()
|
||
}
|
||
|
||
fn char_name(chr: char) -> String {
|
||
special_char_name(chr)
|
||
.map(|s| s.to_owned())
|
||
.or_else(|| unicode_names2::name(chr).map(|name| name.to_string().to_lowercase()))
|
||
.unwrap_or_else(|| "unknown".to_owned())
|
||
}
|
||
|
||
fn special_char_name(chr: char) -> Option<&'static str> {
|
||
#[allow(clippy::match_same_arms)] // many "flag"
|
||
match chr {
|
||
// Special private-use-area extensions found in `emoji-icon-font.ttf`:
|
||
// Private use area extensions:
|
||
'\u{FE4E5}' => Some("flag japan"),
|
||
'\u{FE4E6}' => Some("flag usa"),
|
||
'\u{FE4E7}' => Some("flag"),
|
||
'\u{FE4E8}' => Some("flag"),
|
||
'\u{FE4E9}' => Some("flag"),
|
||
'\u{FE4EA}' => Some("flag great britain"),
|
||
'\u{FE4EB}' => Some("flag"),
|
||
'\u{FE4EC}' => Some("flag"),
|
||
'\u{FE4ED}' => Some("flag"),
|
||
'\u{FE4EE}' => Some("flag south korea"),
|
||
'\u{FE82C}' => Some("number sign in square"),
|
||
'\u{FE82E}' => Some("digit one in square"),
|
||
'\u{FE82F}' => Some("digit two in square"),
|
||
'\u{FE830}' => Some("digit three in square"),
|
||
'\u{FE831}' => Some("digit four in square"),
|
||
'\u{FE832}' => Some("digit five in square"),
|
||
'\u{FE833}' => Some("digit six in square"),
|
||
'\u{FE834}' => Some("digit seven in square"),
|
||
'\u{FE835}' => Some("digit eight in square"),
|
||
'\u{FE836}' => Some("digit nine in square"),
|
||
'\u{FE837}' => Some("digit zero in square"),
|
||
|
||
// Special private-use-area extensions found in `emoji-icon-font.ttf`:
|
||
// Web services / operating systems / browsers
|
||
'\u{E600}' => Some("web-dribbble"),
|
||
'\u{E601}' => Some("web-stackoverflow"),
|
||
'\u{E602}' => Some("web-vimeo"),
|
||
'\u{E603}' => Some("web-twitter"),
|
||
'\u{E604}' => Some("web-facebook"),
|
||
'\u{E605}' => Some("web-googleplus"),
|
||
'\u{E606}' => Some("web-pinterest"),
|
||
'\u{E607}' => Some("web-tumblr"),
|
||
'\u{E608}' => Some("web-linkedin"),
|
||
'\u{E60A}' => Some("web-stumbleupon"),
|
||
'\u{E60B}' => Some("web-lastfm"),
|
||
'\u{E60C}' => Some("web-rdio"),
|
||
'\u{E60D}' => Some("web-spotify"),
|
||
'\u{E60E}' => Some("web-qq"),
|
||
'\u{E60F}' => Some("web-instagram"),
|
||
'\u{E610}' => Some("web-dropbox"),
|
||
'\u{E611}' => Some("web-evernote"),
|
||
'\u{E612}' => Some("web-flattr"),
|
||
'\u{E613}' => Some("web-skype"),
|
||
'\u{E614}' => Some("web-renren"),
|
||
'\u{E615}' => Some("web-sina-weibo"),
|
||
'\u{E616}' => Some("web-paypal"),
|
||
'\u{E617}' => Some("web-picasa"),
|
||
'\u{E618}' => Some("os-android"),
|
||
'\u{E619}' => Some("web-mixi"),
|
||
'\u{E61A}' => Some("web-behance"),
|
||
'\u{E61B}' => Some("web-circles"),
|
||
'\u{E61C}' => Some("web-vk"),
|
||
'\u{E61D}' => Some("web-smashing"),
|
||
'\u{E61E}' => Some("web-forrst"),
|
||
'\u{E61F}' => Some("os-windows"),
|
||
'\u{E620}' => Some("web-flickr"),
|
||
'\u{E621}' => Some("web-picassa"),
|
||
'\u{E622}' => Some("web-deviantart"),
|
||
'\u{E623}' => Some("web-steam"),
|
||
'\u{E624}' => Some("web-github"),
|
||
'\u{E625}' => Some("web-git"),
|
||
'\u{E626}' => Some("web-blogger"),
|
||
'\u{E627}' => Some("web-soundcloud"),
|
||
'\u{E628}' => Some("web-reddit"),
|
||
'\u{E629}' => Some("web-delicious"),
|
||
'\u{E62A}' => Some("browser-chrome"),
|
||
'\u{E62B}' => Some("browser-firefox"),
|
||
'\u{E62C}' => Some("browser-ie"),
|
||
'\u{E62D}' => Some("browser-opera"),
|
||
'\u{E62E}' => Some("browser-safari"),
|
||
'\u{E62F}' => Some("web-google-drive"),
|
||
'\u{E630}' => Some("web-wordpress"),
|
||
'\u{E631}' => Some("web-joomla"),
|
||
'\u{E632}' => Some("lastfm"),
|
||
'\u{E633}' => Some("web-foursquare"),
|
||
'\u{E634}' => Some("web-yelp"),
|
||
'\u{E635}' => Some("web-drupal"),
|
||
'\u{E636}' => Some("youtube"),
|
||
'\u{F189}' => Some("vk"),
|
||
'\u{F1A6}' => Some("digg"),
|
||
'\u{F1CA}' => Some("web-vine"),
|
||
'\u{F8FF}' => Some("os-apple"),
|
||
|
||
// Special private-use-area extensions found in `Ubuntu-Light.ttf`
|
||
'\u{F000}' => Some("uniF000"),
|
||
'\u{F001}' => Some("fi"),
|
||
'\u{F002}' => Some("fl"),
|
||
'\u{F506}' => Some("one seventh"),
|
||
'\u{F507}' => Some("two sevenths"),
|
||
'\u{F508}' => Some("three sevenths"),
|
||
'\u{F509}' => Some("four sevenths"),
|
||
'\u{F50A}' => Some("five sevenths"),
|
||
'\u{F50B}' => Some("six sevenths"),
|
||
'\u{F50C}' => Some("one ninth"),
|
||
'\u{F50D}' => Some("two ninths"),
|
||
'\u{F50E}' => Some("four ninths"),
|
||
'\u{F50F}' => Some("five ninths"),
|
||
'\u{F510}' => Some("seven ninths"),
|
||
'\u{F511}' => Some("eight ninths"),
|
||
'\u{F800}' => Some("zero.alt"),
|
||
'\u{F801}' => Some("one.alt"),
|
||
'\u{F802}' => Some("two.alt"),
|
||
'\u{F803}' => Some("three.alt"),
|
||
'\u{F804}' => Some("four.alt"),
|
||
'\u{F805}' => Some("five.alt"),
|
||
'\u{F806}' => Some("six.alt"),
|
||
'\u{F807}' => Some("seven.alt"),
|
||
'\u{F808}' => Some("eight.alt"),
|
||
'\u{F809}' => Some("nine.alt"),
|
||
'\u{F80A}' => Some("zero.sups"),
|
||
'\u{F80B}' => Some("one.sups"),
|
||
'\u{F80C}' => Some("two.sups"),
|
||
'\u{F80D}' => Some("three.sups"),
|
||
'\u{F80E}' => Some("four.sups"),
|
||
'\u{F80F}' => Some("five.sups"),
|
||
'\u{F810}' => Some("six.sups"),
|
||
'\u{F811}' => Some("seven.sups"),
|
||
'\u{F812}' => Some("eight.sups"),
|
||
'\u{F813}' => Some("nine.sups"),
|
||
'\u{F814}' => Some("zero.sinf"),
|
||
'\u{F815}' => Some("one.sinf"),
|
||
'\u{F816}' => Some("two.sinf"),
|
||
'\u{F817}' => Some("three.sinf"),
|
||
'\u{F818}' => Some("four.sinf"),
|
||
'\u{F819}' => Some("five.sinf"),
|
||
'\u{F81A}' => Some("six.sinf"),
|
||
'\u{F81B}' => Some("seven.sinf"),
|
||
'\u{F81C}' => Some("eight.sinf"),
|
||
'\u{F81D}' => Some("nine.sinf"),
|
||
|
||
_ => None,
|
||
}
|
||
}
|