1use std::sync::Arc;
2
3use crate::Game;
4use crate::block::{BV, Block, BlockItem, Comparator, Eq::Single, Field};
5use crate::data::gui::{GuiTemplate, GuiType};
6use crate::datacontext::DataContext;
7use crate::everything::Everything;
8use crate::gui::validate::validate_property;
9use crate::gui::{BuiltinWidget, GuiValidation, PropertyContainer, WidgetProperty};
10use crate::helpers::{TigerHashMap, TigerHashSet};
11use crate::lowercase::Lowercase;
12use crate::report::{ErrorKey, err, untidy, warn};
13use crate::token::Token;
14
15#[derive(Debug, Clone)]
17enum GuiItem {
18 Property(WidgetProperty, Token, BV),
20 Widget(Arc<GuiBlock>),
22 #[allow(dead_code)]
24 ActionTooltip(Arc<GuiBlock>),
25 ComplexProperty(Arc<GuiBlock>),
27 WidgetProperty(Arc<GuiBlock>),
30 Subst(String, Arc<GuiBlock>),
32 Override(Token, Arc<GuiBlock>),
36}
37
38#[derive(Debug, Clone, Default)]
40pub struct GuiBlock {
41 container: Option<PropertyContainer>,
44 base: Option<Arc<GuiBlock>>,
46 items: Vec<GuiItem>,
48 substnames: TigerHashSet<String>,
50}
51
52#[derive(Debug, Clone, Copy)]
55pub enum GuiBlockFrom<'a> {
56 Template,
58 NoParent,
60 WidgetKey(&'a Token),
62 PropertyKey(WidgetProperty),
64 TypeBase(&'a Token),
66 TypeWrapper(&'a Token),
68}
69
70impl GuiBlock {
71 pub fn from_block(
73 from: GuiBlockFrom,
74 block: &Block,
75 types: &TigerHashMap<Lowercase<'static>, GuiType>,
76 templates: &TigerHashMap<&'static str, GuiTemplate>,
77 ) -> Arc<Self> {
78 enum Expecting<'a> {
79 Field,
80 SubstBlock,
81 SubstBlockBody(&'a Token),
82 BlockOverride,
83 BlockOverrideBody(&'a Token),
84 }
85 let mut state = Expecting::Field;
86
87 let mut gui = Self {
89 container: None,
90 base: None,
91 items: Vec::new(),
92 substnames: TigerHashSet::default(),
93 };
94
95 match from {
97 GuiBlockFrom::Template | GuiBlockFrom::NoParent => (),
98 GuiBlockFrom::WidgetKey(base) | GuiBlockFrom::TypeBase(base) => {
99 if let Some(basetype) = types.get(&Lowercase::new(base.as_str())) {
100 gui.container = basetype.builtin(types).map(PropertyContainer::from);
101 let gui_block = basetype.gui_block(types, templates);
102 gui.substnames.clone_from(&gui_block.substnames);
103 gui.base = Some(gui_block);
104 }
105 }
106 GuiBlockFrom::PropertyKey(prop) => {
107 gui.container = PropertyContainer::try_from(prop).ok();
108 }
109 GuiBlockFrom::TypeWrapper(base) => {
110 if let Some(basetype) = types.get(&Lowercase::new(base.as_str())) {
111 gui.container = basetype.builtin(types).map(PropertyContainer::from);
112 }
113 }
114 }
115
116 for item in block.iter_items() {
117 match state {
118 Expecting::Field => {
119 if let BlockItem::Field(Field(key, cmp, bv)) = item {
120 let key_lc = Lowercase::new(key.as_str());
121 if !matches!(cmp, Comparator::Equals(Single)) {
122 let msg = format!("expected `{key} =`, found `{cmp}`");
123 untidy(ErrorKey::Validation).msg(msg).loc(key).push();
124 }
125 if key_lc == "block" {
126 if let Some(value) = bv.expect_value() {
127 state = Expecting::SubstBlockBody(value);
128 }
129 } else if key_lc == "blockoverride" {
130 if let Some(value) = bv.expect_value() {
131 state = Expecting::BlockOverrideBody(value);
132 }
133 } else if key_lc == "using" {
134 if let Some(value) = bv.expect_value() {
135 if let Some(template) = templates.get(value.as_str()) {
136 gui.inline(&template.gui_block(types, templates));
137 } else {
138 untidy(ErrorKey::Gui).msg("template not found").loc(key).push();
139 }
140 }
141 } else if types.get(key_lc.as_str()).is_some()
142 || BuiltinWidget::builtin_current_game(&key_lc).is_some()
143 {
144 if let Some(block) = bv.expect_block() {
145 let guiblock = GuiBlock::from_block(
146 GuiBlockFrom::WidgetKey(key),
147 block,
148 types,
149 templates,
150 );
151 gui.substnames.extend(guiblock.substnames.iter().cloned());
152 gui.items.push(GuiItem::Widget(guiblock));
153 }
154 } else if let Ok(prop) = WidgetProperty::try_from(&key_lc) {
155 let validation = GuiValidation::from_property(prop);
156 if validation == GuiValidation::ComplexProperty {
157 if let Some(block) = bv.expect_block() {
158 let guiblock = GuiBlock::from_block(
159 GuiBlockFrom::PropertyKey(prop),
160 block,
161 types,
162 templates,
163 );
164 gui.items.push(GuiItem::ComplexProperty(guiblock));
165 }
166 } else if validation == GuiValidation::Widget {
167 match bv {
172 BV::Block(block)
173 if !block.field_value_is("recursive", "yes")
174 && prop != WidgetProperty::tooltipwidget =>
175 {
176 let guiblock = GuiBlock::from_block(
177 GuiBlockFrom::PropertyKey(prop),
178 block,
179 types,
180 templates,
181 );
182 gui.items.push(GuiItem::WidgetProperty(guiblock));
183 }
184 _ => {
185 gui.items.push(GuiItem::Property(
186 prop,
187 key.clone(),
188 bv.clone(),
189 ));
190 }
191 }
192 } else if validation == GuiValidation::ActionTooltip {
193 if !Game::is_eu5() {
194 let msg = "action tooltip is only for EU5";
195 err(ErrorKey::WrongGame).msg(msg).loc(bv).push();
196 }
197 bv.expect_block();
198 gui.items.push(GuiItem::Property(prop, key.clone(), bv.clone()));
199 } else {
200 gui.items.push(GuiItem::Property(prop, key.clone(), bv.clone()));
201 }
202 } else if let Ok(builtin) = BuiltinWidget::try_from(&key_lc) {
203 let msg = format!(
205 "builtin widget `{key}` is only for {}",
206 builtin.to_game_flags()
207 );
208 err(ErrorKey::WrongGame).weak().msg(msg).loc(key).push();
209 } else {
210 let msg = format!("unknown gui field `{key}`");
211 err(ErrorKey::UnknownField).weak().msg(msg).loc(key).push();
212 }
213 } else if let Some(key) = item.expect_value() {
214 let key_lc = Lowercase::new(key.as_str());
215 if key_lc == "block" {
216 state = Expecting::SubstBlock;
217 } else if key_lc == "blockoverride" {
218 state = Expecting::BlockOverride;
219 } else {
220 warn(ErrorKey::Gui).msg("unexpected value").loc(key).push();
221 }
222 }
223 }
224 Expecting::SubstBlock => {
225 if let Some(name) = item.expect_value() {
226 state = Expecting::SubstBlockBody(name);
227 } else {
228 state = Expecting::Field;
229 }
230 }
231 Expecting::BlockOverride => {
232 if let Some(name) = item.expect_value() {
233 state = Expecting::BlockOverrideBody(name);
234 } else {
235 state = Expecting::Field;
236 }
237 }
238 Expecting::SubstBlockBody(name) => {
239 if let Some(block) = item.expect_block() {
240 let guiblock =
241 GuiBlock::from_block(GuiBlockFrom::NoParent, block, types, templates);
242 gui.substnames.insert(name.to_string());
243 gui.substnames.extend(guiblock.substnames.iter().cloned());
244 gui.items.push(GuiItem::Subst(name.to_string(), guiblock));
245 }
246 state = Expecting::Field;
247 }
248 Expecting::BlockOverrideBody(name) => {
249 if let Some(block) = item.expect_block() {
250 if gui.substnames.contains(name.as_str()) {
251 let guiblock = GuiBlock::from_block(
252 GuiBlockFrom::NoParent,
253 block,
254 types,
255 templates,
256 );
257 gui.apply_override(name, &guiblock);
258 gui.items.push(GuiItem::Override(name.clone(), guiblock));
259 } else if !matches!(from, GuiBlockFrom::Template | GuiBlockFrom::NoParent) {
260 }
264 }
265 state = Expecting::Field;
266 }
267 }
268 }
269 Arc::new(gui)
270 }
271
272 pub fn inline(&mut self, other: &Arc<GuiBlock>) {
273 self.substnames.extend(other.substnames.iter().cloned());
274 for item in &other.items {
275 if let GuiItem::Override(name, gui_block) = item {
276 self.apply_override(name, gui_block);
277 }
278 self.items.push(item.clone());
279 }
280 }
281
282 pub fn apply_override(&mut self, name: &Token, overrideblock: &Arc<GuiBlock>) {
284 if !self.substnames.contains(name.as_str()) {
285 return;
286 }
287
288 self.substnames.extend(overrideblock.substnames.iter().cloned());
289
290 if let Some(mut base) = self.base.clone() {
291 self.base = Some(Self::apply_override_arc(&mut base, name, overrideblock));
292 }
293
294 for item in &mut self.items {
295 match item {
296 GuiItem::Property(_, _, _) | GuiItem::Override(_, _) => (),
297 GuiItem::Widget(gui)
298 | GuiItem::ComplexProperty(gui)
299 | GuiItem::WidgetProperty(gui)
300 | GuiItem::ActionTooltip(gui) => {
301 *gui = Self::apply_override_arc(gui, name, overrideblock);
302 }
303 GuiItem::Subst(substname, gui) => {
304 if name.is(substname) {
305 *gui = Arc::clone(overrideblock);
306 }
307 }
308 }
309 }
310 }
311
312 pub fn apply_override_arc(
315 gui: &mut Arc<GuiBlock>,
316 name: &Token,
317 overrideblock: &Arc<GuiBlock>,
318 ) -> Arc<GuiBlock> {
319 if !gui.substnames.contains(name.as_str()) {
320 return Arc::clone(gui);
321 }
322
323 let gui_mut = Arc::make_mut(gui); gui_mut.apply_override(name, overrideblock);
325 Arc::clone(gui)
326 }
327
328 pub fn validate(
332 &self,
333 container: Option<PropertyContainer>,
334 data: &Everything,
335 dc: &mut DataContext,
336 ) {
337 let container = self.container.or(container);
338 if let Some(base) = &self.base {
339 base.validate(container, data, dc);
340 }
341
342 for item in &self.items {
343 match item {
344 GuiItem::Property(prop, key, bv) => {
345 validate_property(*prop, container, key, bv, data, dc);
346 }
347 GuiItem::Subst(_, gui_block) => {
348 gui_block.validate(container, data, dc);
349 }
350 GuiItem::Widget(gui_block)
351 | GuiItem::ComplexProperty(gui_block)
352 | GuiItem::WidgetProperty(gui_block) => {
353 gui_block.validate(None, data, dc);
354 }
355 GuiItem::ActionTooltip(gui_block) => {
356 gui_block.validate_action_tooltip(None, data, dc);
357 }
358 GuiItem::Override(_, _) => (),
359 }
360 }
361 }
362
363 pub fn validate_action_tooltip(
364 &self,
365 container: Option<PropertyContainer>,
366 data: &Everything,
367 dc: &mut DataContext,
368 ) {
369 for item in &self.items {
370 match item {
371 GuiItem::Property(prop, key, bv) => {
372 validate_property(*prop, container, key, bv, data, dc);
373 }
374 GuiItem::Subst(_, gui_block) => {
375 gui_block.validate(container, data, dc);
376 }
377 GuiItem::Widget(_)
378 | GuiItem::ComplexProperty(_)
379 | GuiItem::WidgetProperty(_)
380 | GuiItem::ActionTooltip(_)
381 | GuiItem::Override(_, _) => (),
382 }
383 }
384 }
385}