From f535443a1b7a54bbe12baa8fdf0b144d35803fed Mon Sep 17 00:00:00 2001 From: Fritz Zaucker Date: Thu, 15 Feb 2024 16:08:34 +0100 Subject: [PATCH 1/2] Make foreach function calls consistent --- CHANGES | 3 +++ .../source/class/callbackery/ui/Card.js | 21 ++++++++++++------- .../source/class/callbackery/ui/form/Auto.js | 18 ++++++++-------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index 4384fd38..10e79620 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ + - Translate tooltip and placeholder in Card.js + - Make foreach function calls consistent in Card.js and Auto.js + 0.48.1 2023-12-01 17:32:03 +0100 Tobias Oetiker - use CB_CFG_ as prefix ... this makes more sense than CM_CB diff --git a/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/Card.js b/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/Card.js index 54ca1443..fefaa3a8 100644 --- a/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/Card.js +++ b/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/Card.js @@ -26,7 +26,7 @@ qx.Class.define("callbackery.ui.Card", { this.__dataCache = {}; this.__actions = []; - cfg.action.forEach(function(action) { + cfg.action.forEach(action => { if (action.addToContextMenu) { this.__actions.push(action); } @@ -111,7 +111,7 @@ qx.Class.define("callbackery.ui.Card", { this._setLayout(layout); // add form elements - formCfg.forEach(function(cfg) { + formCfg.forEach(cfg => { var labelCfg = cfg.label; var fieldCfg = cfg.field; @@ -124,12 +124,12 @@ qx.Class.define("callbackery.ui.Card", { font : 'cardLabel' }); if (labelCfg.set) { + ['value','tooltip'].forEach(key => { + if (key in labelCfg.set){ + labelCfg.set[key] = this.xtr(labelCfg.set[key]); + } + }); label.set(labelCfg.set); - // canot use set({}) with xtr(). - // TODO: fix xtr() return - if (labelCfg.set.value) { - label.setValue(this.xtr(labelCfg.set.value)); - } } this._add(label, labelCfg.addSet); } @@ -145,6 +145,11 @@ qx.Class.define("callbackery.ui.Card", { var fieldClass = qx.Bootstrap.getByName(className); var field = new fieldClass; if (fieldCfg.set) { + ['placeholder','tooltip'].forEach(key => { + if (key in fieldCfg.set){ + fieldCfg.set[key] = this.xtr(fieldCfg.set[key]); + } + }); field.set(fieldCfg.set); } var event; @@ -216,7 +221,7 @@ qx.Class.define("callbackery.ui.Card", { }, this); // add action buttons - this.__actions.forEach(function(action) { + this.__actions.forEach(action => { var btn = this.__createButton(this.xtr(action.label), action.buttonSet.icon); btn.addListener('execute', function() { this.__parentForm.setSelection(this.__dataCache); diff --git a/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/form/Auto.js b/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/form/Auto.js index e8f18c18..2fa45510 100644 --- a/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/form/Auto.js +++ b/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/form/Auto.js @@ -59,7 +59,7 @@ qx.Class.define("callbackery.ui.form.Auto", { var tm = this._typeMap = {}; var that = this; var formKeyIdx = 0; - structure.forEach(function(s){ + structure.forEach(s => { var options = {}; // value binding in qooxdoo does not like keys // with strange characters ... (like -) @@ -72,7 +72,7 @@ qx.Class.define("callbackery.ui.form.Auto", { this._keyToFormKey[s.key] = formKey; this._formKeyToKey[formKey] = s.key; } - ['note','copyOnTap','copyFailMsg','copySuccessMsg'].forEach(function(prop){ + ['note','copyOnTap','copyFailMsg','copySuccessMsg'].forEach(prop => { if (s[prop]){ options[prop] = qx.lang.Type.isString(s[prop]) || qx.lang.Type.isArray(s[prop]) ? @@ -167,9 +167,9 @@ qx.Class.define("callbackery.ui.form.Auto", { case 'comboBox': control = new qx.ui.form.ComboBox(); var ctrl = this._boxCtrl[s.key] = new qx.data.controller.List(null, control); - cfg.structure.forEach(function(item){ + cfg.structure.forEach(item => { item = item != null ? this.xtr(item) : null; - },this); + }); var sbModel = qx.data.marshal.Json.createModel(cfg.structure || []); ctrl.setModel(sbModel); break; @@ -203,11 +203,11 @@ qx.Class.define("callbackery.ui.form.Auto", { if (s.set.filter){ s.set.filter = RegExp(s.filter); } - ['placeholder','tooltip','label'].forEach(function(key){ + ['placeholder','tooltip','label'].forEach(key => { if (key in s.set){ s.set[key] = this.xtr(s.set[key]); } - }, this); + }); control.set(s.set); } @@ -263,7 +263,7 @@ qx.Class.define("callbackery.ui.form.Auto", { } }); } - },this); + }); var model = this._model = formCtrl.createModel(true); @@ -364,11 +364,11 @@ qx.Class.define("callbackery.ui.form.Auto", { }]); } else { - data.forEach(function(item,i){ + data.forEach((item,i) => { item.title = item.title != null ? this.xtr(item.title) : null; - },this); + }); model = qx.data.marshal.Json.createModel(data); } let lookup = {}; From 017396923ff135f6cfbe45f084961cbd35138843 Mon Sep 17 00:00:00 2001 From: Fritz Zaucker Date: Wed, 19 Aug 2026 11:40:59 +0200 Subject: [PATCH 2/2] Add noBusyIndicator opt-out for download/display actions Download and display actions block the whole gui with the modal busy indicator ("Preparing Download ...") while the download is prepared. For actions whose data takes a long time to produce -- a report which runs latex, say -- the block is unwelcome: the user cannot do anything else in the meantime, and the popup the action lives in is typically kept open for a second run. Add an optional actionCfg property "noBusyIndicator". When set, neither the indicator nor its 3 second auto-vanish timer runs, and the vanish in the iframe load handler is skipped as well, so we never unblock a blocker somebody else put up. Absent the property nothing changes. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGES | 7 +++++++ lib/CallBackery/GuiPlugin/AbstractAction.pm | 13 ++++++++++++ .../class/callbackery/ui/plugin/Action.js | 20 +++++++++++++------ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 09f4cc20..9bde4829 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ + + - New optional actionCfg property "noBusyIndicator" for download and + display actions. When set, the modal busy indicator ("Preparing + Download ...") is not shown while the download is being prepared, so + the gui stays usable for actions which take a long time to produce + their data. Default behaviour is unchanged. + 0.58.4 2026-07-31 16:15:51 +0200 Tobias Oetiker - Frontend ui.Login: a login could be lost outright. The credentials diff --git a/lib/CallBackery/GuiPlugin/AbstractAction.pm b/lib/CallBackery/GuiPlugin/AbstractAction.pm index 753bc23e..5444ed22 100644 --- a/lib/CallBackery/GuiPlugin/AbstractAction.pm +++ b/lib/CallBackery/GuiPlugin/AbstractAction.pm @@ -93,6 +93,19 @@ has screenOpts => sub { Returns a list of action buttons to place at the top of the form. +For C and C actions the gui is blocked by a modal busy +indicator while the download is being prepared (for at most 3 seconds). +Actions which take a long time to produce their data can switch this off by +setting C to true, keeping the gui usable while they run: + + { + label => trm('Create Report'), + action => 'download', + key => 'createReport', + noBusyIndicator => true, + actionHandler => sub { ... }, + } + =cut has actionCfg => sub { diff --git a/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/plugin/Action.js b/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/plugin/Action.js index 769c35e8..06603d44 100644 --- a/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/plugin/Action.js +++ b/lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/ui/plugin/Action.js @@ -401,12 +401,18 @@ qx.Class.define("callbackery.ui.plugin.Action", { return; } var key = btCfg.key; - if (btCfg.busyMessage) { - busy.manifest(this.xtr(btCfg.busyMessage)); - } else { - busy.manifest(this.tr('Preparing Download ...')); + // actions which take a long time to produce their + // data can opt out of the modal busy indicator so + // that the gui stays usable while they run + let showBusy = !btCfg.noBusyIndicator; + if (showBusy) { + if (btCfg.busyMessage) { + busy.manifest(this.xtr(btCfg.busyMessage)); + } else { + busy.manifest(this.tr('Preparing Download ...')); + } + setTimeout(() => { busy.vanish(); }, 3 * 1000); // hide the activity indicator after 3 seconds anyway } - setTimeout(() => { busy.vanish(); }, 3 * 1000); // hide the activity indicator after 3 seconds anyway callbackery.data.Server.getInstance().callAsyncSmart(function (cookie) { let url = 'download' + '?name=' + cfg.name @@ -422,7 +428,9 @@ qx.Class.define("callbackery.ui.plugin.Action", { height: 100 }); iframe.addListener('load', function (e) { - busy.vanish(); + if (showBusy) { + busy.vanish(); + } var response = { exception: { message: String(that.tr("No Data")),