diff --git a/src/main/java/ai/nets/samj/gui/JSwitchButtonNew.java b/src/main/java/ai/nets/samj/gui/JSwitchButtonNew.java new file mode 100644 index 0000000..cd4c63d --- /dev/null +++ b/src/main/java/ai/nets/samj/gui/JSwitchButtonNew.java @@ -0,0 +1,166 @@ +package ai.nets.samj.gui; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.AbstractButton; +import javax.swing.DefaultButtonModel; +import javax.swing.JLabel; + +public class JSwitchButtonNew extends AbstractButton { + private Color colorDark = new Color(20,20,20); + private Color black = new Color(0,0,0,100); + private Color white = new Color(255,255,255,220); + private Color red = new Color(220,20,20); + private Color green = new Color(20,220,20); + private final String trueLabel; + private final String falseLabel; + private int minWidth = 100; // Default minimum width + private int minHeight = 30; // Default minimum height + private boolean initialized = false; + + public JSwitchButtonNew(String trueLabel, String falseLabel) { + this.trueLabel = trueLabel; + this.falseLabel = falseLabel; + + // Set initial minimum size + setMinimumSize(new Dimension(minWidth, minHeight)); + setPreferredSize(new Dimension(minWidth, minHeight)); + + setModel(new DefaultButtonModel()); + setSelected(false); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + setSelected(!isSelected()); + } + }); + } + + @Override + public void addNotify() { + super.addNotify(); + if (!initialized) { + initialized = true; + // Now we can safely get FontMetrics + calculateMinimumWidth(); + } + } + + private void calculateMinimumWidth() { + FontMetrics fm = getFontMetrics(getFont()); + if (fm != null) { + double trueLenth = 5 + fm.stringWidth(trueLabel); + double falseLenght = 5 + fm.stringWidth(falseLabel); + int maxTextWidth = (int)Math.max(trueLenth, falseLenght); + int gap = Math.max(5, 5 + (int)Math.abs(trueLenth - falseLenght)); + minWidth = maxTextWidth + gap * 4; + setMinimumSize(new Dimension(minWidth, minHeight)); + setPreferredSize(new Dimension(minWidth, minHeight)); + revalidate(); + } + } + + @Override + public void setBounds(int x, int y, int width, int height) { + width = Math.max(width, minWidth); + height = Math.max(height, minHeight); + super.setBounds(x, y, width, height); + } + + @Override + public void setSelected(boolean b) { + if(b) { + setText(trueLabel); + setBackground(green); + } else { + setBackground(red); + setText(falseLabel); + } + super.setSelected(b); + } + + @Override + protected void paintComponent(Graphics g) { + // Cast the basic Graphics object to Graphics2D which provides more advanced features + Graphics2D g2 = (Graphics2D)g; + // Enable anti-aliasing for smoother edges + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // Get current component dimensions + int width = getWidth(); + int height = getHeight(); + + // Calculate thumb width as either half the component width or full height, whichever is smaller + int thumbWidth = Math.min(width / 2, height); + + // Draw the main background of the switch using the background color (red or green) + g2.setColor(getBackground()); + g2.fillRoundRect(1, 1, width - 2, height - 2, height/2, height/2); + + // Draw two borders: outer black and inner white + g2.setColor(black); // Note: this is using the field 'black = new Color(0,0,0,100)' - 100 is alpha (transparency) + g2.drawRoundRect(1, 1, width - 2, height - 2, height/2, height/2); + g2.setColor(white); // Note: this is using the field 'white = new Color(255,255,255,100)' - 100 is alpha + g2.drawRoundRect(2, 2, width - 4, height - 4, height/2, height/2); + + // Calculate thumb X position - when selected, move to right side + int thumbX = isSelected() ? width - thumbWidth - 2 : 2; + + // Draw the thumb with gradient + g2.setPaint(new GradientPaint(thumbX, 0, white, thumbX, height, white)); + g2.fillRoundRect(thumbX, 2, thumbWidth, height - 4, height/2, height/2); + + // Draw the three dots if thumb is wide enough + if (thumbWidth > 14) { + drawThumbDetails(g2, thumbX, thumbWidth, height); + } + + // Configure text drawing + g2.setColor(Color.WHITE); // This is pure white (255,255,255,255) + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + // Set font as bold sans-serif, with size being either half the height or 16, whichever is smaller + g2.setFont(new Font(Font.SANS_SERIF, Font.BOLD, Math.min(height/2, 16))); + + // Calculate text position + FontMetrics fm = g2.getFontMetrics(); + String text = getText(); + int textWidth = fm.stringWidth(text); + // Calculate X position for text based on whether switch is selected + int textX = isSelected() ? + (thumbX - textWidth)/2 : // If selected, text goes on left side + thumbX + thumbWidth + (width - thumbX - thumbWidth - textWidth)/2; // If not selected, text goes on right side + // Calculate Y position to center text vertically + int textY = (height + fm.getAscent() - fm.getDescent())/2; + + // Draw the text + g2.drawString(text, textX, textY); + } + + private void drawThumbDetails(Graphics2D g2, int x, int thumbWidth, int height) { + // Center point of thumb + int centerX = x + thumbWidth/2; + int centerY = height/2; + + // Scale the dots based on thumb size + int dotSize = Math.max(1, thumbWidth/10); + int dotGap = dotSize + 1; + + // Draw dots + g2.setColor(colorDark); + for (int i = -1; i <= 1; i++) { + g2.fillRect(centerX + i*dotGap - dotSize/2, centerY - dotGap, dotSize, dotSize * 3); + } + /** + g2.setColor(colorDark); + for (int i = -1; i <= 1; i++) { + g2.fillRect(centerX + i*dotGap - dotSize/2, centerY, dotSize, dotSize); + } + + g2.setColor(colorDark); + for (int i = -1; i <= 1; i++) { + g2.fillRect(centerX + i*dotGap - dotSize/2, centerY + dotGap, dotSize, dotSize); + } + */ + } +} \ No newline at end of file diff --git a/src/main/java/ai/nets/samj/gui/MainGUI.java b/src/main/java/ai/nets/samj/gui/MainGUI.java index 60da9d5..848c5cc 100644 --- a/src/main/java/ai/nets/samj/gui/MainGUI.java +++ b/src/main/java/ai/nets/samj/gui/MainGUI.java @@ -26,15 +26,14 @@ public class MainGUI extends JFrame { private boolean isDrawerOpen = false; private JCheckBox chkRoiManager = new JCheckBox("Add to RoiManager", true); - private JSwitchButton chkInstant = new JSwitchButton("LIVE", "OFF"); + private JCheckBox chkReturnLargest = new JCheckBox("Only return largest ROI", true); + private JSwitchButtonNew chkInstant = new JSwitchButtonNew("LIVE", "OFF"); private JButton go = new JButton("Go"); private JButton close = new JButton("Close"); private JButton help = new JButton("Help"); private JButton export = new JButton("Export..."); private final ModelSelection cmbModels; private final ImageSelection cmbImages; - private JComboBox cmbObjects = new JComboBox(); - private JTabbedPane tab = new JTabbedPane(); private JLabel drawerTitle = new JLabel(); @@ -49,7 +48,7 @@ public class MainGUI extends JFrame { private static double HEADER_VERTIACAL_RATIO = 0.1; - private static int MAIN_VERTICAL_SIZE = 500; + private static int MAIN_VERTICAL_SIZE = 400; private static int MAIN_HORIZONTAL_SIZE = 250; public MainGUI() { @@ -66,8 +65,6 @@ public void modelChanged(SAMModel selectedModel) { cmbModels = ModelSelection.create(DEFAULT_MODEL_LIST, modelListener); - cmbObjects.addItem("Only Largest Object"); - cmbObjects.addItem("All Objects"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Use BorderLayout for the main frame @@ -108,8 +105,9 @@ private JPanel createTitlePanel() { titlePanel.setBackground(Color.LIGHT_GRAY); int height = (int) (HEADER_VERTIACAL_RATIO * MAIN_VERTICAL_SIZE); titlePanel.setPreferredSize(new Dimension(0, height)); // Fixed height - - JLabel titleLabel = new JLabel("Application Title", SwingConstants.CENTER); + String text = "
" + + "SAM" + "J"; + JLabel titleLabel = new JLabel(text, SwingConstants.CENTER); titleLabel.setFont(new Font("Arial", Font.BOLD, 24)); titlePanel.setLayout(new BorderLayout()); @@ -124,7 +122,7 @@ private JPanel createCenterPanel() { centerPanel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(5, 2, 5, 2); // Insets around components + gbc.insets = new Insets(5, 5, 5, 5); // Insets around components gbc.gridx = 0; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.BOTH; @@ -136,12 +134,12 @@ private JPanel createCenterPanel() { // Second component: Radio button with changing panel gbc.gridy = 1; - gbc.weighty = 0.5; + gbc.weighty = 0.4; centerPanel.add(createSecondComponent(), gbc); // Third component: Two checkboxes and a button gbc.gridy = 2; - gbc.weighty = 0.25; + gbc.weighty = 0.35; centerPanel.add(createThirdComponent(), gbc); return centerPanel; @@ -151,7 +149,7 @@ private JPanel createCenterPanel() { private JPanel createBottomPanel() { JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new GridBagLayout()); - bottomPanel.setBorder(new EmptyBorder(2, 2, 2, 2)); + bottomPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); GridBagConstraints gbcb = new GridBagConstraints(); gbcb.gridy = 0; @@ -159,13 +157,11 @@ private JPanel createBottomPanel() { gbcb.weighty = 1; gbcb.fill = GridBagConstraints.BOTH; - JButton button1 = new JButton("OK"); - JButton button2 = new JButton("Cancel"); gbcb.gridx = 0; - bottomPanel.add(button1, gbcb); + bottomPanel.add(help, gbcb); gbcb.gridx = 1; - bottomPanel.add(button2, gbcb); + bottomPanel.add(close, gbcb); return bottomPanel; } @@ -208,8 +204,8 @@ private JPanel createSecondComponent() { // Radio buttons JPanel radioPanel = new JPanel(); - JRadioButton radioButton1 = new JRadioButton("Option 1", true); - JRadioButton radioButton2 = new JRadioButton("Option 2"); + JRadioButton radioButton1 = new JRadioButton("Manual", true); + JRadioButton radioButton2 = new JRadioButton("Preset prompts"); ButtonGroup radioGroup = new ButtonGroup(); radioGroup.add(radioButton1); @@ -223,12 +219,37 @@ private JPanel createSecondComponent() { cardPanel.setBorder(new LineBorder(Color.BLACK)); // First card - JPanel card1 = new JPanel(); - card1.add(new JLabel("Content for Option 1")); - - // Second card - JPanel card2 = new JPanel(); - card2.add(new JLabel("Content for Option 2")); + JPanel card1 = new JPanel(new GridBagLayout()); + GridBagConstraints gbc0 = new GridBagConstraints(); + gbc0.gridx = 0; + gbc0.gridy = 0; + gbc0.weighty = 0.2; + gbc0.anchor = GridBagConstraints.NORTH; + card1.add(new JLabel("Instant Annotation"), gbc0); + + gbc0.gridy = 1; + gbc0.anchor = GridBagConstraints.CENTER; + gbc0.fill = GridBagConstraints.BOTH; + gbc0.weighty = 0.8; + gbc0.insets = new Insets(0, 0, 10, 0); + card1.add(chkInstant, gbc0); + //chkInstant.setSize(new Dimension(0, (int) (0.1 * MAIN_VERTICAL_SIZE))); + + JPanel card2 = new JPanel(new GridBagLayout()); + gbc0.gridy = 0; + gbc0.anchor = GridBagConstraints.NORTH; + gbc0.fill = GridBagConstraints.NONE; + gbc0.weighty = 0.2; + gbc0.insets = new Insets(0, 0, 0, 0); + card2.add(new JLabel("Selection from ROIManager"), gbc0); + + gbc0.gridy = 1; + gbc0.anchor = GridBagConstraints.CENTER; + gbc0.fill = GridBagConstraints.BOTH; + gbc0.insets = new Insets(0, 0, 10, 0); + gbc0.weighty = 0.8; + JButton btnBatchSAMize = new JButton("Batch SAMize"); + card2.add(btnBatchSAMize, gbc0); cardPanel.add(card1, "Option 1"); cardPanel.add(card2, "Option 2"); @@ -278,22 +299,17 @@ private JPanel createThirdComponent() { gbc.weightx = 1.0; // First checkbox - JCheckBox checkBox1 = new JCheckBox("Option A"); gbc.gridy = 0; - thirdComponent.add(checkBox1, gbc); + thirdComponent.add(chkRoiManager, gbc); // Second checkbox - JCheckBox checkBox2 = new JCheckBox("Option B"); gbc.gridy = 1; - thirdComponent.add(checkBox2, gbc); + thirdComponent.add(this.chkReturnLargest, gbc); // Button - JButton processButton = new JButton("Process"); gbc.gridy = 2; - //gbc.weighty = 1.0; // Allows the button to move with resizing - //gbc.anchor = GridBagConstraints.NORTH; - thirdComponent.add(processButton, gbc); - thirdComponent.setPreferredSize(new Dimension(0, (int) (MAIN_VERTICAL_SIZE * 0.15))); + thirdComponent.add(this.export, gbc); + thirdComponent.setPreferredSize(new Dimension(0, (int) (MAIN_VERTICAL_SIZE * 0.2))); return thirdComponent; }