
Closes https://github.com/emilk/egui/issues/2068 Before this PR, the default font, Ubuntu-Light, was ~11% smaller than it should have been, and the default monospace font, Hack, was ~14% smaller. This means that setting the font size `12` in egui would yield smaller text than using that font size in any other app. Ooops! The change is that this PR now takes into account the ttf properties `units_per_em` and `height_unscaled`. If your egui application has specified you own font sizes or text styles you will see the text in your application grow larger, unless you go in and compensate by dividing all font sizes by ~1.21 for Ubuntu-Light/Proportional and ~1.16 for Hack/Monospace, and with something else if you are using a custom font! This effects any use of `FontId`, `RichText::size`, etc. This PR changes the default `Style::text_styles` to compensate, so the default egui style should look the same before and after this PR.
261 lines
9.6 KiB
Rust
261 lines
9.6 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(18.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.vertical_centered(|ui| {
|
||
ui.add(crate::egui_github_link_file!());
|
||
});
|
||
|
||
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,
|
||
}
|
||
}
|