diff --git a/src/editor/blocks_ui.rs b/src/editor/blocks_ui.rs index 5a27177..6564920 100644 --- a/src/editor/blocks_ui.rs +++ b/src/editor/blocks_ui.rs @@ -169,7 +169,6 @@ impl BlockEdit { if im.button(gfx, Rect::new(CW(), CH() * 0.7, CW() * 12.0, CH() * 1.7), &tr("ed.back")) || (is_key_pressed(KeyCode::Escape) && !im.typing && self.drag.is_none()) { back = true; } - gfx.text(&title, CW() * 14.0, CH() * 1.0, S(), FRAME_HI); let mut bx = w - CW(); let buttons = [tr("ed.delete"), tr("ed.paste"), tr("ed.copy"), tr("ed.redo"), tr("ed.undo")]; let mut pressed_btn = None; @@ -184,6 +183,10 @@ impl BlockEdit { let sw_ = CW() * 18.0; bx -= sw_ + CW(); im.text_field(gfx, id("bsearch", 0), Rect::new(bx, CH() * 0.7, sw_, CH() * 1.7), &mut self.search, 24); + // the script's name, as much as fits before the search field + let room = ((bx - CW() * 15.0) / CW()).max(4.0) as usize; + let title: String = title.chars().take(room).collect(); + gfx.text(&title, CW() * 14.0, CH() * 1.0, S(), FRAME_HI); if self.search.is_empty() && im.focus != Some(id("bsearch", 0)) { gfx.text(&tr("ed.search"), bx + CW() * 0.5, CH() * 1.0, S(), DIM); } @@ -294,7 +297,9 @@ impl BlockEdit { } y += CH() * 2.2; } - gfx.text(&tr("ed.blocks_hint"), CW() * 0.5, h - CH() * 6.0, SMALL(), DIM); + for (k, l) in wrap(&tr("ed.blocks_hint"), ((pal_w - CW()) / (GW * SMALL())) as usize).iter().enumerate() { + gfx.text(l, CW() * 0.5, h - CH() * 6.0 + k as f32 * GH * SMALL() * 1.2, SMALL(), DIM); + } // ---- the script let ws = Rect::new(pal_w, top, w - pal_w, h - top - CH() * 2.0); @@ -418,13 +423,15 @@ impl BlockEdit { path.push(k); let cat = list[k].category(); let col = cat_color(cat); - let header = Rect::new(x, y, w, CH() * ROW); + // the spawn block has many fields: two rows + let rows = if matches!(list[k], Block::Spawn { .. }) { 2.0 } else { 1.0 }; + let header = Rect::new(x, y, w, CH() * ROW * rows); let problem = ctx.problems.iter().find(|(p, _)| *p == path).map(|(_, k)| *k); let selected = self.selected.as_ref() == Some(&path); let found = !ctx.search.is_empty() && (tr(&format!("blk.{}", list[k].kind_name())).to_lowercase().contains(&ctx.search) || block_text(&list[k]).to_lowercase().contains(&ctx.search)); // slots: upper half before this block, lower half after it - slots.push((Rect::new(x, y - CH() * 0.3, w, CH() * ROW * 0.5 + CH() * 0.3), lp.to_vec(), k)); - slots.push((Rect::new(x, y + CH() * ROW * 0.5, w, CH() * ROW * 0.5), lp.to_vec(), k + 1)); + slots.push((Rect::new(x, y - CH() * 0.3, w, header.h * 0.5 + CH() * 0.3), lp.to_vec(), k)); + slots.push((Rect::new(x, y + header.h * 0.5, w, header.h * 0.5), lp.to_vec(), k + 1)); let nested = matches!(list[k], Block::If { .. } | Block::Repeat { .. }); // the block: header row with its fields draw_block_shape(header, col, selected, found); @@ -433,7 +440,7 @@ impl BlockEdit { im.tip(header, &tr(p)); } // grip: drag the block from here - let grip = Rect::new(x, y, CW() * 1.4, CH() * ROW); + let grip = Rect::new(x, y, CW() * 1.4, header.h); gfx.text("≡", x + CW() * 0.3, y + CH() * 0.6, S(), Color::new(1.0, 1.0, 1.0, 0.6)); if im.pressed && im.over(grip) { im.pressed = false; @@ -441,7 +448,7 @@ impl BlockEdit { } let fy = y + (CH() * ROW - CH() * 1.5) * 0.5; let mut cx = x + CW() * 1.8; - self.block_fields(im, gfx, &mut list[k], &path, &mut cx, fy, ctx); + self.block_fields(im, gfx, &mut list[k], &path, &mut cx, fy, x + CW() * 3.0, ctx); // a click on the block's free area selects it if im.clicked(header) { ops.push(Op::Select(path.clone())); @@ -455,7 +462,7 @@ impl BlockEdit { } self.help = tr(&format!("blk.help.{}", list[k].kind_name())); } - y += CH() * ROW; + y += header.h; if nested { // the C shape: bodies indented, with an "else" bar for if let inner_x = x + CW() * 2.5; @@ -496,7 +503,7 @@ impl BlockEdit { // the fields inside a block's header row #[allow(clippy::too_many_arguments)] - fn block_fields(&mut self, im: &mut Im, gfx: &Gfx, b: &mut Block, path: &[usize], cx: &mut f32, y: f32, ctx: &Ctx) { + fn block_fields(&mut self, im: &mut Im, gfx: &Gfx, b: &mut Block, path: &[usize], cx: &mut f32, y: f32, x2: f32, ctx: &Ctx) { let word = |cx: &mut f32, s: &str| { gfx.text(s, *cx, y + CH() * 0.25, S(), TXT); *cx += gfx.measure(s, S()) + CW() * 0.6; @@ -517,6 +524,13 @@ impl BlockEdit { word(cx, &tr("blk.w_level")); self.value(im, gfx, level, Ty::Num, path, 3, cx, y, ctx); self.side_dd(im, gfx, side, false, path, 4, cx, y); + // second row: where, and the name + let y = y + CH() * ROW; + *cx = x2; + let word = |cx: &mut f32, s: &str| { + gfx.text(s, *cx, y + CH() * 0.25, S(), TXT); + *cx += gfx.measure(s, S()) + CW() * 0.6; + }; word(cx, &tr("blk.w_at")); let mut opts = vec![tr("blk.place_core"), tr("blk.place_event")]; opts.extend(ctx.zones.iter().map(|z| trf("blk.place_zone", &[z.clone()]))); diff --git a/src/editor/map.rs b/src/editor/map.rs index db25df0..57a8683 100644 --- a/src/editor/map.rs +++ b/src/editor/map.rs @@ -91,6 +91,9 @@ impl MapEdit { game.map.activate(); game.recount(); game.ents.retain(|e| e.alive); + // no fog of war while painting (it is worked out again when the map is stored) + game.map.explored.clear(); + game.map.visible.clear(); let (cx, cy) = game.map.core; let mut cam = Cam::new(cx as f32 + 1.5, cy as f32 + 1.5); cam.zoom = 28.0; @@ -653,16 +656,16 @@ impl MapEdit { } y += CH() * 2.3; // categories as a grid of buttons - let cw = (bar_w - CW() * 2.5) / 3.0; + let cw = (bar_w - CW() * 2.25) / 2.0; for (k, c) in CATS.iter().enumerate() { - let r = Rect::new(x + (k % 3) as f32 * (cw + CW() * 0.25), y + (k / 3) as f32 * CH() * 1.9, cw, CH() * 1.7); + let r = Rect::new(x + (k % 2) as f32 * (cw + CW() * 0.25), y + (k / 2) as f32 * CH() * 1.8, cw, CH() * 1.6); let name = tr(&format!("ed.cat.{:?}", c).to_lowercase()); if im.select(gfx, r, &name, self.cat == *c, TXT) { self.cat = *c; self.item = 0; } } - y += CH() * 6.0; + y += CH() * 1.8 * ((CATS.len() + 1) / 2) as f32 + CH() * 0.4; // the items of the category let items: Vec = match self.cat { Cat::Terrain => TERRAIN.iter().map(|t| tr(&format!("ed.terrain.{}", t))).collect(), @@ -780,14 +783,16 @@ impl MapEdit { self.mirror = c as u8; } bx += CW() * 19.0; - if im.button(gfx, Rect::new(bx, by, CW() * 8.0, CH() * 1.7), &tr("ed.undo")) { + let uw = gfx.measure(&tr("ed.undo"), S()) + CW() * 2.0; + if im.button(gfx, Rect::new(bx, by, uw, CH() * 1.7), &tr("ed.undo")) { self.undo(); } - bx += CW() * 8.5; - if im.button(gfx, Rect::new(bx, by, CW() * 8.0, CH() * 1.7), &tr("ed.redo")) { + bx += uw + CW() * 0.5; + let rw = gfx.measure(&tr("ed.redo"), S()) + CW() * 2.0; + if im.button(gfx, Rect::new(bx, by, rw, CH() * 1.7), &tr("ed.redo")) { self.redo(); } - bx += CW() * 9.0; + bx += rw + CW(); let info = trf("ed.map_info", &[self.game.map.w.to_string(), self.game.map.h.to_string(), self.game.ents.iter().filter(|e| e.alive).count().to_string(), self.game.towers.len().to_string()]); gfx.text(&info, bx, by + CH() * 0.3, SMALL(), DIM); gfx.text(&tr("ed.map_hint"), bar_w + CW(), sh() - CH() * 1.5, SMALL(), DIM); diff --git a/src/editor/mod.rs b/src/editor/mod.rs index d60d7ac..7f786c4 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -207,6 +207,13 @@ impl Editor { pub fn frame(&mut self, gfx: &Gfx, r3d: &mut R3d, dt: f32, now: f64) -> EdAction { clear_background(Color::from_rgba(4, 3, 5, 255)); self.im.begin(); + // an open dialog takes the clicks: the page behind it only draws + let held = (self.im.pressed, self.im.released, self.im.wheel); + if self.open_dialog { + self.im.pressed = false; + self.im.released = false; + self.im.wheel = 0.0; + } let mut act = EdAction::None; match self.screen { Screen::Map(i) => { @@ -236,6 +243,7 @@ impl Editor { } } if self.open_dialog { + (self.im.pressed, self.im.released, self.im.wheel) = held; begin_ui(); if let Some(a) = self.open_screen(gfx) { act = a; @@ -263,30 +271,18 @@ impl Editor { let mut act = EdAction::None; let (w, h) = (sw(), sh()); let im = &mut self.im; - // top bar: name, author, language, file and server buttons + // top bar: the title and the buttons, then name, author, language let bar_h = CH() * 5.0; panel(gfx, Rect::new(0.0, 0.0, w, bar_h), ""); - gfx.text(&tr("ed.campaign"), CW(), CH() * 0.4, S(), FRAME_HI); - let fw = (CW() * 24.0).min(w * 0.25); - label(gfx, CW(), CH() * 2.0, &tr("ed.name"), DIM); - im.text_field(gfx, id("cname", 0), Rect::new(CW() * 8.0, CH() * 1.7, fw, CH() * 1.5), &mut self.doc.name, 40); - label(gfx, CW(), CH() * 3.5, &tr("ed.author"), DIM); - im.text_field(gfx, id("cauthor", 0), Rect::new(CW() * 8.0, CH() * 3.3, fw, CH() * 1.5), &mut self.doc.author, 32); - let lx = CW() * 9.0 + fw; - label(gfx, lx, CH() * 2.0, &tr("ed.language"), DIM); - let langs = languages(); - let names: Vec = langs.iter().map(|(_, n)| n.clone()).collect(); - let cur = langs.iter().find(|(c, _)| *c == self.doc.language).map(|(_, n)| n.clone()).unwrap_or_else(|| self.doc.language.clone()); - if let Some(k) = im.dropdown(gfx, id("clang", 0), Rect::new(lx, CH() * 3.3, CW() * 14.0, CH() * 1.5), &cur, &names) { - self.doc.language = langs[k].0.clone(); - } - // buttons on the right + gfx.text(&tr("ed.campaign"), CW(), CH() * 0.8, S(), FRAME_HI); let labels = [tr("ed.play"), tr("ed.new"), tr("ed.open"), tr("ed.save_file"), tr("ed.save_server"), tr("ed.close")]; - let bw = labels.iter().map(|l| gfx.measure(l, S())).fold(0.0, f32::max) + CW() * 2.0; - let mut bx = w - CW() - bw; + let mut bx = w - CW(); for k in (0..labels.len()).rev() { - let r = Rect::new(bx, CH() * 0.6, bw, CH() * 1.7); - if im.button(gfx, r, &labels[k]) { + let bw = gfx.measure(&labels[k], S()) + CW() * 2.0; + bx -= bw; + let r = Rect::new(bx, CH() * 0.5, bw, CH() * 1.7); + let clicked = if k == 0 { im.button_c(gfx, r, &labels[k], GREEN) } else { im.button(gfx, r, &labels[k]) }; + if clicked { match k { 0 => act = EdAction::Play, 1 => { @@ -303,18 +299,39 @@ impl Editor { _ => act = EdAction::Close, } } - bx -= bw + CW() * 0.5; + bx -= CW() * 0.5; } + let y2 = CH() * 2.9; + let fw = (CW() * 22.0).min(w * 0.2); + let mut x = CW(); + label(gfx, x, y2 + CH() * 0.25, &tr("ed.name"), DIM); + x += gfx.measure(&tr("ed.name"), S()) + CW(); + im.text_field(gfx, id("cname", 0), Rect::new(x, y2, fw, CH() * 1.5), &mut self.doc.name, 40); + x += fw + CW() * 1.5; + label(gfx, x, y2 + CH() * 0.25, &tr("ed.author"), DIM); + x += gfx.measure(&tr("ed.author"), S()) + CW(); + im.text_field(gfx, id("cauthor", 0), Rect::new(x, y2, fw * 0.8, CH() * 1.5), &mut self.doc.author, 32); + x += fw * 0.8 + CW() * 1.5; + label(gfx, x, y2 + CH() * 0.25, &tr("ed.language"), DIM); + x += gfx.measure(&tr("ed.language"), S()) + CW(); + let langs = languages(); + let names: Vec = langs.iter().map(|(_, n)| n.clone()).collect(); + let cur = langs.iter().find(|(c, _)| *c == self.doc.language).map(|(_, n)| n.clone()).unwrap_or_else(|| self.doc.language.clone()); + if let Some(k) = im.dropdown(gfx, id("clang", 0), Rect::new(x, y2, CW() * 14.0, CH() * 1.5), &cur, &names) { + self.doc.language = langs[k].0.clone(); + } + x += CW() * 15.5; let mut publ = self.published; - if self.server_id.is_some() && im.toggle(gfx, w - CW() * 2.0 - bw * 3.5, CH() * 3.1, &tr("ed.published"), &mut publ) { + if self.server_id.is_some() && im.toggle(gfx, x, y2, &tr("ed.published"), &mut publ) { self.published = publ; act = EdAction::Publish(publ); } - let add = Rect::new(w - CW() - bw, CH() * 3.0, bw, CH() * 1.7); - if im.button(gfx, add, &tr("ed.add_mission")) { + let al = tr("ed.add_mission"); + let aw = gfx.measure(&al, S()) + CW() * 2.0; + if im.button(gfx, Rect::new(w - CW() - aw, y2 - CH() * 0.1, aw, CH() * 1.7), &al) { let n = self.doc.missions.len(); self.doc.add_mission(); - self.doc.missions[n].pos = (-self.graph.pan.x / CW() * 0.0 + 40.0 + (n % 5) as f32 * 220.0, 40.0 + (n / 5) as f32 * 140.0); + self.doc.missions[n].pos = (40.0 + (n % 5) as f32 * 220.0, 40.0 + (n / 5) as f32 * 140.0); } // the canvas: cards and arrows