Language dropdown with pixel flags in settings, title and login
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b025036277
commit
d8e9184a9c
3 files changed
+334
-5
No files matched your search
+6
-4
@@ -1024,10 +1024,12 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if menu.scrollable() {
|
let (_, wheel) = mouse_wheel();
|
||||||
let (_, wheel) = mouse_wheel();
|
if wheel != 0.0 && menu.page.is_some() {
|
||||||
if wheel != 0.0 {
|
let d = if wheel > 0.0 { -1 } else { 1 };
|
||||||
menu.scroll_lines(if wheel > 0.0 { -1 } else { 1 });
|
// an open language dropdown scrolls first
|
||||||
|
if !menu.lang_wheel(d) && menu.scrollable() {
|
||||||
|
menu.scroll_lines(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+121
-1
@@ -15,6 +15,7 @@ use crate::ui::{grid_center, grid_center_y, mouse, sh, sw, tool_info, wrap, side
|
|||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
mod bestiary;
|
mod bestiary;
|
||||||
|
mod flags;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum Page {
|
pub enum Page {
|
||||||
@@ -289,6 +290,7 @@ enum Btn {
|
|||||||
BestSel(i32),
|
BestSel(i32),
|
||||||
BestPick(usize),
|
BestPick(usize),
|
||||||
BestLevel(i32),
|
BestLevel(i32),
|
||||||
|
LangOpen, // the language dropdown field
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const REPO_URL: &str = "https://git.familie-zink.org/zink/dungeoncraft";
|
pub const REPO_URL: &str = "https://git.familie-zink.org/zink/dungeoncraft";
|
||||||
@@ -417,6 +419,11 @@ pub struct Menu {
|
|||||||
scroll: usize,
|
scroll: usize,
|
||||||
rows_visible: usize,
|
rows_visible: usize,
|
||||||
buttons: Vec<(Rect, Btn)>,
|
buttons: Vec<(Rect, Btn)>,
|
||||||
|
lang_open: Option<Rect>, // the language dropdown is open below/above this field
|
||||||
|
lang_scroll: usize, // first language shown in the open dropdown
|
||||||
|
lang_opts: Vec<(Rect, String)>, // the open dropdown's rows (language codes)
|
||||||
|
lang_rows: usize, // rows the open dropdown shows at once
|
||||||
|
cur_lang: String, // the language in use (for the field and the highlight)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button(gfx: &Gfx, r: Rect, label: &str, hot: bool, accent: Color) {
|
fn button(gfx: &Gfx, r: Rect, label: &str, hot: bool, accent: Color) {
|
||||||
@@ -445,6 +452,11 @@ impl Menu {
|
|||||||
campaign_pending: false,
|
campaign_pending: false,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
scroll: 0,
|
scroll: 0,
|
||||||
|
lang_open: None,
|
||||||
|
lang_scroll: 0,
|
||||||
|
lang_opts: Vec::new(),
|
||||||
|
lang_rows: 0,
|
||||||
|
cur_lang: String::new(),
|
||||||
rows_visible: 1,
|
rows_visible: 1,
|
||||||
buttons: Vec::new(),
|
buttons: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -456,6 +468,7 @@ impl Menu {
|
|||||||
|
|
||||||
// Esc: go one level back
|
// Esc: go one level back
|
||||||
pub fn back(&mut self) {
|
pub fn back(&mut self) {
|
||||||
|
self.lang_open = None;
|
||||||
self.page = match self.page {
|
self.page = match self.page {
|
||||||
Some(Page::KeyCapture { from_title, .. }) => Some(Page::Settings { tab: 5, from_title }),
|
Some(Page::KeyCapture { from_title, .. }) => Some(Page::Settings { tab: 5, from_title }),
|
||||||
Some(Page::Settings { from_title: true, .. }) => Some(Page::Title),
|
Some(Page::Settings { from_title: true, .. }) => Some(Page::Title),
|
||||||
@@ -574,6 +587,15 @@ impl Menu {
|
|||||||
if self.page.is_none() {
|
if self.page.is_none() {
|
||||||
return MenuAction::None;
|
return MenuAction::None;
|
||||||
}
|
}
|
||||||
|
// an open language dropdown takes the click: a language, or anywhere else closes it
|
||||||
|
if self.lang_open.take().is_some() {
|
||||||
|
if let Some((_, code)) = self.lang_opts.iter().find(|(r, _)| r.contains(vec2(x, y))) {
|
||||||
|
settings.language = code.clone();
|
||||||
|
return MenuAction::SettingsChanged;
|
||||||
|
}
|
||||||
|
return MenuAction::Consumed;
|
||||||
|
}
|
||||||
|
let hit_rect = self.buttons.iter().find(|(r, _)| r.contains(vec2(x, y))).map(|(r, _)| *r);
|
||||||
let hit = self.buttons.iter().find(|(r, _)| r.contains(vec2(x, y))).map(|(_, b)| *b);
|
let hit = self.buttons.iter().find(|(r, _)| r.contains(vec2(x, y))).map(|(_, b)| *b);
|
||||||
// overwrite and delete need a second click on the same button
|
// overwrite and delete need a second click on the same button
|
||||||
let confirm = self.confirm.take();
|
let confirm = self.confirm.take();
|
||||||
@@ -581,6 +603,13 @@ impl Menu {
|
|||||||
let from_title = matches!(self.page, Some(Page::Title) | Some(Page::Settings { from_title: true, .. }) | Some(Page::KeyCapture { from_title: true, .. }));
|
let from_title = matches!(self.page, Some(Page::Title) | Some(Page::Settings { from_title: true, .. }) | Some(Page::KeyCapture { from_title: true, .. }));
|
||||||
match hit {
|
match hit {
|
||||||
None => MenuAction::Consumed,
|
None => MenuAction::Consumed,
|
||||||
|
Some(Btn::LangOpen) => {
|
||||||
|
self.lang_open = hit_rect;
|
||||||
|
// start the list so that the language in use is visible
|
||||||
|
let k = languages().iter().position(|(c, _)| *c == settings.language).unwrap_or(0);
|
||||||
|
self.lang_scroll = k.saturating_sub(3);
|
||||||
|
MenuAction::Consumed
|
||||||
|
}
|
||||||
Some(Btn::Continue) => MenuAction::Continue,
|
Some(Btn::Continue) => MenuAction::Continue,
|
||||||
Some(Btn::Resume) => {
|
Some(Btn::Resume) => {
|
||||||
self.page = None;
|
self.page = None;
|
||||||
@@ -809,7 +838,12 @@ impl Menu {
|
|||||||
|
|
||||||
pub fn draw(&mut self, gfx: &Gfx, settings: &Settings, status: Option<&str>, cloud: &Cloud) {
|
pub fn draw(&mut self, gfx: &Gfx, settings: &Settings, status: Option<&str>, cloud: &Cloud) {
|
||||||
self.buttons.clear();
|
self.buttons.clear();
|
||||||
let Some(page) = self.page else { return };
|
self.lang_opts.clear();
|
||||||
|
self.cur_lang = settings.language.clone();
|
||||||
|
let Some(page) = self.page else {
|
||||||
|
self.lang_open = None;
|
||||||
|
return;
|
||||||
|
};
|
||||||
let sw = sw();
|
let sw = sw();
|
||||||
let sh = sh();
|
let sh = sh();
|
||||||
draw_rectangle(0.0, 0.0, sw, sh, Color::new(0.0, 0.0, 0.0, if page == Page::Title { 0.55 } else { 0.45 }));
|
draw_rectangle(0.0, 0.0, sw, sh, Color::new(0.0, 0.0, 0.0, if page == Page::Title { 0.55 } else { 0.45 }));
|
||||||
@@ -840,6 +874,9 @@ impl Menu {
|
|||||||
gfx.text(&esc, x + CW() * 4.0, y + CH() * 3.0, S(), DIM);
|
gfx.text(&esc, x + CW() * 4.0, y + CH() * 3.0, S(), DIM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(anchor) = self.lang_open {
|
||||||
|
self.draw_lang_list(gfx, anchor);
|
||||||
|
}
|
||||||
if let Some(st) = status {
|
if let Some(st) = status {
|
||||||
// long messages wrap onto several lines
|
// long messages wrap onto several lines
|
||||||
let maxc = ((sw / CW()) as usize).saturating_sub(8).clamp(10, 70);
|
let maxc = ((sw / CW()) as usize).saturating_sub(8).clamp(10, 70);
|
||||||
@@ -865,6 +902,83 @@ impl Menu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the language dropdown field: flag, the language's own name, an arrow
|
||||||
|
fn lang_field(&mut self, gfx: &Gfx, x: f32, y: f32, w: f32) {
|
||||||
|
let (mx, my) = mouse();
|
||||||
|
let r = Rect::new(x, y, w, CH() * 2.0);
|
||||||
|
let hot = r.contains(vec2(mx, my)) || self.lang_open == Some(r);
|
||||||
|
draw_rectangle(r.x, r.y, r.w, r.h, if hot { Color::new(0.35, 0.22, 0.1, 1.0) } else { Color::new(0.13, 0.09, 0.08, 1.0) });
|
||||||
|
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0, if hot { FRAME_HI } else { FRAME });
|
||||||
|
let code = self.cur_lang.clone();
|
||||||
|
let name = languages().into_iter().find(|(c, _)| *c == code).map(|(_, n)| n).unwrap_or_else(|| code.clone());
|
||||||
|
self.lang_row(gfx, &code, &name, r, if hot { FRAME_HI } else { TXT });
|
||||||
|
gfx.glyph(if self.lang_open == Some(r) { '▲' } else { '▼' }, r.x + r.w - CW() * 1.5, r.y + CH() * 0.5, S(), if hot { FRAME_HI } else { DIM });
|
||||||
|
self.buttons.push((r, Btn::LangOpen));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a flag and a language name inside a row; returns the x where the text ends
|
||||||
|
fn lang_row(&self, gfx: &Gfx, code: &str, name: &str, r: Rect, col: Color) -> f32 {
|
||||||
|
let px = S();
|
||||||
|
let fy = r.y + (r.h - flags::FLAG_H as f32 * px) * 0.5;
|
||||||
|
flags::draw_flag(code, (r.x + CW() * 0.75).floor(), fy.floor(), px);
|
||||||
|
let tx = r.x + CW() * 0.75 + flags::FLAG_W as f32 * px + CW();
|
||||||
|
let maxc = ((r.x + r.w - CW() * 2.0 - tx) / CW()).max(1.0) as usize;
|
||||||
|
let name: String = name.chars().take(maxc).collect();
|
||||||
|
gfx.text(&name, tx, r.y + (r.h - CH()) * 0.5, S(), col);
|
||||||
|
tx + gfx.measure(&name, S())
|
||||||
|
}
|
||||||
|
|
||||||
|
// the open dropdown: all languages under (or, without room, above) the field; scrolls
|
||||||
|
fn draw_lang_list(&mut self, gfx: &Gfx, a: Rect) {
|
||||||
|
let (mx, my) = mouse();
|
||||||
|
let langs = languages();
|
||||||
|
let row_h = CH() * 2.0;
|
||||||
|
let below = ((sh() - a.y - a.h - CH()) / row_h).floor().max(0.0) as usize;
|
||||||
|
let above = ((a.y - CH()) / row_h).floor().max(0.0) as usize;
|
||||||
|
let down = below >= langs.len().min(6) || below >= above;
|
||||||
|
let rows = langs.len().min(if down { below } else { above }).max(1);
|
||||||
|
self.lang_rows = rows;
|
||||||
|
self.lang_scroll = self.lang_scroll.min(langs.len().saturating_sub(rows));
|
||||||
|
let w = a.w.max(CW() * 20.0);
|
||||||
|
let x = a.x.min(sw() - w - CW() * 0.5).max(0.0);
|
||||||
|
let y0 = if down { a.y + a.h } else { a.y - rows as f32 * row_h };
|
||||||
|
draw_rectangle(x, y0, w, rows as f32 * row_h, Color::new(0.06, 0.04, 0.05, 0.98));
|
||||||
|
for (k, (code, name)) in langs.iter().enumerate().skip(self.lang_scroll).take(rows) {
|
||||||
|
let r = Rect::new(x, y0 + (k - self.lang_scroll) as f32 * row_h, w, row_h);
|
||||||
|
let hot = r.contains(vec2(mx, my));
|
||||||
|
let cur = *code == self.cur_lang;
|
||||||
|
if hot || cur {
|
||||||
|
draw_rectangle(r.x, r.y, r.w, r.h, if hot { Color::new(0.35, 0.22, 0.1, 1.0) } else { Color::new(0.2, 0.13, 0.08, 1.0) });
|
||||||
|
}
|
||||||
|
self.lang_row(gfx, code, name, r, if hot || cur { FRAME_HI } else { TXT });
|
||||||
|
self.lang_opts.push((r, code.clone()));
|
||||||
|
}
|
||||||
|
// more languages than rows: arrows show that the list scrolls
|
||||||
|
if self.lang_scroll > 0 {
|
||||||
|
gfx.glyph('▲', x + w - CW() * 1.5, y0 + CH() * 0.5, S(), DIM);
|
||||||
|
}
|
||||||
|
if self.lang_scroll + rows < langs.len() {
|
||||||
|
gfx.glyph('▼', x + w - CW() * 1.5, y0 + rows as f32 * row_h - CH() * 1.5, S(), DIM);
|
||||||
|
}
|
||||||
|
draw_rectangle_lines(x, y0, w, rows as f32 * row_h, 2.0, FRAME_HI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mouse wheel / swipe over an open dropdown: scrolls its list; true if it took the motion
|
||||||
|
pub fn lang_wheel(&mut self, d: i32) -> bool {
|
||||||
|
if self.lang_open.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let n = languages().len();
|
||||||
|
self.lang_scroll = (self.lang_scroll as i32 + d).clamp(0, n.saturating_sub(self.lang_rows) as i32) as usize;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// the language dropdown in a corner of the title and login screens
|
||||||
|
fn corner_lang_field(&mut self, gfx: &Gfx) {
|
||||||
|
let w = CW() * 18.0;
|
||||||
|
self.lang_field(gfx, (sw() - w - CW()).max(0.0), CH(), w);
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_title(&mut self, gfx: &Gfx, cloud: &Cloud) {
|
fn draw_title(&mut self, gfx: &Gfx, cloud: &Cloud) {
|
||||||
let sw = sw();
|
let sw = sw();
|
||||||
let sh = sh();
|
let sh = sh();
|
||||||
@@ -899,6 +1013,7 @@ impl Menu {
|
|||||||
let credit = tr("help.font");
|
let credit = tr("help.font");
|
||||||
gfx.text(&credit, grid_center(sw, gfx.measure(&credit, SMALL())), sh - CH() * 7.0, SMALL(), DIM);
|
gfx.text(&credit, grid_center(sw, gfx.measure(&credit, SMALL())), sh - CH() * 7.0, SMALL(), DIM);
|
||||||
self.draw_license(gfx);
|
self.draw_license(gfx);
|
||||||
|
self.corner_lang_field(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// licence and repository link at the bottom of the screen (title and login)
|
// licence and repository link at the bottom of the screen (title and login)
|
||||||
@@ -1086,6 +1201,10 @@ impl Menu {
|
|||||||
// value with < > buttons
|
// value with < > buttons
|
||||||
let vw = 18.0 * CW();
|
let vw = 18.0 * CW();
|
||||||
let vx = x + cols as f32 * CW() - vw - CW() * 3.0;
|
let vx = x + cols as f32 * CW() - vw - CW() * 3.0;
|
||||||
|
if it == Item::Language {
|
||||||
|
self.lang_field(gfx, vx, by, vw);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let left = Rect::new(vx, by, CW() * 2.5, CH() * 2.0);
|
let left = Rect::new(vx, by, CW() * 2.5, CH() * 2.0);
|
||||||
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 2.0);
|
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 2.0);
|
||||||
button(gfx, left, "<", left.contains(vec2(mx, my)), FRAME);
|
button(gfx, left, "<", left.contains(vec2(mx, my)), FRAME);
|
||||||
@@ -1568,6 +1687,7 @@ impl Menu {
|
|||||||
self.buttons.push((br, Btn::DoLogin));
|
self.buttons.push((br, Btn::DoLogin));
|
||||||
}
|
}
|
||||||
self.draw_license(gfx);
|
self.draw_license(gfx);
|
||||||
|
self.corner_lang_field(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account: change the password, log out; the admin manages the users
|
// Account: change the password, log out; the admin manages the users
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
// Pixel flags for the language dropdown: 12 x 9 pixels each, one letter per pixel
|
||||||
|
// (see PALETTE). Simplified renditions; the coats of arms are a few pixels only.
|
||||||
|
|
||||||
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
|
pub const FLAG_W: usize = 12;
|
||||||
|
pub const FLAG_H: usize = 9;
|
||||||
|
|
||||||
|
fn palette(c: char) -> Option<Color> {
|
||||||
|
let rgb = |r: u8, g: u8, b: u8| Some(Color::from_rgba(r, g, b, 255));
|
||||||
|
match c {
|
||||||
|
'R' => rgb(206, 32, 41), // red
|
||||||
|
'W' => rgb(240, 240, 240), // white
|
||||||
|
'B' => rgb(0, 57, 166), // blue
|
||||||
|
'N' => rgb(1, 33, 105), // navy (Union Jack)
|
||||||
|
'Y' => rgb(255, 204, 0), // yellow
|
||||||
|
'G' => rgb(0, 146, 70), // green
|
||||||
|
'D' => rgb(0, 102, 0), // dark green (Portugal)
|
||||||
|
'K' => rgb(16, 16, 16), // black
|
||||||
|
'S' => rgb(0, 106, 167), // Swedish blue
|
||||||
|
'F' => rgb(0, 47, 108), // Finnish blue
|
||||||
|
'L' => rgb(13, 94, 175), // Greek blue
|
||||||
|
'O' => rgb(170, 21, 27), // dark red (coats of arms)
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// three horizontal stripes of three rows each
|
||||||
|
macro_rules! tricolour {
|
||||||
|
($a:literal, $b:literal, $c:literal) => {
|
||||||
|
[$a, $a, $a, $b, $b, $b, $c, $c, $c]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// the flag of a language code
|
||||||
|
pub fn flag(code: &str) -> Option<[&'static str; FLAG_H]> {
|
||||||
|
Some(match code {
|
||||||
|
"de" => tricolour!("KKKKKKKKKKKK", "RRRRRRRRRRRR", "YYYYYYYYYYYY"),
|
||||||
|
"nl" => tricolour!("RRRRRRRRRRRR", "WWWWWWWWWWWW", "BBBBBBBBBBBB"),
|
||||||
|
"ru" => tricolour!("WWWWWWWWWWWW", "BBBBBBBBBBBB", "RRRRRRRRRRRR"),
|
||||||
|
"hu" => tricolour!("RRRRRRRRRRRR", "WWWWWWWWWWWW", "GGGGGGGGGGGG"),
|
||||||
|
"it" => ["GGGGWWWWRRRR"; FLAG_H],
|
||||||
|
"ro" => ["BBBBYYYYRRRR"; FLAG_H],
|
||||||
|
"en" => [
|
||||||
|
"RWNNWRRWNNWR",
|
||||||
|
"WRRWWRRWWRRW",
|
||||||
|
"NNWRWRRWRWNN",
|
||||||
|
"WWWWWRRWWWWW",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"WWWWWRRWWWWW",
|
||||||
|
"NNWRWRRWRWNN",
|
||||||
|
"WRRWWRRWWRRW",
|
||||||
|
"RWNNWRRWNNWR",
|
||||||
|
],
|
||||||
|
"es" => [
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"YYYYYYYYYYYY",
|
||||||
|
"YYOOYYYYYYYY",
|
||||||
|
"YYOROYYYYYYY",
|
||||||
|
"YYOOYYYYYYYY",
|
||||||
|
"YYYYYYYYYYYY",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
],
|
||||||
|
"pt" => [
|
||||||
|
"DDDDDRRRRRRR",
|
||||||
|
"DDDDDRRRRRRR",
|
||||||
|
"DDDDDRRRRRRR",
|
||||||
|
"DDDYYYRRRRRR",
|
||||||
|
"DDDYRYRRRRRR",
|
||||||
|
"DDDYYYRRRRRR",
|
||||||
|
"DDDDDRRRRRRR",
|
||||||
|
"DDDDDRRRRRRR",
|
||||||
|
"DDDDDRRRRRRR",
|
||||||
|
],
|
||||||
|
"da" => [
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
"RRRWWRRRRRRR",
|
||||||
|
],
|
||||||
|
"sv" => [
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"YYYYYYYYYYYY",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
"SSSYYSSSSSSS",
|
||||||
|
],
|
||||||
|
"fi" => [
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
"FFFFFFFFFFFF",
|
||||||
|
"FFFFFFFFFFFF",
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
"WWWFFWWWWWWW",
|
||||||
|
],
|
||||||
|
"no" => [
|
||||||
|
"RRRWNWRRRRRR",
|
||||||
|
"RRRWNWRRRRRR",
|
||||||
|
"RRRWNWRRRRRR",
|
||||||
|
"WWWWNWWWWWWW",
|
||||||
|
"NNNNNNNNNNNN",
|
||||||
|
"WWWWNWWWWWWW",
|
||||||
|
"RRRWNWRRRRRR",
|
||||||
|
"RRRWNWRRRRRR",
|
||||||
|
"RRRWNWRRRRRR",
|
||||||
|
],
|
||||||
|
"hr" => [
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"WWWWRWRWWWWW",
|
||||||
|
"WWWWWRWRWWWW",
|
||||||
|
"WWWWRWRWWWWW",
|
||||||
|
"BBBBWRWRBBBB",
|
||||||
|
"BBBBBWRBBBBB",
|
||||||
|
"BBBBBBBBBBBB",
|
||||||
|
],
|
||||||
|
"sr" => [
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
"RRRYYRRRRRRR",
|
||||||
|
"RROWWORRRRRR",
|
||||||
|
"BBOWWOBBBBBB",
|
||||||
|
"BBOWWOBBBBBB",
|
||||||
|
"BBBOOBBBBBBB",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
],
|
||||||
|
"sk" => [
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"WWRRRRRWWWWW",
|
||||||
|
"BBRRWRRBBBBB",
|
||||||
|
"BBRWWWRBBBBB",
|
||||||
|
"BBRRWRRBBBBB",
|
||||||
|
"RRRBBBRRRRRR",
|
||||||
|
"RRRRBRRRRRRR",
|
||||||
|
"RRRRRRRRRRRR",
|
||||||
|
],
|
||||||
|
"cs" => [
|
||||||
|
"BWWWWWWWWWWW",
|
||||||
|
"BBWWWWWWWWWW",
|
||||||
|
"BBBWWWWWWWWW",
|
||||||
|
"BBBBWWWWWWWW",
|
||||||
|
"BBBBBRRRRRRR",
|
||||||
|
"BBBBRRRRRRRR",
|
||||||
|
"BBBRRRRRRRRR",
|
||||||
|
"BBRRRRRRRRRR",
|
||||||
|
"BRRRRRRRRRRR",
|
||||||
|
],
|
||||||
|
"el" => [
|
||||||
|
"LLWLLWWWWWWW",
|
||||||
|
"LLWLLLLLLLLL",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"LLWLLLLLLLLL",
|
||||||
|
"LLWLLWWWWWWW",
|
||||||
|
"LLLLLLLLLLLL",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
"LLLLLLLLLLLL",
|
||||||
|
"WWWWWWWWWWWW",
|
||||||
|
],
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// draws a flag with its top-left corner at (x, y), px screen pixels per flag pixel
|
||||||
|
pub fn draw_flag(code: &str, x: f32, y: f32, px: f32) {
|
||||||
|
let rows = flag(code).unwrap_or(["WWWWWWWWWWWW"; FLAG_H]);
|
||||||
|
for (r, row) in rows.iter().enumerate() {
|
||||||
|
for (k, ch) in row.chars().enumerate() {
|
||||||
|
if let Some(c) = palette(ch) {
|
||||||
|
draw_rectangle(x + k as f32 * px, y + r as f32 * px, px, px, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw_rectangle_lines(x - 1.0, y - 1.0, FLAG_W as f32 * px + 2.0, FLAG_H as f32 * px + 2.0, 1.0, Color::new(0.0, 0.0, 0.0, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_language_has_a_complete_flag() {
|
||||||
|
for code in ["de", "en", "es", "pt", "nl", "da", "no", "sv", "fi", "ru", "hr", "hu", "sr", "ro", "sk", "cs", "it", "el"] {
|
||||||
|
let f = flag(code).unwrap_or_else(|| panic!("{} has a flag", code));
|
||||||
|
for row in f.iter() {
|
||||||
|
assert_eq!(row.chars().count(), FLAG_W, "{}: {}", code, row);
|
||||||
|
assert!(row.chars().all(|c| palette(c).is_some()), "{}: {}", code, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user