| Bring vidya in cfd3e36 nandi 19d ago | 1 | //! **Vidya** — theme layer on top of [egui]. |
| 2 | //! |
| 3 | //! Palette and spacing track GNOME HIG / libadwaita *feel* without linking GTK |
| 4 | //! or using that name in the public API. Dark is the default. |
| 5 | //! |
| 6 | //! On Android (edge-to-edge), call [`reserve_system_chrome`] once per frame |
| 7 | //! **before** other panels, or use [`top_header`] so content cannot sit under |
| 8 | //! the system status / navigation bars. |
| 9 | //! |
| 10 | //! Layout composition: prefer [`page_body`] / [`card`] / [`compact_card`] / |
| 11 | //! [`pack`] / [`vstack`] / [`lead_trail`] / [`two_col`] / [`grid_cols`] / |
| 12 | //! [`metric_bps`] so apps avoid width overflow, justified-gap stretch, and |
| 13 | //! metric “waterfall” columns. |
| 14 | //! |
| 15 | //! **Top-level application UI is grid-only:** [`central_page`] / [`page_body`] |
| 16 | //! take a [`GridCtx`] callback (not a free-stack `Ui`). Compose with |
| 17 | //! [`GridCtx::section`] / `g.row`; nest cards and nested grids inside cells. |
| 18 | //! |
| 19 | //! ```ignore |
| 20 | //! vidya::central_page(ctx, &th, "main", |g| { |
| 21 | //! g.section(|ui| { /* … */ }); |
| 22 | //! g.section(|ui| { /* … */ }); |
| 23 | //! }); |
| 24 | //! ``` |
| 25 | //! |
| 26 | //! Nested / table Grid DSL: |
| 27 | //! ```ignore |
| 28 | //! vidya::grid_cols(ui, &th, "procs", &[ColSpec::Flex, ColSpec::MetricBps], |g| { |
| 29 | //! g.row(|r| { r.heading("Name"); r.heading("Write"); }); |
| 30 | //! g.row(|r| { r.text(name); r.metric_bps(rate); }); |
| 31 | //! }); |
| 32 | //! ``` |
| 33 | //! |
| 34 | //! Window icon (embed a PNG with `include_bytes!`): |
| 35 | //! ```ignore |
| 36 | //! use egui::ViewportBuilder; |
| 37 | //! use vidya::with_app_icon_id; |
| 38 | //! |
| 39 | //! let viewport = with_app_icon_id( |
| 40 | //! ViewportBuilder::default().with_title("usage"), |
| 41 | //! "usage", // Wayland app_id ↔ .desktop StartupWMClass |
| 42 | //! include_bytes!("../assets/usage-256.png"), |
| 43 | //! ); |
| 44 | //! ``` |
| 45 | //! |
| 46 | //! ```ignore |
| 47 | //! use vidya::{apply_dark, Theme}; |
| 48 | //! |
| 49 | //! apply_dark(ctx); |
| 50 | //! if vidya::primary_button(ui, &Theme::dark(), "Open").clicked() { /* … */ } |
| 51 | //! ``` |
| 52 | |
| 53 | mod app_icon; |
| 54 | mod chrome; |
| 55 | mod fonts; |
| 56 | mod hotkeys; |
| 57 | mod icons; |
| 58 | mod layout; |
| 59 | mod theme; |
| 60 | mod video; |
| 61 | |
| 62 | pub use app_icon::{ |
| 63 | icon_data_from_png, try_with_app_icon, try_with_app_icon_id, with_app_icon, with_app_icon_id, |
| 64 | AppIconError, |
| 65 | }; |
| 66 | #[cfg(target_os = "android")] |
| 67 | pub use chrome::sync_system_chrome_from_android; |
| 68 | pub use chrome::{ |
| 69 | dialog, reserve_system_chrome, set_system_chrome, system_chrome, top_header, SystemChrome, |
| 70 | }; |
| 71 | pub use fonts::install_symbol_font; |
| 72 | pub use hotkeys::{ |
| 73 | command_glyph, command_pressed, command_shift_shortcut_label, command_shortcut_label, |
| 74 | consume_command, consume_command_shift, consume_escape, escape_label, is_macos, |
| 75 | }; |
| 76 | pub use icons::{ |
| 77 | emoji_icon, emoji_icon_colored, emoji_pack_len, has_emoji_icon, icon, icon_colored, |
| 78 | icon_for_emoji, normalize_emoji, paint_emoji_in, paint_icon, paint_icon_in, reaction_chip, |
| 79 | reaction_chip_sized, twemoji_key, Icon, |
| 80 | }; |
| 81 | pub use layout::{ |
| 82 | card, card_frame_chrome_x, central_page, central_page_cols, compact_card, data_table, |
| 83 | default_min_col, distribute_col_max, fill_width, fit_width, format_bps, format_rate, grid, |
| 84 | grid_cols, grid_cols_with, hflow, inset_row, lead_trail, measure_mono_caption, metric_bps, |
| 85 | metric_cell, metric_cell_px, metric_rate, pack, pad_metric, page_body, page_body_cols, |
| 86 | page_scroll, side_by_side, table_metric, table_text, table_text_capped, two_col, vstack, Col, |
| 87 | ColKind, ColSpec, GridCtx, GridOpts, RowDsl, FLEX_COL_MIN_PX, METRIC_BPS_CHARS, |
| 88 | METRIC_RATE_CHARS, |
| 89 | }; |
| 90 | pub use theme::{ |
| 91 | body, button, checkbox, destructive_button, dim_label, icon_button, primary_button, status_dot, |
| 92 | text_field_multiline, text_field_singleline, title, title_2, Mode, Palette, Spacing, Theme, |
| 93 | TypeScale, |
| 94 | }; |
| 95 | pub use video::{video_player, VideoPlayerAction, VideoPlayerOpts, VideoPlayerState}; |
| 96 | |
| 97 | use egui::{Context, FontFamily, FontId, Style, TextStyle}; |
| 98 | |
| 99 | /// Install palette + spacing + text styles on the egui context. |
| 100 | /// |
| 101 | /// Also registers a small symbol-font fallback so punctuation like `→` / `●` / |
| 102 | /// `▾` / `▴` and block/box art (`█▄▀░`) do not render as hollow boxes on Android. |
| 103 | pub fn apply(ctx: &Context, theme: &Theme) { |
| 104 | install_symbol_font(ctx); |
| 105 | ctx.set_visuals(theme.visuals()); |
| 106 | ctx.set_style(theme.style()); |
| 107 | } |
| 108 | |
| 109 | /// Dark shell (default). |
| 110 | pub fn apply_dark(ctx: &Context) { |
| 111 | apply(ctx, &Theme::dark()); |
| 112 | } |
| 113 | |
| 114 | /// Light shell. |
| 115 | pub fn apply_light(ctx: &Context) { |
| 116 | apply(ctx, &Theme::light()); |
| 117 | } |
| 118 | |
| 119 | /// Map type scale onto egui text styles. |
| 120 | pub fn install_text_styles(style: &mut Style, scale: &TypeScale) { |
| 121 | use FontFamily::Proportional; |
| 122 | style.text_styles = [ |
| 123 | (TextStyle::Small, FontId::new(scale.caption, Proportional)), |
| 124 | (TextStyle::Body, FontId::new(scale.body, Proportional)), |
| 125 | (TextStyle::Button, FontId::new(scale.body, Proportional)), |
| 126 | (TextStyle::Heading, FontId::new(scale.title, Proportional)), |
| 127 | ( |
| 128 | TextStyle::Monospace, |
| 129 | FontId::new(scale.body, FontFamily::Monospace), |
| 130 | ), |
| 131 | ] |
| 132 | .into(); |
| 133 | } |