/* Author: UnlegitDqrk * Feb. 2 2026 * */ package org.openautonomousconnection.oacswing.component; import lombok.Getter; import lombok.Setter; import org.openautonomousconnection.oacswing.component.design.DesignManager; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; /** * Custom title bar with tabs and window control buttons. */ public class OACTitleBar extends OACPanel { @Getter @Setter private static int HEIGHT = 42; private final OACFrame frame; private final OACTabbedPane tabs; private Point dragStartOnScreen; private Point dragStartFrameLocation; /** * Creates a title bar for the given frame. * * @param frame owning frame */ public OACTitleBar(OACFrame frame) { super(new BorderLayout()); this.frame = frame; setPreferredSize(new Dimension(1, HEIGHT)); setBorder(new EmptyBorder(6, 10, 6, 10)); tabs = createTabs(); OACPanel left = new OACPanel(new BorderLayout()); left.setOpaque(false); left.add(tabs, BorderLayout.CENTER); OACPanel right = createWindowControls(); add(left, BorderLayout.CENTER); add(right, BorderLayout.EAST); installDragToMove(); installDoubleClickMaximize(); } /** * Returns the tab component. * * @return tabbed pane */ public OACTabbedPane getTabs() { return tabs; } /** * Adds a new tab. * * @param title tab title */ public void addTab(String title) { OACPanel placeholder = new OACPanel(); placeholder.setOpaque(false); tabs.addTab(title, placeholder); } private OACTabbedPane createTabs() { OACTabbedPane tp = new OACTabbedPane(OACTabbedPane.TOP, OACTabbedPane.SCROLL_TAB_LAYOUT); tp.setOpaque(false); tp.setFocusable(false); return tp; } private OACPanel createWindowControls() { OACPanel p = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0)); p.setOpaque(false); OACButton min = createTitleButton("—"); OACButton max = createTitleButton("▢"); OACButton close = createTitleButton("✕"); min.addActionListener(e -> frame.setState(Frame.ICONIFIED)); max.addActionListener(e -> toggleMaximize()); close.addActionListener(e -> frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING))); p.add(min); p.add(max); p.add(close); return p; } private OACButton createTitleButton(String text) { OACButton b = new OACButton(text); b.setFocusable(false); b.setBorderPainted(false); b.setContentAreaFilled(true); b.setOpaque(true); b.setPreferredSize(new Dimension(42, 28)); return b; } private void installDragToMove() { MouseAdapter drag = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { dragStartOnScreen = e.getLocationOnScreen(); dragStartFrameLocation = frame.getLocation(); } @Override public void mouseDragged(MouseEvent e) { if (dragStartOnScreen == null || dragStartFrameLocation == null) { return; } Point now = e.getLocationOnScreen(); int dx = now.x - dragStartOnScreen.x; int dy = now.y - dragStartOnScreen.y; if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) { return; } frame.setLocation(dragStartFrameLocation.x + dx, dragStartFrameLocation.y + dy); frame.dispatchEvent(new ComponentEvent(e.getComponent(), ComponentEvent.COMPONENT_MOVED)); } }; addMouseListener(drag); addMouseMotionListener(drag); } private void installDoubleClickMaximize() { addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) { toggleMaximize(); } } }); } private void toggleMaximize() { int state = frame.getExtendedState(); boolean maximized = (state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH; if (maximized) { frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH); } else { frame.setExtendedState(state | Frame.MAXIMIZED_BOTH); } frame.dispatchEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED)); } }