Browse Source

Merging in latest from upstream (DESIGN/design:refs/heads/fbp/feature)

* commit '3cf8cdbfbf6e6dc29fea75fc96030b3c3f11e02c':
  REPORT-136866 feat:NewUI性能监控工具
  REPORT-139356 插件数据源注册添加回来
  REPORT-139356 插件数据源注册添加回来
  REPORT-139789 fix: FBP SystemOptionProvider 放到 extra-decision
  REPORT-128071 插件引擎适配微服务-适配支持特定服务
  REPORT-137630 feat: 东区图表Tab切换性能优化
fbp/feature
Richard.Fang-方超 1 week ago
parent
commit
c0ce47fbb3
  1. 14
      designer-base/src/main/java/com/fine/theme/utils/FineUIUtils.java
  2. 21
      designer-base/src/main/java/com/fr/design/carton/CartonUtils.java
  3. 68
      designer-base/src/main/java/com/fr/design/carton/DispatchInfo.java
  4. 193
      designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java
  5. 8
      designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java
  6. 33
      designer-base/src/main/java/com/fr/design/carton/latency/AbstractUIDispatchHandler.java
  7. 104
      designer-base/src/main/java/com/fr/design/carton/latency/UIDispatchManager.java
  8. 2
      designer-base/src/main/java/com/fr/design/debug/remote/HeaderRenderer.java
  9. 14
      designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkAction.java
  10. 2
      designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkHelper.java
  11. 8
      designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkTablePane.java
  12. 2
      designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkTableRowSorter.java
  13. 4
      designer-base/src/main/java/com/fr/design/debug/remote/SizeColorCellRenderer.java
  14. 4
      designer-base/src/main/java/com/fr/design/debug/remote/TimeColorCellRenderer.java
  15. 48
      designer-base/src/main/java/com/fr/design/debug/ui/LatencyInfo.java
  16. 17
      designer-base/src/main/java/com/fr/design/debug/ui/LatencyMonitorEvent.java
  17. 56
      designer-base/src/main/java/com/fr/design/debug/ui/UIInspectorHolder.java
  18. 46
      designer-base/src/main/java/com/fr/design/debug/ui/UILatencyInfoHandler.java
  19. 124
      designer-base/src/main/java/com/fr/design/debug/ui/UILatencyWorker.java
  20. 40
      designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorAction.java
  21. 44
      designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorHelper.java
  22. 274
      designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorPane.java
  23. 20
      designer-base/src/main/java/com/fr/design/mainframe/toolbar/DebugModeMenuDef.java
  24. 6
      designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/ChartOtherPane.java
  25. 15
      designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/ChartStylePane.java
  26. 42
      designer-realize/src/main/java/com/fanruan/boot/adaptation/ReportAdaptationComponent.java
  27. 5
      designer-realize/src/main/java/com/fanruan/boot/env/DesignPluginComponent.java
  28. 31
      designer-realize/src/main/java/com/fanruan/boot/env/DesignVersionComponent.java
  29. 12
      designer-realize/src/main/java/com/fr/start/CarinaDesigner.java

14
designer-base/src/main/java/com/fine/theme/utils/FineUIUtils.java

@ -6,6 +6,8 @@ import com.fr.design.border.FineBorderFactory;
import com.fr.design.constants.LayoutConstants;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrame;
import com.fr.design.mainframe.theme.edit.ui.LabelUtils;
import com.fr.design.i18n.DesignSizeI18nManager;
import com.fr.stable.os.OperatingSystem;
@ -503,4 +505,16 @@ public class FineUIUtils {
public static JTextArea createAutoWrapTipLabel(String text) {
return LabelUtils.createAutoWrapLabel(text, FineUIUtils.getUIColor("Label.tipColor", "inactiveCaption"));
}
/**
* 基于设计器父面板计算当前面板尺寸
* @param width 宽度比例
* @param height 高度比例
*
* @return 面板尺寸
*/
public static Dimension calPaneDimensionByContext(double width, double height) {
DesignerFrame parent = DesignerContext.getDesignerFrame();
return new Dimension((int) (parent.getWidth() * width),(int) (parent.getHeight() * height));
}
}

21
designer-base/src/main/java/com/fr/design/carton/CartonUtils.java

@ -1,5 +1,6 @@
package com.fr.design.carton;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils;
@ -15,9 +16,11 @@ import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.text.SimpleDateFormat;
import static com.fr.design.carton.CartonConstants.DATE_FORMAT;
import static com.fr.design.carton.CartonConstants.EASY_CHECKER_FILE_NAME;
import static com.fr.design.carton.CartonConstants.JOURNAL_FILE_PATH;
import static com.fr.design.carton.CartonConstants.TIMER_CHECKER_FILE_NAME;
import static com.fr.design.carton.CartonConstants.UNREASONABLE_DISPATCH_DURATION_MS;
/**
* 设计器卡顿业务工具类
@ -125,6 +128,24 @@ public class CartonUtils {
}
}
/**
* 记录event事件信息
* @param info event事件信息
*/
public static void recordDispatchInfo(DispatchInfo info) {
long cost = info.timeSoFar();
JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + info.getEventSeq());
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), DATE_FORMAT.format(info.getStartDispatchTimeMillis()));
if (cost > UNREASONABLE_DISPATCH_DURATION_MS) {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), cost + "ms");
} else {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), info.totalTime() + "ms");
}
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG);
}
/**
* 用于判断是不是特定的堆栈
*/

68
designer-base/src/main/java/com/fr/design/carton/DispatchInfo.java

@ -0,0 +1,68 @@
package com.fr.design.carton;
/**
* SwingEvent事件包装类
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/05
*/
public class DispatchInfo {
/**
* 当前线程
*/
private final Thread eventDispatchThread = Thread.currentThread();
/**
* 上次触发时间
*/
private long lastDispatchTimeMillis = System.currentTimeMillis();
/**
* 开始时间
*/
private final long startDispatchTimeMillis = System.currentTimeMillis();
/**
* 事件唯一编号
*/
private final long eventSeq;
public DispatchInfo() {
eventSeq = EventDispatchThreadHangMonitor.incrementAndGetSeq();
}
public Thread getEventDispatchThread() {
return eventDispatchThread;
}
public long getLastDispatchTimeMillis() {
return lastDispatchTimeMillis;
}
public long getStartDispatchTimeMillis() {
return startDispatchTimeMillis;
}
public void setLastDispatchTimeMillis(long lastDispatchTimeMillis) {
this.lastDispatchTimeMillis = lastDispatchTimeMillis;
}
public long getEventSeq() {
return eventSeq;
}
/**
* event事件已运行时间
*/
public long timeSoFar() {
return (System.currentTimeMillis() - lastDispatchTimeMillis);
}
/**
* event事件总运行时间
*/
public long totalTime() {
return (System.currentTimeMillis() - startDispatchTimeMillis);
}
}

193
designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java

@ -1,16 +1,8 @@
package com.fr.design.carton;
import com.fanruan.product.ProductConstantsBase;
import com.fr.concurrent.FineExecutors;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.ui.util.UIUtil;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import org.jetbrains.annotations.NotNull;
import javax.swing.SwingUtilities;
import java.awt.EventQueue;
@ -18,15 +10,10 @@ import java.awt.Toolkit;
import java.awt.AWTEvent;
import java.awt.event.WindowEvent;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.fr.design.carton.CartonConstants.CHECK_INTERVAL_MS;
import static com.fr.design.carton.CartonConstants.DATE_FORMAT;
import static com.fr.design.carton.CartonConstants.LATENCY_SAMPLING_FREQUENCY;
import static com.fr.design.carton.CartonConstants.UNREASONABLE_DISPATCH_DURATION_MS;
/**
* 参考自git swinghelper
@ -39,14 +26,11 @@ import static com.fr.design.carton.CartonConstants.UNREASONABLE_DISPATCH_DURATIO
public final class EventDispatchThreadHangMonitor extends EventQueue {
public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor();
/**
* 一个timer
*/
private Timer timer;
/**
* 事件唯一编码用于方便日志的查看
*/
private static long hangCount = 0;
private static long eventSequence = 0;
/**
* 类似于一个开关当该值为默认的false启动时定时任务在窗口开启前都不会对执行的事件进行检查
*/
@ -54,7 +38,7 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
/**
* 该链表为主要的实现定时任务的容器在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表由post方法remove
*/
private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>();
private final LinkedList<DispatchInfo> dispatches = new LinkedList<>();
/**
* 一个变量用于控制easy监测模式的开关
*/
@ -72,7 +56,6 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
/**
* 一个变量用于记录Timer的开关
*/
public boolean isTimerWitch() {
return timerWitch;
}
@ -83,96 +66,20 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
private boolean timerWitch = false;
private synchronized static long getHangCount() {
return hangCount++;
}
/**
* event事件的包装类
* 获取Swing事件唯一编号
*
* @return 事件编号
*/
public static class DispatchInfo {
// 上一次被打印的堆栈ou
private StackTraceElement[] lastReportedStack;
//获取执行该事件的线程
private final Thread eventDispatchThread = Thread.currentThread();
//在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间
private long lastDispatchTimeMillis = System.currentTimeMillis();
//事件开始的时间
private final long startDispatchTimeMillis = System.currentTimeMillis();
//事件编号
private final long hangNumber;
//构造函数,给当前对象赋一个递增的唯一编号
public DispatchInfo() {
hangNumber = getHangCount();
}
//定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法
public void checkForHang() {
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
examineHang();
}
}
//超时堆栈的具体处理
private void examineHang() {
//获取执行线程的当前堆栈
StackTraceElement[] currentStack = eventDispatchThread.getStackTrace();
if (CartonUtils.isWaitingForNextEvent(currentStack)) {
return;
}
//某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次
if (lastReportedStack != null && CartonUtils.stacksEqual(lastReportedStack, currentStack)) {
return;
}
String stackTrace = CartonUtils.stackTraceToString(currentStack);
lastReportedStack = currentStack;
JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber);
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms");
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace);
CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG);
CartonUtils.checkForDeadlock();
}
//记录连续运行了多长时间
public long timeSoFar() {
return (System.currentTimeMillis() - lastDispatchTimeMillis);
}
//记录一个事件从被分发到结束的总运行时间
public long totalTime() {
return (System.currentTimeMillis() - startDispatchTimeMillis);
}
//事件处理完后的时间判断
public void dispose() {
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
exportCartonLog(true);
} else if (lastReportedStack != null) {
exportCartonLog(false);
}
}
public synchronized static long incrementAndGetSeq() {
return eventSequence++;
}
/**
*
* @param flag 判断一下输出日志时要输出哪个时间
*/
private void exportCartonLog(boolean flag) {
JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber);
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), DATE_FORMAT.format(startDispatchTimeMillis));
if (flag) {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms");
} else {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms");
}
CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG);
}
public LinkedList<DispatchInfo> getDispatches() {
return dispatches;
}
private EventDispatchThreadHangMonitor() {
}
/**
@ -183,17 +90,9 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
*/
public void startFilterModalWindow() {
scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//不用干事,切个片就可以
}
});
}
}, 0, 500, TimeUnit.MILLISECONDS);
scheduledExecutorService.scheduleAtFixedRate(() -> SwingUtilities.invokeLater(() -> {
// 切片即可
}), 0, 500, TimeUnit.MILLISECONDS);
}
public void stopFilterModalWindow() {
@ -201,54 +100,11 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
scheduledExecutorService.shutdown();
}
}
/**
* Sets up a timer to check for hangs frequently.
* 初始化一个Timer
*/
public void initTimer() {
final long initialDelayMs = 0;
final boolean daemon = true;
timer = new Timer("EventDispatchThreadHangMonitor", daemon);
timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS);
}
/**
* /消除Timer
*/
public void stopTimer() {
if (timer != null) {
timer.cancel();
}
}
/**
* /定时执行的任务
*/
public class HangChecker extends TimerTask {
@Override
public void run() {
synchronized (dispatches) {
//如果链表为空或者窗口还没启开,定时检测就不进行
if (dispatches.isEmpty() || !haveShownSomeComponent) {
return;
}
dispatches.getLast().checkForHang();
}
}
}
/**
* 将swing中默认的EventQueue换成自己的
*/
public static void initMonitoring() {
UIUtil.invokeLaterIfNeeded(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE));
}
/**
* Overrides EventQueue.dispatchEvent to call our pre and post hooks either
* side of the system's event dispatch code.
* 重写
*/
@Override
protected void dispatchEvent(AWTEvent event) {
if (!useCustomEventQueue()) {
@ -267,16 +123,23 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
}
}
/**
* EDT监控器是否已就绪
*
* @return EDT监控器初始化完成
*/
public boolean ready() {
return !dispatches.isEmpty() && haveShownSomeComponent;
}
private boolean useCustomEventQueue() {
// 开启性能监控或开启卡顿工具箱,则走自定义的EventQueue
return SwitchForSwingChecker.isLatencyMonitoring() ||
isEasyWitch() || isTimerWitch();
return SwitchForSwingChecker.isLatencyMonitoring() || isEasyWitch() || isTimerWitch();
}
private boolean needSampling() {
// UI性能采样逻辑:开启采样并且符合采样频次
return SwitchForSwingChecker.isLatencyMonitoring()
&& (hangCount % LATENCY_SAMPLING_FREQUENCY == 0);
return SwitchForSwingChecker.isLatencyMonitoring() && (eventSequence % LATENCY_SAMPLING_FREQUENCY == 0);
}
/**
@ -298,13 +161,13 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
DesignerLatencyMetric.getInstance().record(justFinishedDispatch.timeSoFar());
}
if (isEasyWitch()) {
justFinishedDispatch.dispose();
CartonUtils.recordDispatchInfo(justFinishedDispatch);
}
//嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis
Thread currentEventDispatchThread = Thread.currentThread();
for (DispatchInfo dispatchInfo : dispatches) {
if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) {
dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis();
if (dispatchInfo.getEventDispatchThread() == currentEventDispatchThread) {
dispatchInfo.setLastDispatchTimeMillis(System.currentTimeMillis());
}
}
}

8
designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java

@ -1,7 +1,7 @@
package com.fr.design.carton;
import com.fanruan.product.ProductConstantsBase;
import com.fr.design.carton.latency.UIDispatchManager;
import com.fr.design.i18n.Toolkit;
import com.fr.general.GeneralUtils;
import com.fr.json.JSON;
@ -82,7 +82,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
public static void startTimerChecker() {
if (!checkerTimerSwitch) {
EventDispatchThreadHangMonitor.INSTANCE.initTimer();
UIDispatchManager.getInstance().startScheduler();
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer();
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true);
checkerTimerSwitch = true;
@ -94,7 +94,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
public static void stopTimerChecker() {
if (checkerTimerSwitch) {
EventDispatchThreadHangMonitor.INSTANCE.stopTimer();
UIDispatchManager.getInstance().stopSchedulerIfNecessary();
CartonThreadExecutorPool.getTimerThreadExecutorPool().stopTimer();
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(false);
checkerTimerSwitch = false;
@ -286,7 +286,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter {
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true);
}
if (checkerTimerSwitch) {
EventDispatchThreadHangMonitor.INSTANCE.initTimer();
UIDispatchManager.getInstance().startScheduler();
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer();
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true);
}

33
designer-base/src/main/java/com/fr/design/carton/latency/AbstractUIDispatchHandler.java

@ -0,0 +1,33 @@
package com.fr.design.carton.latency;
import com.fr.design.carton.DispatchInfo;
/**
* 设计器UI事件切面处理器
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/07
*/
public abstract class AbstractUIDispatchHandler {
/**
* 是否需要处理
*/
protected abstract boolean accept(DispatchInfo info);
/**
* 实际处理
*/
protected abstract void doHandle(DispatchInfo info);
/**
* 处理UI切面
*/
public void handle(DispatchInfo info) {
if (accept(info)) {
doHandle(info);
}
}
}

104
designer-base/src/main/java/com/fr/design/carton/latency/UIDispatchManager.java

@ -0,0 +1,104 @@
package com.fr.design.carton.latency;
import com.fr.design.carton.DispatchInfo;
import com.fr.design.carton.EventDispatchThreadHangMonitor;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.fr.design.carton.CartonConstants.CHECK_INTERVAL_MS;
/**
* 设计器UI事件切面处理器
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/05
*/
public class UIDispatchManager {
private ScheduledExecutorService scheduler;
private final List<AbstractUIDispatchHandler> handlerList = new ArrayList<>();
private final static class InstanceHolder {
static final UIDispatchManager INSTANCE = new UIDispatchManager();
}
/**
* 单例
*/
public static UIDispatchManager getInstance() {
return UIDispatchManager.InstanceHolder.INSTANCE;
}
/**
* 注册处理器
* @param handler 处理器
*/
public void registerHandler(AbstractUIDispatchHandler handler) {
handlerList.add(handler);
}
/**
* 注销处理器
* @param handler 处理器
*/
public void unregisterHandler(AbstractUIDispatchHandler handler) {
handlerList.remove(handler);
}
/**
* 开启定时UI事件监听任务
*/
public void startScheduler() {
if (scheduler != null && !scheduler.isShutdown()) {
return;
}
scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
Thread thread = new Thread(r, "DesignerLatencyChecker");
thread.setDaemon(true);
return thread;
});
scheduler.scheduleAtFixedRate(new DispatchChecker(), 0, CHECK_INTERVAL_MS, TimeUnit.MILLISECONDS);
}
/**
* 尝试停止定时UI事件监听任务但如果存在处理器则不停止
*/
public void stopSchedulerIfNecessary() {
if (!handlerList.isEmpty()) {
return;
}
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdownNow();
}
}
/**
* UI事件切面检查器
*/
public class DispatchChecker implements Runnable {
@Override
public void run() {
LinkedList<DispatchInfo> dispatches = EventDispatchThreadHangMonitor.INSTANCE.getDispatches();
synchronized (dispatches) {
if (EventDispatchThreadHangMonitor.INSTANCE.ready()) {
handle(dispatches.getLast());
}
}
}
}
/**
* 处理UI事件
* @param dispatchInfo ui事件信息
*/
public void handle(DispatchInfo dispatchInfo) {
handlerList.forEach(handler -> handler.handle(dispatchInfo));
}
}

2
designer-base/src/main/java/com/fr/design/remote/ui/debug/HeaderRenderer.java → designer-base/src/main/java/com/fr/design/debug/remote/HeaderRenderer.java

@ -1,4 +1,4 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import com.fine.theme.icon.LazyIcon;
import com.fine.theme.light.ui.FineTableHeaderUI;

14
designer-base/src/main/java/com/fr/design/remote/ui/debug/RemoteDesignNetWorkAction.java → designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkAction.java

@ -1,22 +1,19 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import com.fine.theme.utils.FineUIUtils;
import com.fr.design.actions.UpdateAction;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrame;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.workspace.WorkContext;
import javax.swing.JDialog;
import javax.swing.KeyStroke;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static com.fine.theme.utils.FineUIScale.createScaleDimension;
import static com.fine.theme.utils.FineUIScale.unscale;
import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER;
/**
@ -42,7 +39,7 @@ public class RemoteDesignNetWorkAction extends UpdateAction {
return;
}
JDialog jDialog = new JDialog(DesignerContext.getDesignerFrame(), TITLE);
jDialog.setSize(calculatePaneDimension());
jDialog.setSize(FineUIUtils.calPaneDimensionByContext(0.8, 0.6));
RemoteDesignNetWorkTablePane netWorkPane = new RemoteDesignNetWorkTablePane();
jDialog.add(netWorkPane);
jDialog.addWindowListener(new WindowAdapter() {
@ -56,10 +53,5 @@ public class RemoteDesignNetWorkAction extends UpdateAction {
jDialog.setVisible(true);
}
private static Dimension calculatePaneDimension() {
DesignerFrame parent = DesignerContext.getDesignerFrame();
return createScaleDimension((int) (unscale(parent.getWidth()) * 0.8),
(int) (unscale(parent.getHeight()) * 0.6));
}
}

2
designer-base/src/main/java/com/fr/design/remote/ui/debug/RemoteDesignNetWorkHelper.java → designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkHelper.java

@ -1,4 +1,4 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import com.fr.stable.StringUtils;

8
designer-base/src/main/java/com/fr/design/remote/ui/debug/RemoteDesignNetWorkTablePane.java → designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkTablePane.java

@ -1,4 +1,4 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import com.fanruan.workplace.http.debug.RequestInfo;
import com.fine.theme.icon.LazyIcon;
@ -24,9 +24,9 @@ import java.awt.Component;
import java.util.concurrent.atomic.AtomicLong;
import static com.fanruan.workplace.http.debug.RemoteDesignDebugEvent.REMOTE_HTTP_REQUEST;
import static com.fr.design.remote.ui.debug.RemoteDesignNetWorkHelper.dateFormat;
import static com.fr.design.remote.ui.debug.RemoteDesignNetWorkHelper.simpleSize;
import static com.fr.design.remote.ui.debug.RemoteDesignNetWorkHelper.simpleTime;
import static com.fr.design.debug.remote.RemoteDesignNetWorkHelper.dateFormat;
import static com.fr.design.debug.remote.RemoteDesignNetWorkHelper.simpleSize;
import static com.fr.design.debug.remote.RemoteDesignNetWorkHelper.simpleTime;
/**
* 远程设计网络调试面板

2
designer-base/src/main/java/com/fr/design/remote/ui/debug/RemoteDesignNetWorkTableRowSorter.java → designer-base/src/main/java/com/fr/design/debug/remote/RemoteDesignNetWorkTableRowSorter.java

@ -1,4 +1,4 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;

4
designer-base/src/main/java/com/fr/design/remote/ui/debug/SizeColorCellRenderer.java → designer-base/src/main/java/com/fr/design/debug/remote/SizeColorCellRenderer.java

@ -1,10 +1,10 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import com.fine.theme.light.ui.FineTableHeaderUI;
import java.awt.Color;
import static com.fr.design.remote.ui.debug.RemoteDesignNetWorkHelper.DEFAULT_COLOR;
import static com.fr.design.debug.remote.RemoteDesignNetWorkHelper.DEFAULT_COLOR;
/**
* 大小多颜色渲染

4
designer-base/src/main/java/com/fr/design/remote/ui/debug/TimeColorCellRenderer.java → designer-base/src/main/java/com/fr/design/debug/remote/TimeColorCellRenderer.java

@ -1,10 +1,10 @@
package com.fr.design.remote.ui.debug;
package com.fr.design.debug.remote;
import com.fine.theme.light.ui.FineTableHeaderUI;
import java.awt.Color;
import static com.fr.design.remote.ui.debug.RemoteDesignNetWorkHelper.DEFAULT_COLOR;
import static com.fr.design.debug.remote.RemoteDesignNetWorkHelper.DEFAULT_COLOR;
/**
* 时间多颜色渲染

48
designer-base/src/main/java/com/fr/design/debug/ui/LatencyInfo.java

@ -0,0 +1,48 @@
package com.fr.design.debug.ui;
/**
* UI卡顿信息Bean
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/07
*/
public class LatencyInfo {
// swing事件编号
private long seq;
// 耗时 ms
private long cost;
// 堆栈信息
private StackTraceElement[] detailStack;
public LatencyInfo(long seq, long cost, StackTraceElement[] detailStack) {
this.seq = seq;
this.cost = cost;
this.detailStack = detailStack;
}
public long getCost() {
return cost;
}
public void setCost(long cost) {
this.cost = cost;
}
public StackTraceElement[] getDetailStack() {
return detailStack;
}
public void setDetailStack(StackTraceElement[] detailStack) {
this.detailStack = detailStack;
}
public long getSeq() {
return seq;
}
public void setSeq(long seq) {
this.seq = seq;
}
}

17
designer-base/src/main/java/com/fr/design/debug/ui/LatencyMonitorEvent.java

@ -0,0 +1,17 @@
package com.fr.design.debug.ui;
import com.fr.event.Event;
/**
* UI性能监控事件
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/08
*/
public enum LatencyMonitorEvent implements Event<LatencyInfo> {
/**
* 超出卡顿阈值
*/
OFF_THRESHOLD_EVENT
}

56
designer-base/src/main/java/com/fr/design/debug/ui/UIInspectorHolder.java

@ -0,0 +1,56 @@
package com.fr.design.debug.ui;
import com.fanruan.gui.UiInspector;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* UIInspectorHolder 单例管理
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/08
*/
public class UIInspectorHolder {
private UiInspector uiInspector;
private final AtomicBoolean installed = new AtomicBoolean(false);
private final static class InstanceHolder {
static final UIInspectorHolder INSTANCE = new UIInspectorHolder();
}
/**
* 单例
*/
public static UIInspectorHolder getInstance() {
return UIInspectorHolder.InstanceHolder.INSTANCE;
}
/**
* 是否已启用UIInspector
* @return 是否启用
*/
public boolean isInstalled() {
return installed.get();
}
/**
* 启用UIInspector
*/
public void install() {
if (installed.compareAndSet(false, true)) {
uiInspector = new UiInspector();
}
}
/**
* 注销UIInspector
*/
public void uninstall() {
if (uiInspector != null) {
uiInspector.dispose();
installed.set(false);
}
}
}

46
designer-base/src/main/java/com/fr/design/debug/ui/UILatencyInfoHandler.java

@ -0,0 +1,46 @@
package com.fr.design.debug.ui;
import com.fr.design.carton.DispatchInfo;
import com.fr.design.carton.latency.AbstractUIDispatchHandler;
/**
* UI卡顿实时监控Handler
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/07
*/
public class UILatencyInfoHandler extends AbstractUIDispatchHandler {
private long threshold = 200;
private final static class InstanceHolder {
static final UILatencyInfoHandler INSTANCE = new UILatencyInfoHandler();
}
/**
* 单例
*/
public static UILatencyInfoHandler getInstance() {
return UILatencyInfoHandler.InstanceHolder.INSTANCE;
}
public long getThreshold() {
return threshold;
}
public void setThreshold(long threshold) {
this.threshold = threshold;
}
@Override
protected boolean accept(DispatchInfo info) {
return info.timeSoFar() > threshold;
}
@Override
protected void doHandle(DispatchInfo info) {
LatencyInfo infoBean = new LatencyInfo(info.getEventSeq(), info.timeSoFar(), info.getEventDispatchThread().getStackTrace());
UILatencyWorker.getInstance().submit(infoBean);
}
}

124
designer-base/src/main/java/com/fr/design/debug/ui/UILatencyWorker.java

@ -0,0 +1,124 @@
package com.fr.design.debug.ui;
import com.fr.design.carton.latency.UIDispatchManager;
import com.fr.event.EventDispatcher;
import com.fr.third.guava.cache.Cache;
import com.fr.third.guava.cache.CacheBuilder;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
* UI性能监控Worker
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/07
*/
public class UILatencyWorker {
private ExecutorService executorService;
private final AtomicBoolean initialized = new AtomicBoolean(false);
// 默认允许存储300卡顿信息
private final Cache<Long, LatencyInfo> infoContainer = CacheBuilder.newBuilder()
.maximumSize(300).build();
private final static class InstanceHolder {
static final UILatencyWorker INSTANCE = new UILatencyWorker();
}
/**
* 单例
*/
public static UILatencyWorker getInstance() {
return InstanceHolder.INSTANCE;
}
/**
* 开始监控: 启动异步提交线程启动定时堆栈检测任务
*/
public void start() {
if (initialized.compareAndSet(false, true)) {
executorService = Executors.newSingleThreadExecutor();
UIDispatchManager.getInstance().registerHandler(UILatencyInfoHandler.getInstance());
UIDispatchManager.getInstance().startScheduler();
}
}
/**
* 停止监控关闭异步提交线程及定时堆栈任务
*/
public void stop() {
UIDispatchManager.getInstance().unregisterHandler(UILatencyInfoHandler.getInstance());
UIDispatchManager.getInstance().stopSchedulerIfNecessary();
if (this.executorService != null) {
this.executorService.shutdown();
}
initialized.set(false);
}
/**
* 是否监控中
*
* @return 是否监控中
*/
public boolean isMonitoring() {
return initialized.get();
}
/**
* 设置UI卡顿堆栈阈值
* @param threshold 阈值 ms
*/
public void resetThreshold(long threshold) {
UILatencyInfoHandler.getInstance().setThreshold(threshold);
}
/**
* 提交卡顿堆栈信息
* @param latencyInfo 卡顿信息
*/
public void submit(LatencyInfo latencyInfo) {
executorService.submit(() -> {
if (UIMonitorHelper.isIgnoreEvent(latencyInfo.getDetailStack())) {
return;
}
LatencyInfo existInfo = infoContainer.getIfPresent(latencyInfo.getSeq());
// 确保记录的是最深的堆栈信息
if (existInfo == null || latencyInfo.getDetailStack().length > existInfo.getDetailStack().length) {
infoContainer.put(latencyInfo.getSeq(), latencyInfo);
EventDispatcher.fire(LatencyMonitorEvent.OFF_THRESHOLD_EVENT, latencyInfo);
}
});
}
/**
* 输出卡顿文本信息
*
* @return 卡顿文本信息
*/
public String getLatencyData() {
return getAllLatencyInfo().stream().map(info -> "seq:" + info.getSeq() + "\n" +
"cost:" + info.getCost() + "ms\n" +
"stack:" + UIMonitorHelper.convertStack(info.getDetailStack()) + "\n").collect(Collectors.joining("\n"));
}
/**
* 获取当前记录的所有卡顿信息
* @return 全量卡顿信息
*/
public Collection<LatencyInfo> getAllLatencyInfo() {
return infoContainer.asMap().values();
}
/**
* 清空全量卡顿信息
*/
public void clearData() {
infoContainer.invalidateAll();
}
}

40
designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorAction.java

@ -0,0 +1,40 @@
package com.fr.design.debug.ui;
import com.fine.theme.utils.FineUIUtils;
import com.fr.design.actions.UpdateAction;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.utils.gui.GUICoreUtils;
import javax.swing.JDialog;
import javax.swing.KeyStroke;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER;
/**
* UI实时监控
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/07
*/
public class UIMonitorAction extends UpdateAction {
public static final String TITLE = "UI Monitor";
public UIMonitorAction() {
this.setName(TITLE);
this.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, DEFAULT_MODIFIER));
}
@Override
public void actionPerformed(ActionEvent e) {
JDialog jDialog = new JDialog(DesignerContext.getDesignerFrame(), TITLE);
jDialog.setSize(FineUIUtils.calPaneDimensionByContext(0.5, 0.7));
UIMonitorPane monitorPane = new UIMonitorPane();
jDialog.add(monitorPane);
GUICoreUtils.centerWindow(jDialog);
jDialog.setVisible(true);
}
}

44
designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorHelper.java

@ -0,0 +1,44 @@
package com.fr.design.debug.ui;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* UI性能监控工具类
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/08
*/
public class UIMonitorHelper {
/**
* 判断是否特定的堆栈
*/
public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) {
return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative;
}
/**
* 用于判断是否为需要忽略记录的堆栈信息
*/
public static boolean isIgnoreEvent(StackTraceElement[] currentStack) {
return currentStack != null && currentStack.length >= 1 && (
stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true)
|| stackTraceElementIs(currentStack[0], "sun.misc.Unsafe", "park", true)
);
}
/**
* 堆栈格式化
* @param stackTrace 堆栈信息
*
* @return 格式化后的堆栈信息
*/
public static String convertStack(StackTraceElement[] stackTrace) {
return Arrays.stream(stackTrace)
.map(st -> "\t" + st.toString()).collect(Collectors.joining("\n"));
}
}

274
designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorPane.java

@ -0,0 +1,274 @@
package com.fr.design.debug.ui;
import com.fine.swing.ui.layout.Row;
import com.fine.theme.icon.LazyIcon;
import com.fine.theme.utils.FineClientProperties;
import com.fine.theme.utils.FineUIUtils;
import com.formdev.flatlaf.util.ScaledEmptyBorder;
import com.fr.base.extension.FileExtension;
import com.fr.design.border.FineBorderFactory;
import com.fr.design.carton.latency.LatencyLevel;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icheckbox.UICheckBox;
import com.fr.design.gui.icombobox.UIComboBox;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.icontainer.UITableScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.itable.FineUITable;
import com.fr.design.gui.itextarea.UITextArea;
import com.fr.design.gui.itoolbar.UIToolbar;
import com.fr.design.mainframe.DesignerContext;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.file.FILE;
import com.fr.file.FILEChooserPane;
import com.fr.file.filter.ChooseFileFilter;
import com.fr.general.GeneralUtils;
import com.fr.log.FineLoggerFactory;
import org.jetbrains.annotations.NotNull;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.Date;
import static com.fine.swing.ui.layout.Layouts.cell;
import static com.fine.swing.ui.layout.Layouts.column;
import static com.fine.swing.ui.layout.Layouts.row;
/**
* UI监控面板
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/11/07
*/
public class UIMonitorPane extends JPanel {
private DefaultTableModel model;
private UICheckBox inspector;
private UICheckBox monitor;
public UIMonitorPane() {
setLayout(new BorderLayout());
setBorder(new ScaledEmptyBorder(10, 10, 10, 10));
initComponent();
}
private void initComponent() {
UITableScrollPane tablePane = initLatencyTable();
Row topSettingRow = initTopSettingRow();
inspector = new UICheckBox("Open UI Inspector");
monitor = new UICheckBox("Open Latency Monitor");
JPanel monitorPane = column(10,
cell(monitor), cell(topSettingRow), cell(tablePane).weight(1)
).getComponent();
add(column(10,
cell(FineUIUtils.wrapComponentWithTitle(inspector, "UI Inspector")),
cell(FineUIUtils.wrapComponentWithTitle(monitorPane, "UI Latency Monitor"))
).getComponent(), BorderLayout.CENTER);
topSettingRow.setVisible(false);
tablePane.setVisible(false);
initMonitorStatus(topSettingRow, tablePane);
}
private void initMonitorStatus(Row topSettingRow, UITableScrollPane tablePane) {
inspector.setSelected(UIInspectorHolder.getInstance().isInstalled());
monitor.setSelected(UILatencyWorker.getInstance().isMonitoring());
// 注册事件监听
inspector.addChangeListener(e -> {
if (inspector.isSelected()) {
UIInspectorHolder.getInstance().install();
} else {
UIInspectorHolder.getInstance().uninstall();
}
});
monitor.addChangeListener(e -> {
topSettingRow.setVisible(monitor.isSelected());
tablePane.setVisible(monitor.isSelected());
if (monitor.isSelected()) {
startMonitor();
} else {
stopMonitor();
}
});
// 初始化卡顿堆栈表
if (monitor.isSelected()) {
SwingUtilities.invokeLater(() -> UILatencyWorker.getInstance().getAllLatencyInfo()
.forEach(info -> model.addRow(parseInfo2Row(info))));
}
}
private Row initTopSettingRow() {
UIComboBox comboBox = initThresholdComboBox();
UIButton export = new UIButton(new LazyIcon("export"));
export.setToolTipText("Export latency log");
UIButton clear = new UIButton(new LazyIcon("remove"));
clear.setToolTipText("Clear latency log");
JToolBar toolbar = new UIToolbar();
toolbar.add(comboBox);
toolbar.add(clear);
toolbar.add(export);
export.addActionListener(e -> exportData());
clear.addActionListener(e -> {
model.setRowCount(0);
UILatencyWorker.getInstance().clearData();
});
return row(5, cell(new UILabel("Latency Threshold")), cell(toolbar)).getComponent();
}
private static @NotNull UIComboBox initThresholdComboBox() {
UIComboBox comboBox = new UIComboBox(Arrays.stream(LatencyLevel.values())
.filter(it -> it != LatencyLevel.FLASH).map(LatencyLevel::getStart).toArray());
comboBox.putClientProperty(FineClientProperties.COMBO_BOX_TYPE, FineClientProperties.ADAPTIVE_COMBO_BOX);
comboBox.setSelectedItem(UILatencyInfoHandler.getInstance().getThreshold());
comboBox.addActionListener(e -> {
if (comboBox.getSelectedItem() != null) {
UILatencyWorker.getInstance().resetThreshold((Long) comboBox.getSelectedItem());
}
});
// 阈值初始化
comboBox.setSelectedItem(comboBox.getSelectedItem());
return comboBox;
}
private UITableScrollPane initLatencyTable() {
model = new DefaultTableModel();
model.addColumn("seq");
model.addColumn("cost(ms)");
model.addColumn("stack");
FineUITable table = new FineUITable(model) {
public boolean isCellEditable(int row, int column) {
return false;
}
};
UITableScrollPane tablePane = new UITableScrollPane(table);
table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int row = table.rowAtPoint(e.getPoint());
if (row >= 0) {
String stack = (String) table.getValueAt(row, 2);
StackPane stackPane = new StackPane(stack);
BasicDialog dialog = stackPane.showLargeWindow(SwingUtilities.getWindowAncestor(e.getComponent()), null);
dialog.setAlwaysOnTop(true);
dialog.setVisible(true);
}
}
});
TableColumnModel columnModel = table.getColumnModel();
adjustColumnWidth(columnModel.getColumn(0));
adjustColumnWidth(columnModel.getColumn(1));
return tablePane;
}
private void adjustColumnWidth(TableColumn column) {
column.setPreferredWidth(100);
column.setMinWidth(100);
column.setMaxWidth(100);
}
/**
* 开启性能监控
*/
public void startMonitor() {
EventDispatcher.listen(LatencyMonitorEvent.OFF_THRESHOLD_EVENT, latencyInfoListener);
UILatencyWorker.getInstance().start();
}
/**
* 关闭性能监控
*/
public void stopMonitor() {
UILatencyWorker.getInstance().stop();
EventDispatcher.stopListen(latencyInfoListener);
model.setRowCount(0);
}
private void exportData() {
// 导出为txt文件
FILEChooserPane fileChooserPane = FILEChooserPane.getMultiEnvInstance(true, false);
String fileName = "latency_log_" + GeneralUtils.objectToString(new Date()).replaceAll(":", "_");
fileChooserPane.setFileNameTextField(fileName, ".txt");
fileChooserPane.addChooseFILEFilter(new ChooseFileFilter(FileExtension.TXT));
int saveValue = fileChooserPane.showSaveDialog(DesignerContext.getDesignerFrame());
if (saveValue == FILEChooserPane.JOPTIONPANE_OK_OPTION || saveValue == FILEChooserPane.OK_OPTION) {
FILE target = fileChooserPane.getSelectedFILE();
try {
target.mkfile();
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(target.getPath(), true))) {
bufferedWriter.write(UILatencyWorker.getInstance().getLatencyData());
}
} catch (Exception exp) {
FineLoggerFactory.getLogger().error("[Latency] Error export latency log.", exp);
}
}
}
private final Listener<LatencyInfo> latencyInfoListener = new Listener<LatencyInfo>() {
@Override
public void on(Event event, LatencyInfo latencyInfo) {
SwingUtilities.invokeLater(() -> {
// 存量卡顿堆栈信息更新
for (int i = 0; i < model.getRowCount(); i++) {
if (latencyInfo.getSeq() == (Long) model.getValueAt(i, 0)) {
model.removeRow(i);
break;
}
}
model.addRow(parseInfo2Row(latencyInfo));
});
}
};
private Object[] parseInfo2Row(LatencyInfo latencyInfo) {
return new Object[]{
latencyInfo.getSeq(),
latencyInfo.getCost(),
UIMonitorHelper.convertStack(latencyInfo.getDetailStack())};
}
static class StackPane extends BasicPane {
public StackPane(String stack) {
setLayout(new BorderLayout());
UITextArea textArea = new UITextArea();
textArea.setBorder(null);
textArea.setEditable(false);
textArea.setText(stack);
UIScrollPane scrollPane = new UIScrollPane(textArea);
scrollPane.setBorder(FineBorderFactory.createWrappedRoundBorder());
add(scrollPane);
SwingUtilities.invokeLater(() -> scrollPane.getViewport().setViewPosition(new Point(0, 0)));
}
@Override
protected String title4PopupWindow() {
return "Latency Stack";
}
}
}

20
designer-base/src/main/java/com/fr/design/mainframe/toolbar/DebugModeMenuDef.java

@ -1,13 +1,11 @@
package com.fr.design.mainframe.toolbar;
import com.fanruan.gui.UiInspector;
import com.fine.theme.light.ui.laf.FineDarkLaf;
import com.fine.theme.light.ui.laf.FineLightLaf;
import com.fr.design.actions.UpdateAction;
import com.fr.design.gui.UILookAndFeel;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.menu.MenuDef;
import com.fr.design.remote.ui.debug.RemoteDesignNetWorkAction;
import com.fr.design.debug.remote.RemoteDesignNetWorkAction;
import com.fr.design.debug.ui.UIMonitorAction;
import java.awt.event.ActionEvent;
@ -24,15 +22,7 @@ public class DebugModeMenuDef extends MenuDef {
super("Debug");
addLookAndFeelMenu();
addRemotePane();
}
private void addUIInspect() {
this.addShortCut(new UpdateAction() {
@Override
public void actionPerformed(ActionEvent e) {
new UiInspector().showInspector(DesignerContext.getDesignerFrame());
}
});
addUIMonitorPane();
}
private void addLookAndFeelMenu() {
@ -48,4 +38,8 @@ public class DebugModeMenuDef extends MenuDef {
private void addRemotePane() {
this.addShortCut(new RemoteDesignNetWorkAction());
}
private void addUIMonitorPane() {
this.addShortCut(new UIMonitorAction());
}
}

6
designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/ChartOtherPane.java

@ -17,6 +17,7 @@ import com.fr.design.mainframe.chart.info.ChartInfoCollector;
import com.fr.van.chart.designer.component.richText.VanChartRichEditorPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import java.util.ArrayList;
import java.util.List;
import java.awt.BorderLayout;
@ -114,7 +115,10 @@ public class ChartOtherPane extends AbstractChartAttrPane {
if (ChartOtherPane.this.isHaveCondition()) {
VanChartRichEditorPane.refreshCommonChartFieldNames(chart);
ColSelectedWithSummaryMethodEditor.refreshCommonChartFieldNames(chart);
conditionAttrPane.populateBean(chart);
SwingUtilities.invokeLater(() -> {
conditionAttrPane.populateBean(chart);
initListener(conditionAttrPane);
});
}
}

15
designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/ChartStylePane.java

@ -30,6 +30,7 @@ public class ChartStylePane extends AbstractChartAttrPane {
private Chart chart;
private AttributeChangeListener listener;
private BasicPane chartAxisPane;
private boolean chartStyleInitialized = false;
protected Chart getChart() {
return chart;
@ -64,11 +65,15 @@ public class ChartStylePane extends AbstractChartAttrPane {
@Override
public void populate(ChartCollection collection) {
this.chart = collection.getSelectedChart();
this.remove(leftContentPane);
initContentPane();
this.removeAttributeChangeListener();
stylePane.populateBean(chart);
this.addAttributeChangeListener(listener);
// 子组件布局初始化,只在第一次进入的时候绘制
if (!chartStyleInitialized) {
this.remove(leftContentPane);
initContentPane();
this.removeAttributeChangeListener();
this.addAttributeChangeListener(listener);
this.initAllListeners();
chartStyleInitialized = true;
}
this.initAllListeners();
}

42
designer-realize/src/main/java/com/fanruan/boot/adaptation/ReportAdaptationComponent.java

@ -4,6 +4,7 @@ import com.fanruan.carina.Carina;
import com.fanruan.carina.annotions.DependsOn;
import com.fanruan.carina.annotions.FineComponent;
import com.fanruan.carina.annotions.Start;
import com.fanruan.carina.context.ContextListener;
import com.fanruan.plugins.resource.PluginResourceHelper;
import com.fanruan.portal.FinePortal;
import com.fanruan.portal.module.PortalModule;
@ -23,7 +24,6 @@ import com.fr.general.InterProviderImpl;
import com.fr.locale.InterMutableKey;
import com.fr.locale.LocaleMarker;
import com.fr.locale.LocaleScope;
import com.fr.plugin.ExtraClassManager;
import com.fr.plugin.context.PluginContext;
import com.fr.plugin.injectable.PluginModule;
import com.fr.plugin.observer.PluginEventType;
@ -167,25 +167,31 @@ public class ReportAdaptationComponent {
}
private void registerPluginModules() {
// 注册插件模块
try {
Set<SystemOptionProvider> systemOptionProviders = ExtraClassManager.getInstance().getArray(SystemOptionProvider.XML_TAG);
if (!CollectionUtils.isEmpty(systemOptionProviders)) {
// 资源引入采用新的方式,WebCoalition接口不再继承使用,这里只处理模块注册
for (SystemOptionProvider optionProvider : systemOptionProviders) {
PortalModule portalModule = PortalModule.create(optionProvider.id(), optionProvider.displayName())
.sortIndex(optionProvider.sortIndex())
.dynamicControl(m -> new PluginPortalModuleDevice(optionProvider.parentId(), m));
FinePortal.registerModule(optionProvider.parentId(), portalModule);
// 插件资源注册
PluginResourceHelper.getInstance().registerAtom2Portal(optionProvider);
Carina.getApplicationContext().addListener(new ContextListener() {
@Override
public void onStart() {
// 注册插件模块
try {
Set<SystemOptionProvider> systemOptionProviders = ExtraDecisionClassManager.getInstance().getArray(SystemOptionProvider.XML_TAG);
if (!CollectionUtils.isEmpty(systemOptionProviders)) {
// 资源引入采用新的方式,WebCoalition接口不再继承使用,这里只处理模块注册
for (SystemOptionProvider optionProvider : systemOptionProviders) {
PortalModule portalModule = PortalModule.create(optionProvider.id(), optionProvider.displayName())
.sortIndex(optionProvider.sortIndex())
.dynamicControl(m -> new PluginPortalModuleDevice(optionProvider.parentId(), m));
FinePortal.registerModule(optionProvider.parentId(), portalModule);
// 插件资源注册
PluginResourceHelper.getInstance().registerAtom2Portal(optionProvider);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
private void checkI18n() {
for (LocaleMarker marker : Carina.getApplicationContext().group(InterMutableKey.class).getAll()) {

5
designer-realize/src/main/java/com/fanruan/boot/env/DesignPluginComponent.java vendored

@ -15,6 +15,7 @@ import com.fr.invoke.ClassFactory;
import com.fr.json.JSONObject;
import com.fr.plugin.beforeload.embed.PluginEmbedInfo;
import com.fr.plugin.config.PluginConfigContext;
import com.fr.plugin.db.PluginDBManager;
import com.fr.plugin.injectable.PluginInjectionFilter;
import com.fr.plugin.manage.PluginManager;
import com.fr.plugin.manage.PluginSyncModuleType;
@ -46,7 +47,7 @@ import java.util.function.Supplier;
* Created on 2024/5/17
*/
@FineComponent(name = "design_plugin")
@DependsOn(dependencies = {"design_env_prepare"})
@DependsOn(dependencies = {"design_version"})
public class DesignPluginComponent extends PluginComponent {
@ -91,6 +92,7 @@ public class DesignPluginComponent extends PluginComponent {
}
});
registerPluginClassFinder();
PluginDBManager.getInstance().init();
}
@Override
@ -143,5 +145,6 @@ public class DesignPluginComponent extends PluginComponent {
@Stop
public void stop() {
super.stop();
PluginDBManager.getInstance().destroy();
}
}

31
designer-realize/src/main/java/com/fanruan/boot/env/DesignVersionComponent.java vendored

@ -0,0 +1,31 @@
package com.fanruan.boot.env;
import com.fanruan.boot.VersionComponent;
import com.fanruan.carina.annotions.DependsOn;
import com.fanruan.carina.annotions.FineComponent;
import com.fanruan.carina.annotions.Start;
import com.fanruan.version.ServiceVersion;
import com.fanruan.version.VersionCenter;
import com.fanruan.version.VersionConstants;
/**
* 设计器版本模块
*
* @author Leo.Qin
* @since 11.0
* Created on 2024/11/11
*/
@FineComponent(name = "design_version")
@DependsOn(dependencies = {"design_env_prepare"})
public class DesignVersionComponent extends VersionComponent {
@Start
@Override
public void start() {
super.start();
ServiceVersion serviceVersion = VersionCenter.getInstance().getServiceVersion();
serviceVersion.setName(VersionConstants.SERVICE_NAME_FR);
VersionCenter.getInstance().register(serviceVersion);
}
}

12
designer-realize/src/main/java/com/fr/start/CarinaDesigner.java

@ -6,12 +6,10 @@ import com.fanruan.carina.Carina;
import com.fanruan.carina.context.CarinaApplicationContext;
import com.fanruan.carina.standard.PartitionManager;
import com.fanruan.carina.standard.PartitionManagerImpl;
import com.fanruan.gui.UiInspector;
import com.fr.base.StateHubContext;
import com.fr.design.backup.DesignContext;
import com.fr.design.carton.SwitchForSwingChecker;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.mainframe.DesignerUIModeConfig;
import com.fr.log.FineLoggerFactory;
import com.fr.runtime.FineRuntime;
import com.fr.start.common.DesignerStartupContext;
@ -39,7 +37,6 @@ public class CarinaDesigner extends MainDesigner{
* main
*/
public static void main(String[] args) {
installUIDevModeTools();
DesignerStartupContext.getRecorder().start();
PartitionManager manager = new PartitionManagerImpl();
StateHubContext.setReady(false);
@ -68,13 +65,4 @@ public class CarinaDesigner extends MainDesigner{
DesignerLatencyMetric.getInstance().start();
}
/**
* 进入UI开发者模式
*/
private static void installUIDevModeTools() {
if (DesignerUIModeConfig.getInstance().isUIDevMode()) {
new UiInspector();
}
}
}

Loading…
Cancel
Save