From 7ed5e6545d00f07b5dd520c27ef16e957f18e1b1 Mon Sep 17 00:00:00 2001 From: vito Date: Fri, 2 Feb 2024 16:26:29 +0800 Subject: [PATCH] =?UTF-8?q?REPORT-99485=20TableLayoutHelper=20=E9=80=82?= =?UTF-8?q?=E9=85=8DDPI=E7=BC=A9=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/layout/TableLayoutHelper.java | 108 +++-------- .../components/TablelayoutStoryBoard.java | 169 ++++++++++++++++++ .../gui/storybook/components/Preferred.gif | Bin 0 -> 17609 bytes 3 files changed, 198 insertions(+), 79 deletions(-) create mode 100644 designer-base/src/test/java/com/fr/design/gui/storybook/components/TablelayoutStoryBoard.java create mode 100644 designer-base/src/test/resources/com/fr/design/gui/storybook/components/Preferred.gif diff --git a/designer-base/src/main/java/com/fr/design/layout/TableLayoutHelper.java b/designer-base/src/main/java/com/fr/design/layout/TableLayoutHelper.java index e66452402..5e31ba2f7 100644 --- a/designer-base/src/main/java/com/fr/design/layout/TableLayoutHelper.java +++ b/designer-base/src/main/java/com/fr/design/layout/TableLayoutHelper.java @@ -1,16 +1,13 @@ package com.fr.design.layout; - +import com.fine.theme.utils.FineUIScale; import com.fr.design.constants.LayoutConstants; -import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; -import com.fr.design.utils.gui.GUICoreUtils; -import com.fr.stable.StringUtils; - -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; +import java.awt.Component; +import java.awt.Container; public class TableLayoutHelper { @@ -18,8 +15,6 @@ public class TableLayoutHelper { public static final int FILL_LASTCOLUMN = 1; public static final int FILL_LASTROW = 2; public static final int FILL_LASTCOL_AND_ROW = 3; - private static final int FIVE = 5; - private static final int TEN = 10; private TableLayoutHelper() { } @@ -287,6 +282,8 @@ public class TableLayoutHelper { layoutRowSize[i * 2 + 1] = rowSize[i]; } + scaleIfNeed(layoutRowSize, layoutColumnSize); + layoutSize[0] = layoutColumnSize; layoutSize[1] = layoutRowSize; @@ -296,6 +293,28 @@ public class TableLayoutHelper { return resultPane; } + /** + * 缩放必要的尺寸。 + * 尺寸参数有三种,小于0有三种预定义类型,0~1之间为比例参数,大于等于1为普通px。 + * 这里只对px进行缩放处理 + * 注意:当前只对从TableLayoutHelper创建的TableLayout进行尺寸缩放。 + */ + private static void scaleIfNeed(double[] layoutRowSize, double[] layoutColumnSize) { + for (int i = 0; i < layoutRowSize.length; i++) { + double size = layoutRowSize[i]; + if (size >= 1.0) { + layoutRowSize[i] = FineUIScale.scale((float) size); + } + } + + for (int i = 0; i < layoutColumnSize.length; i++) { + double size = layoutColumnSize[i]; + if (size >= 1.0) { + layoutColumnSize[i] = FineUIScale.scale((float) size); + } + } + } + /** * 创建具有不同垂直间距的TableLayout面板 * @@ -363,73 +382,4 @@ public class TableLayoutHelper { }; return createTableLayoutPane(comp, row, column); } - - public static void main(String[] args) { - JFrame jf = new JFrame("test"); - jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - JPanel content = (JPanel) jf.getContentPane(); - content.setLayout(new GridLayout(2, 3)); - double p = TableLayout.PREFERRED; - double f = TableLayout.FILL; - JPanel jp1 = TableLayoutHelper.createTableLayoutPane(createTestComponents("jp1"), TableLayoutHelper.FILL_NONE); - JPanel jp2 = TableLayoutHelper.createGapTableLayoutPane(createTestComponents("jp2"), - TableLayoutHelper.FILL_LASTCOL_AND_ROW, (double)2 * TEN, (double)2 * TEN); - JPanel jp3 = TableLayoutHelper.createGapTableLayoutPane(createTestComponents("jp3"), - new double[]{f, p, f, p}, new double[]{f, f}, 4, 4); - JPanel jp4 = TableLayoutHelper.createGapTableLayoutPane(createTestComponents("jp4"), - new double[]{p, FIVE * TEN, p, p, p, p}, new double[]{p, f}, new int[][]{{1, 3}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, 1, FIVE); - - JPanel jp5 = TableLayoutHelper.createDiffVGapTableLayoutPane(createTestComponents("jp5"), - new double[]{p, p, p, p, p}, new double[]{p, f}, 4, new double[]{4, 8, 14, 4}); - JPanel jp6 = new JPanel(); - jp1.setBorder(BorderFactory.createLineBorder(Color.red)); - jp2.setBorder(BorderFactory.createLineBorder(Color.red)); - jp3.setBorder(BorderFactory.createLineBorder(Color.red)); - jp4.setBorder(BorderFactory.createLineBorder(Color.red)); - jp5.setBorder(BorderFactory.createLineBorder(Color.red)); - jp6.setBorder(BorderFactory.createLineBorder(Color.red)); - - - content.add(jp1); - content.add(jp2); - content.add(jp3); - content.add(jp4); - content.add(jp5); - content.add(jp6); - - GUICoreUtils.centerWindow(jf); - jf.setSize(900, 600); - jf.setVisible(true); - } - - private static Component[][] createTestComponents(String name) { - UILabel label1 = new UILabel(name + "laaaable1"); - UILabel label2 = new UILabel(name + "lable2"); - UILabel label3 = new UILabel(name + "lable3"); - UILabel label4 = new UILabel(name + "lable4"); - UILabel label5 = new UILabel(name + "lable5"); - UIButton button1 = new UIButton(name + "button1"); - UIButton button2 = new UIButton(name + "button2"); - label1.setSize(label1.getPreferredSize()); - label1.setBorder(BorderFactory.createLineBorder(Color.blue)); - label2.setSize(label2.getPreferredSize()); - label2.setBorder(BorderFactory.createLineBorder(Color.blue)); - label3.setSize(label3.getPreferredSize()); - label3.setBorder(BorderFactory.createLineBorder(Color.blue)); - label4.setSize(label4.getPreferredSize()); - label4.setBorder(BorderFactory.createLineBorder(Color.blue)); - label5.setSize(label5.getPreferredSize()); - label5.setBorder(BorderFactory.createLineBorder(Color.blue)); - button1.setSize(button1.getPreferredSize()); - button2.setSize(button2.getPreferredSize()); - button1.setBackground(Color.darkGray); - button2.setBackground(Color.darkGray); - return new Component[][]{ - new Component[]{label1, button1}, - new Component[]{label2, null}, - new Component[]{label3}, - new Component[]{null, label4}, - new Component[]{button2, label5} - }; - } -} \ No newline at end of file +} diff --git a/designer-base/src/test/java/com/fr/design/gui/storybook/components/TablelayoutStoryBoard.java b/designer-base/src/test/java/com/fr/design/gui/storybook/components/TablelayoutStoryBoard.java new file mode 100644 index 000000000..c99995d60 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/gui/storybook/components/TablelayoutStoryBoard.java @@ -0,0 +1,169 @@ +package com.fr.design.gui.storybook.components; + +import com.fine.swing.ui.layout.Layouts; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.gui.storybook.StoryBoard; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import org.jetbrains.annotations.NotNull; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.Color; +import java.awt.Component; + +import static com.fine.swing.ui.layout.Layouts.cell; +import static com.fine.swing.ui.layout.Layouts.row; +import static com.fr.design.layout.TableLayoutHelper.createDiffVGapTableLayoutPane; +import static com.fr.design.layout.TableLayoutHelper.createGapTableLayoutPane; +import static com.fr.design.layout.TableLayoutHelper.createTableLayoutPane; + +/** + * 表格布局展示 + *
+ * TableLayout 来自开源项目 + * TableLayout + * + * @author vito + * @since 11.0 + * Created on 2024/1/31 + */ +public class TablelayoutStoryBoard extends StoryBoard { + + + private static final double P = TableLayout.PREFERRED; + private static final double F = TableLayout.FILL; + + public TablelayoutStoryBoard() { + + super("Tablelayout"); + add( + cell(new UILabel("简单布局")).with(this::h3), + layout1(), + + cell(new UILabel("TableLayoutHelper 快速创建")).with(this::h3), + row( + cell(createTableLayoutPane(createTestComponents("jp1"), TableLayoutHelper.FILL_NONE)) + .with(it -> it.setBorder(BorderFactory.createLineBorder(Color.red))).weight(1), + + cell(createGapTableLayoutPane(createTestComponents("jp2"), + TableLayoutHelper.FILL_LASTCOL_AND_ROW, 20, 20)) + .with(it -> it.setBorder(BorderFactory.createLineBorder(Color.red))).weight(1), + + cell(createGapTableLayoutPane(createTestComponents("jp3"), + new double[]{F, P, F, P}, new double[]{F, F}, 4, 4)) + .with(it -> it.setBorder(BorderFactory.createLineBorder(Color.red))).weight(1) + ), + row( + cell(createGapTableLayoutPane(createTestComponents("jp4"), + new double[]{P, 50, P, P, P, P}, new double[]{P, F}, new int[][]{{1, 3}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, 1, 5)) + .with(it -> it.setBorder(BorderFactory.createLineBorder(Color.red))).weight(1), + + cell(createDiffVGapTableLayoutPane(createTestComponents("jp5"), + new double[]{P, P, P, P, P}, new double[]{P, F}, 4, new double[]{4, 8, 14, 4})) + .with(it -> it.setBorder(BorderFactory.createLineBorder(Color.red))).weight(1) + ) + + + ); + } + + + private static Component[][] createTestComponents(String name) { + UILabel label1 = new UILabel(name + "laaaable1"); + UILabel label2 = new UILabel(name + "lable2"); + UILabel label3 = new UILabel(name + "lable3"); + UILabel label4 = new UILabel(name + "lable4"); + UILabel label5 = new UILabel(name + "lable5"); + UIButton button1 = new UIButton(name + "button1"); + UIButton button2 = new UIButton(name + "button2"); + label1.setSize(label1.getPreferredSize()); + label1.setBorder(BorderFactory.createLineBorder(Color.blue)); + label2.setSize(label2.getPreferredSize()); + label2.setBorder(BorderFactory.createLineBorder(Color.blue)); + label3.setSize(label3.getPreferredSize()); + label3.setBorder(BorderFactory.createLineBorder(Color.blue)); + label4.setSize(label4.getPreferredSize()); + label4.setBorder(BorderFactory.createLineBorder(Color.blue)); + label5.setSize(label5.getPreferredSize()); + label5.setBorder(BorderFactory.createLineBorder(Color.blue)); + button1.setSize(button1.getPreferredSize()); + button2.setSize(button2.getPreferredSize()); + return new Component[][]{ + new Component[]{label1, button1}, + new Component[]{label2, null}, + new Component[]{label3}, + new Component[]{null, label4}, + new Component[]{button2, label5} + }; + } + + /** + * 图示 {@link /com/fr/design/gui/storybook/components/Preferred.gif} + */ + private Layouts.Cell layout1() { + + JPanel pane = getTableLayoutPanel(); + + // Create all controls + UILabel labelName = new UILabel("Name"); + UILabel labelAddress = new UILabel("Address"); + UILabel labelCity = new UILabel("City"); + UILabel labelState = new UILabel("State"); + UILabel labelZip = new UILabel("Zip"); + + UITextField textfieldName = new UITextField(10); + UITextField textfieldAddress = new UITextField(20); + UITextField textfieldCity = new UITextField(10); + UITextField textfieldState = new UITextField(2); + UITextField textfieldZip = new UITextField(5); + + UIButton buttonOk = new UIButton("OK"); + UIButton buttonCancel = new UIButton("Cancel"); + JPanel panelButton = new JPanel(); + panelButton.add(buttonOk); + panelButton.add(buttonCancel); + + // Add all controls + pane.add(labelName, "1, 1, 5, 1"); + pane.add(textfieldName, "1, 3, 5, 3"); + pane.add(labelAddress, "1, 5, 5, 5"); + pane.add(textfieldAddress, "1, 7, 5, 7"); + pane.add(labelCity, "1, 9"); + pane.add(textfieldCity, "1, 11"); + pane.add(labelState, "3, 9"); + pane.add(textfieldState, "3, 11"); + pane.add(labelZip, "5, 9"); + pane.add(textfieldZip, "5, 11"); + pane.add(panelButton, "1, 13, 5, 13"); + return cell(pane); + } + + @NotNull + private static JPanel getTableLayoutPanel() { + JPanel pane = new JPanel(); + // b - border + // f - FILL + // p - PREFERRED + // vs - vertical space between labels and text fields + // vg - vertical gap between form elements + // hg - horizontal gap between form elements + + double b = 10; + double f = TableLayout.FILL; + double p = TableLayout.PREFERRED; + double vs = 5; + double vg = 10; + double hg = 10; + + double[][] size = + {{b, f, hg, p, hg, p, b}, + {b, p, vs, p, vg, p, vs, p, vg, p, vs, p, vg, p, b}}; + + TableLayout layout = new TableLayout(size); + pane.setLayout(layout); + return pane; + } +} diff --git a/designer-base/src/test/resources/com/fr/design/gui/storybook/components/Preferred.gif b/designer-base/src/test/resources/com/fr/design/gui/storybook/components/Preferred.gif new file mode 100644 index 0000000000000000000000000000000000000000..88e9c2dfab60f3d4e0b2ee462a4ea6a2641a54ef GIT binary patch literal 17609 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 fwLn%8y^d6(7!6fbO