!7 cmodule DialogEditor { L entries = synchroList(); S globalID = aGlobalID(); transient JPanel stack; transient JTextArea taRaw; transient ReliableSingleThread rstFromRaw; start { rstFromRaw = dm_rstWithDelay(500, r { if (taRaw != null) fromRaw(getText(taRaw)) }); onChangeAndNow(r { if (taRaw != null) setText(taRaw, toRaw()); if (empty(entries) || nempty(last(entries).text)) { new TypeAndText e; entries.add(e); change(); if (stack != null) addAndValidate(stack, makeLine(e)); } }); } visualize { stack = dynamicVStack2(); fillStack(); taRaw = jTypeWriterTextArea(toRaw()); main.onChange(taRaw, rstFromRaw); ret centerAndSouthWithMargins(jtabs( "Dialog" := jscroll_trackWidth(withRightMargin(stack)), "Raw" := withMargin(taRaw)), centerAndEastWithMargin( withLabel("Dialog ID:", dm_textField('globalID)), jbutton("Save", r save)); } void fillStack { if (stack == null) ret; removeAllComponents(stack); for (TypeAndText e : cloneList(entries)) addAndValidate(stack, makeLine(e)); } JPanel makeLine(final TypeAndText e) { final JComboBox cb = jcombobox(ai_lineTypes(), e.type); final JTextField tf = jtextfield(e.text); main.onChange(cb, r { e.type = selectedItem(cb); change(); }); main.onChange(tf, r { e.text = gtt(tf); change(); }); ret westAndCenterWithMargin(cb, tf); } S toRaw() { ret lines(nempties(map(entries, func(TypeAndText e) -> S { bothEmpty(e.text, e.type) ? "" : or2(e.type, "?") + ": " + unnull(e.text) }))); } void fromRaw(S raw) { L entries = asSynchroList(ai_parseTypedDialog(raw)); if (empty(entries) || nempty(last(entries).text)) entries.add(new TypeAndText); if (setField(+entries)) fillStack(); } void save { if (emptyAfterTrim(globalID)) setField(globalID := aGlobalID()); dm_requireAndCallModule("#1021238/DialogsCRUD", 'saveDialog, globalID, toRaw()); } }