From 2062c8874720ce47183ca6d99422fafa80daf880 Mon Sep 17 00:00:00 2001 From: "Richard.Fang" Date: Thu, 6 Mar 2025 10:29:48 +0800 Subject: [PATCH] =?UTF-8?q?REPORT-148266=20feat:comboBox=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=8F=A0=E5=8A=A0=E5=AF=BC=E8=87=B4=E9=83=A8=E5=88=86=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=8D=A1=E9=A1=BF=E9=97=AE=E9=A2=98=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/data/DesignTableDataManager.java | 39 ++-- .../data/datapane/TableDataComboBox.java | 171 ++++++++++++++---- .../data/datapane/TreeTableDataDictPane.java | 3 +- .../editor/editor/ColumnSelectedEditor.java | 2 +- .../present/dict/TableDataDictPane.java | 2 +- .../java/com/fr/design/ui/util/UIUtil.java | 54 +++++- .../fr/design/chart/AutoChartTypePane.java | 2 +- .../chartx/component/MapAreaMatchPane.java | 2 +- .../design/mainframe/ChartPropertyPane.java | 5 +- .../chart/gui/data/DatabaseTableDataPane.java | 2 +- .../dscolumn/SelectedDataColumnPane.java | 2 +- 11 files changed, 222 insertions(+), 62 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java index 2354007e18..2afcaabe4d 100644 --- a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java +++ b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java @@ -115,6 +115,9 @@ public abstract class DesignTableDataManager { //增强for循环用的iterator实现的, 如果中间哪个listener修改或删除了(如ChartEditPane.dsChangeListener), // 由于dsListeners是arraylist, 此时会ConcurrentModifyException ChangeEvent e = null; + if (dsListeners.get(i) == null) { + continue; + } dsListeners.get(i).stateChanged(e); } } @@ -185,23 +188,33 @@ public abstract class DesignTableDataManager { globalDsListeners.add(l); } +/** + * 添加模板数据集改变 监听事件. + * + * @param l ChangeListener监听器 + */ +public static void addDsChangeListener(ChangeListener l) { + getDsListenersForCurrentTemplate().add(l); +} + +/** + * 移除模板数据集改变 监听事件. + * + * @param l ChangeListener监听器 + */ +public static void removeDsChangeListener(ChangeListener l) { + getDsListenersForCurrentTemplate().remove(l); +} + /** - * 添加模板数据集改变 监听事件. + * 获取当前模板的监听器列表. * - * @param l ChangeListener监听器 + * @return 模板对应的监听器列表,如果列表不存在则新建. */ - public static void addDsChangeListener(ChangeListener l) { + private static List getDsListenersForCurrentTemplate() { JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - String key = StringUtils.EMPTY; - if (JTemplate.isValid(template)) { - key = template.getPath(); - } - List dsListeners = dsListenersMap.get(key); - if (dsListeners == null) { - dsListeners = new ArrayList(); - dsListenersMap.put(key, dsListeners); - } - dsListeners.add(l); + String key = JTemplate.isValid(template) ? template.getPath() : StringUtils.EMPTY; + return dsListenersMap.computeIfAbsent(key, k -> new ArrayList<>()); } /** diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java index ebbdbf0f35..e177837470 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java @@ -1,13 +1,14 @@ package com.fr.design.data.datapane; +import com.fr.design.ui.util.UIUtil; import java.awt.Component; import java.awt.event.ItemEvent; -import java.util.Iterator; -import java.util.Map.Entry; import javax.swing.DefaultComboBoxModel; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -32,16 +33,53 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC protected java.util.Map resMap; private java.util.Map dsMap; private static final long serialVersionUID = 1L; - private boolean refresModel = false; - private String treeName; //树数据集本身的名字 + private boolean refreshModel = false; + private String treeName = StringUtils.EMPTY; //树数据集本身的名字 private ChangeListener changeListener; + /** + * 兼容插件调用 + * + * @param source 传入的数据源 + */ public TableDataComboBox(TableDataSource source){ - this(source,StringUtils.EMPTY); + this(); } + + /** + * 兼容插件调用 + * + * @param source 传入的数据源 + * @param treeName 树数据集名称 + */ public TableDataComboBox(TableDataSource source, String treeName) { + this(treeName); + } + + /** + * 根据树名称创建TableDataComboBox。 + * + * @param treeName 树数据集名称 + */ + public TableDataComboBox(String treeName) { + this(); + // 传入树数据集名称 + this.treeName = treeName; + } + + /** + * 初始化TableDataComboBox + */ + public TableDataComboBox() { super(); - this.treeName = treeName; + setListCellRenderer(); + addComboBoxListener(); + } + + /** + * 设置渲染器 + */ + private void setListCellRenderer() { this.setRenderer(new UIComboBoxRenderer() { private static final long serialVersionUID = 1L; @@ -60,37 +98,66 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC return renderer; } }); - refresh(source); - registerDSChangeListener(); } - /** - * refresh ComboBox - * @param source - */ + /** + * 在comboBox可见时添加数据集响应事件与refresh操作 + */ + private void addComboBoxListener() { + this.addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) { + registerDSChangeListener(); + refresh(DesignTableDataManager.getEditingTableDataSource()); + } + + @Override + public void ancestorRemoved(AncestorEvent event) { + DesignTableDataManager.removeDsChangeListener(changeListener); + } + + @Override + public void ancestorMoved(AncestorEvent event) { + } + }); + } + + /** + * 刷新数据源并更新下拉框的模型和选中项 + * + * @param source 数据源,用于刷新模型 + */ public void refresh(TableDataSource source) { - TableDataWrapper dataWrapper = getSelectedItem(); - refresModel = true; - setResMap(source); - setDsMap(); - DefaultComboBoxModel model = new DefaultComboBoxModel(); - this.setModel(model); - model.addElement(UIConstants.PENDING); - Iterator> entryIt = dsMap.entrySet().iterator(); - while (entryIt.hasNext()) { - TableDataWrapper tableDataWrapper = entryIt.next().getValue(); - if (!ComparatorUtils.equals(tableDataWrapper.getTableDataName(), treeName)) { - model.addElement(tableDataWrapper); - } - } - if (dataWrapper != null) { - if (DesignTableDataManager.isDsNameChanged(dataWrapper.getTableDataName())) { - this.setSelectedTableDataByName(DesignTableDataManager.getChangedDsNameByOldDsName(dataWrapper.getTableDataName())); - } else { - this.getModel().setSelectedItem(dataWrapper); - } - } - refresModel = false; + UIUtil.executeAsyncTaskAndUpdateUI( + () -> { + setResMap(source); + setDsMap(); + return null; + }, + result -> refreshComboBoxModel() + ); + } + + /** + * 刷新下拉框模型,同时保留当前选中的数据项 + *

+ * 1. 获取下拉框中当前选中的数据项 + * 2. 刷新下拉框的模型(清空并重新填充数据),此操作会重置选中的数据项 + * 3. 在刷新模型后,恢复之前选中的数据项 + *

+ * 关于 `refreshModel` 的作用: + * 1. **抑制事件触发**:下拉框模型在调用 `addElement` 方法时会触发 `fireItemStateChanged` 事件, + * 通过标记 `refreshModel`,可以在刷新过程中抑制此事件 + * 2. **处理异步和顺序问题**:由于取数操作是异步的,可能会导致回调后的 UI 操作与其他逻辑(如 `populateBean`)的调用顺序交错。 + * 标记 `refreshModel` 可确保在刷新模型时,不触发选中事件,从而避免逻辑干扰 + * 3. **逻辑清晰性**:刷新模型本质上是更新数据源的操作,不应触发与用户交互相关的选中事件,避免对上层逻辑造成额外负担 + */ + private void refreshComboBoxModel() { + refreshModel = true; + TableDataWrapper selectedItem = getSelectedItem(); + refreshModel(); + updateSelectedItem(selectedItem); + refreshModel = false; } protected void setResMap(TableDataSource source) { @@ -101,6 +168,26 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC dsMap = DesignTableDataManager.getAllDataSetIncludingProcedure(resMap); } + private void refreshModel() { + //创建ComboBox模型并设置 + DefaultComboBoxModel model = new DefaultComboBoxModel(); + this.setModel(model); + model.addElement(UIConstants.PENDING); + // 遍历添加所有数据项到模型,树数据集comboBox下拉模型中排除掉本身 + dsMap.values().stream() + .filter(tableDataWrapper -> tableDataWrapper != null && !ComparatorUtils.equals(tableDataWrapper.getTableDataName(), treeName)) + .forEach(model::addElement); + } + + private void updateSelectedItem(TableDataWrapper dataWrapper) { + if (dataWrapper != null) { + if (DesignTableDataManager.isDsNameChanged(dataWrapper.getTableDataName())) { + this.setSelectedTableData(DesignTableDataManager.getChangedDsNameByOldDsName(dataWrapper.getTableDataName())); + } else { + this.getModel().setSelectedItem(dataWrapper); + } + } + } /** @@ -117,8 +204,18 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC } public void setSelectedTableDataByName(String name) { - TableDataWrapper tableDataWrappe = dsMap.get(name) == null? dsMap.get(name + "_P_CURSOR") : dsMap.get(name); - this.getModel().setSelectedItem(tableDataWrappe); + setResMap(DesignTableDataManager.getEditingTableDataSource()); + setDsMap(); + // 数据集名称修改后控件传入的还是旧名称 + if (DesignTableDataManager.isDsNameChanged(name)) { + name = DesignTableDataManager.getChangedDsNameByOldDsName(name); + } + setSelectedTableData(name); + } + + private void setSelectedTableData(String name) { + TableDataWrapper tableDataWrapper = dsMap.get(name) == null ? dsMap.get(name + "_P_CURSOR") : dsMap.get(name); + this.getModel().setSelectedItem(tableDataWrapper); } @Override @@ -132,7 +229,7 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC //august:addElement方法竟然会fireItemStateChanged,蛋疼 @Override protected void fireItemStateChanged(ItemEvent e) { - if (!refresModel) { + if (!refreshModel) { super.fireItemStateChanged(e); } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java index 175ddbfda3..fcb8705e9a 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java @@ -167,8 +167,7 @@ public class TreeTableDataDictPane extends BasicPane implements Previewable { } protected void setTableDataNameComboBox(String treeName) { - tableDataNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource(), treeName); - + tableDataNameComboBox = new TableDataComboBox(treeName); } private void tdChange(boolean isChangeDS) { diff --git a/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java b/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java index 5260ce1752..29d6387f5d 100644 --- a/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java +++ b/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java @@ -31,7 +31,7 @@ public class ColumnSelectedEditor extends Editor implements Prep public ColumnSelectedEditor() { this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_DS_Column")); this.setLayout(FRGUIPaneFactory.createLeftZeroLayout()); - tableDataComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableDataComboBox = new TableDataComboBox(); columnNames = new String[0]; tableDataComboBox.addItemListener(new ItemListener() { diff --git a/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java b/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java index 5ebd426593..f068f71216 100644 --- a/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java +++ b/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java @@ -83,7 +83,7 @@ public class TableDataDictPane extends FurtherBasicBeanPane } private void initBasicComponets() { - tableDataNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableDataNameComboBox = new TableDataComboBox(); tableDataNameComboBox.addItemListener(e -> { if (e.getStateChange() == ItemEvent.SELECTED) { tdChange(e); diff --git a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java index 86093a6d82..8565e90f31 100644 --- a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java +++ b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java @@ -3,9 +3,12 @@ package com.fr.design.ui.util; import com.fr.log.FineLoggerFactory; import com.fr.third.guava.base.Stopwatch; import com.fr.third.guava.base.Supplier; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import javax.swing.SwingWorker; +import javax.swing.SwingUtilities; import org.jetbrains.annotations.NotNull; -import javax.swing.SwingUtilities; import java.util.concurrent.TimeUnit; /** @@ -81,4 +84,53 @@ public class UIUtil { } return result; } + + /** + * 执行异步任务并在任务完成后更新UI。 + *

+ * 该方法将执行一个耗时的后台任务,并在任务完成后将结果传递给一个回调函数来更新UI。 + * 使用 SwingWorker 来处理后台任务,确保在任务完成后回到 EDT(事件分发线程) 更新 UI。 + * 提供了一个可选的 `finallyBlock` 参数,用于执行清理操作,例如释放资源或重置状态。 + * + * @param 任务结果的类型 + * @param task 需要在后台执行的任务。该任务的执行过程由 Supplier 提供,返回任务的结果。 + * @param uiUpdater 在任务完成后执行的回调函数,用来处理结果并更新UI。 + * @param finallyBlock 可选的清理操作,在任务结束后无论是否发生异常都会执行。可以传入 null 表示不需要清理操作。常见场景包括状态标志的重置或资源释放。 + * + */ + public static void executeAsyncTaskAndUpdateUI(Supplier task, Consumer uiUpdater, Runnable finallyBlock) { + new SwingWorker() { + @Override + protected T doInBackground() throws Exception { + return task.get(); + } + @Override + protected void done() { + try { + T result = get(); + uiUpdater.accept(result); + } catch (InterruptedException | ExecutionException e) { + FineLoggerFactory.getLogger().debug(e.getMessage(), e); + } finally { + if (finallyBlock != null) { + finallyBlock.run(); + } + } + } + }.execute(); + } + + /** + * 执行异步任务并在任务完成后更新UI。 + *

+ * 该方法将执行一个耗时的后台任务,并在任务完成后将结果传递给一个回调函数来更新UI。 + * 使用 SwingWorker 来处理后台任务,确保在任务完成后回到 EDT(事件分发线程) 更新 UI。 + * + * @param 任务结果的类型 + * @param task 需要在后台执行的任务。该任务的执行过程由 Supplier 提供,返回任务的结果。 + * @param uiUpdater 在任务完成后执行的回调函数,用来处理结果并更新UI。 + */ + public static void executeAsyncTaskAndUpdateUI(Supplier task, Consumer uiUpdater) { + executeAsyncTaskAndUpdateUI(task, uiUpdater, null); + } } diff --git a/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java b/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java index 589028ae52..4aaf6a0281 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java +++ b/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java @@ -141,7 +141,7 @@ public class AutoChartTypePane extends ChartWizardPane implements CallbackEvent } private void initDataFiledBox() { - tableNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableNameComboBox = new TableDataComboBox(); tableNameComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { diff --git a/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java b/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java index 58068d10a8..ba25b5b81c 100644 --- a/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java +++ b/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java @@ -128,7 +128,7 @@ public class MapAreaMatchPane extends BasicBeanPane { } private void initButtonGroup() { - tableNameCombox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableNameCombox = new TableDataComboBox(); tableNameCombox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { diff --git a/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java b/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java index 297245960d..fe46528ca5 100644 --- a/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java +++ b/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java @@ -9,18 +9,17 @@ import com.fr.base.chart.BaseChartCollection; import com.fr.chart.chartattr.ChartCollection; import com.fr.chart.charttypes.ChartTypeManager; import com.fr.chartx.attr.ChartProvider; -import com.fr.decision.webservice.v10.map.geojson.helper.GEOJSONHelper; import com.fr.design.ChartTypeInterfaceManager; import com.fr.design.designer.TargetComponent; import com.fr.design.gui.chart.BaseChartPropertyPane; import com.fr.design.gui.chart.ChartEditPaneProvider; import com.fr.design.gui.frpane.UITitlePanel; import com.fr.design.mainframe.chart.ChartEditPane; +import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.gui.GUICoreUtils; import javax.swing.BorderFactory; import javax.swing.Icon; -import javax.swing.SwingWorker; import java.awt.BorderLayout; import java.awt.Component; @@ -116,7 +115,7 @@ public class ChartPropertyPane extends BaseChartPropertyPane { */ public void populateChartPropertyPane(BaseChartCollection collection, TargetComponent ePane) { if (collection instanceof ChartCollection) { - populateChartPropertyPane((ChartCollection) collection, ePane); + UIUtil.invokeAndWaitIfNeeded(() -> populateChartPropertyPane((ChartCollection) collection, ePane)); } } diff --git a/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java b/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java index 40983c56b5..3fbf1aa5a4 100644 --- a/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java +++ b/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java @@ -82,7 +82,7 @@ public class DatabaseTableDataPane extends BasicPane{ } private void initTableCombox() { - tableNameCombox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()){ + tableNameCombox = new TableDataComboBox(){ //图表的数据集选择下拉框,不需要注册监听,chartEditPane已经注册了。 public void registerDSChangeListener() { diff --git a/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java b/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java index 052fba9046..de10613835 100644 --- a/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java +++ b/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java @@ -330,7 +330,7 @@ public class SelectedDataColumnPane extends BasicPane { protected void initTableNameComboBox() { - tableNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableNameComboBox = new TableDataComboBox(); tableNameComboBox.setPreferredSize(new Dimension(100, 20)); }