testing some new stuff!
This commit is contained in:
@@ -3,44 +3,45 @@ package io.swtc;
|
||||
import com.github.sarxos.webcam.Webcam;
|
||||
import io.swtc.proccessing.WebcamCaptureLoop;
|
||||
import io.swtc.proccessing.CameraPanel;
|
||||
import io.swtc.proccessing.FilterPanel;
|
||||
import io.swtc.recording.VideoRecorder;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.geom.CubicCurve2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
*
|
||||
* This file is basically just UI, its boring the interesting stuff is in the utilities section!
|
||||
*
|
||||
* */
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Blender-style UI where the Effects panel is triggered by a Tab click
|
||||
*/
|
||||
public class SwingIFrame {
|
||||
private final JFrame mainFrame;
|
||||
private final JDesktopPane desktopPane;
|
||||
private final BlenderDesktopPane desktopPane;
|
||||
private boolean fullscreen = false;
|
||||
private Rectangle windowedBounds;
|
||||
private boolean blackbg = false;
|
||||
private final Color defDesktopBg;
|
||||
private final Color bgcolor;
|
||||
|
||||
public SwingIFrame() {
|
||||
mainFrame = new JFrame("viewer");
|
||||
mainFrame.setSize(1280,720);
|
||||
mainFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
private final Map<JInternalFrame, EffectsPanelFrame> cameraToEffects = new HashMap<>();
|
||||
|
||||
public SwingIFrame() {
|
||||
mainFrame = new JFrame("Viewer");
|
||||
mainFrame.setSize(1280, 720);
|
||||
mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
|
||||
// this is good on the eyes for long periods of times,
|
||||
// i would have used #4B9EA0, which is also easy on the eyes
|
||||
bgcolor = Color.decode("#1e1e1e");
|
||||
|
||||
desktopPane = new JDesktopPane();
|
||||
desktopPane = new BlenderDesktopPane(cameraToEffects);
|
||||
desktopPane.setBackground(Color.WHITE);
|
||||
defDesktopBg = desktopPane.getBackground();
|
||||
mainFrame.add(desktopPane, BorderLayout.CENTER);
|
||||
@@ -50,7 +51,7 @@ public class SwingIFrame {
|
||||
}
|
||||
|
||||
public void addCameraInternalFrame(Webcam webcam) {
|
||||
JInternalFrame iframe = new JInternalFrame(
|
||||
JInternalFrame cameraFrame = new JInternalFrame(
|
||||
webcam.getName(),
|
||||
true, true, true, true
|
||||
);
|
||||
@@ -61,326 +62,152 @@ public class SwingIFrame {
|
||||
);
|
||||
|
||||
JPanel contentPanel = new JPanel(new BorderLayout());
|
||||
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
|
||||
tabbedPane.addTab("View", cameraPanel);
|
||||
tabbedPane.addTab("Capture", createCapturePanel(cameraPanel, webcam));
|
||||
tabbedPane.addTab("Effects", createEffectsPanel(cameraPanel));
|
||||
// Add a placeholder tab that acts as a button
|
||||
tabbedPane.addTab("Open Effect Panel", new JPanel());
|
||||
|
||||
contentPanel.add(tabbedPane, BorderLayout.CENTER);
|
||||
|
||||
iframe.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() {
|
||||
// Logic to open/show the Effects Frame when the tab is clicked
|
||||
tabbedPane.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void internalFrameClosing(javax.swing.event.InternalFrameEvent e) {
|
||||
// if we dont call this the camera stays open until the procces dies.
|
||||
captureLoop.stop();
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (tabbedPane.getSelectedIndex() == 2) { // "Effects +" tab index
|
||||
// Revert selection back to "View" so the tab doesn't stay stuck on the empty panel
|
||||
tabbedPane.setSelectedIndex(0);
|
||||
|
||||
EffectsPanelFrame effectsFrame = cameraToEffects.get(cameraFrame);
|
||||
if (effectsFrame != null) {
|
||||
effectsFrame.setVisible(true);
|
||||
try {
|
||||
effectsFrame.setSelected(true);
|
||||
} catch (java.beans.PropertyVetoException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
iframe.add(contentPanel);
|
||||
iframe.setSize(600, 500);
|
||||
cameraFrame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() {
|
||||
@Override
|
||||
public void internalFrameClosing(javax.swing.event.InternalFrameEvent e) {
|
||||
captureLoop.stop();
|
||||
EffectsPanelFrame effectsFrame = cameraToEffects.get(cameraFrame);
|
||||
if (effectsFrame != null) {
|
||||
effectsFrame.dispose();
|
||||
cameraToEffects.remove(cameraFrame);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cameraFrame.add(contentPanel);
|
||||
cameraFrame.setSize(600, 500);
|
||||
|
||||
int offset = desktopPane.getAllFrames().length * 30;
|
||||
iframe.setLocation(offset, offset);
|
||||
cameraFrame.setLocation(50 + offset, 50 + offset);
|
||||
|
||||
desktopPane.add(iframe);
|
||||
iframe.setVisible(true);
|
||||
// Pre-create the effects frame but keep it hidden
|
||||
EffectsPanelFrame effectsFrame = new EffectsPanelFrame(
|
||||
"Effects - " + webcam.getName(),
|
||||
cameraPanel,
|
||||
cameraFrame
|
||||
);
|
||||
effectsFrame.setSize(350, 600);
|
||||
effectsFrame.setLocation(700 + offset, 50 + offset);
|
||||
effectsFrame.setVisible(false); // Hidden by default
|
||||
|
||||
cameraToEffects.put(cameraFrame, effectsFrame);
|
||||
addLinkageListeners(cameraFrame, effectsFrame);
|
||||
|
||||
desktopPane.add(cameraFrame);
|
||||
desktopPane.add(effectsFrame);
|
||||
|
||||
cameraFrame.setVisible(true);
|
||||
captureLoop.start();
|
||||
}
|
||||
|
||||
private void addLinkageListeners(JInternalFrame cameraFrame, EffectsPanelFrame effectsFrame) {
|
||||
java.awt.event.ComponentAdapter linker = new java.awt.event.ComponentAdapter() {
|
||||
@Override
|
||||
public void componentMoved(java.awt.event.ComponentEvent e) {
|
||||
desktopPane.repaint();
|
||||
}
|
||||
@Override
|
||||
public void componentResized(java.awt.event.ComponentEvent e) {
|
||||
desktopPane.repaint();
|
||||
}
|
||||
};
|
||||
cameraFrame.addComponentListener(linker);
|
||||
effectsFrame.addComponentListener(linker);
|
||||
}
|
||||
|
||||
// --- Standard UI Setup Methods ---
|
||||
|
||||
private void setupBlackBg() {
|
||||
JRootPane root = mainFrame.getRootPane();
|
||||
|
||||
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke("B"), "toggleBlackBg");
|
||||
|
||||
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("B"), "toggleBlackBg");
|
||||
root.getActionMap().put("toggleBlackBg", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
setbgblack();
|
||||
}
|
||||
@Override public void actionPerformed(ActionEvent e) { setbgblack(); }
|
||||
});
|
||||
}
|
||||
/* Setup F11 for Fullscreen */
|
||||
|
||||
private void setupFullscreenToggle() {
|
||||
JRootPane root = mainFrame.getRootPane();
|
||||
|
||||
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke("F11"), "toggleFullscreen");
|
||||
|
||||
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F11"), "toggleFullscreen");
|
||||
root.getActionMap().put("toggleFullscreen", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleFullscreen();
|
||||
}
|
||||
@Override public void actionPerformed(ActionEvent e) { toggleFullscreen(); }
|
||||
});
|
||||
}
|
||||
|
||||
private void setbgblack() {
|
||||
if (!blackbg) {
|
||||
// easy stuff here, just setting the bg to the private color
|
||||
desktopPane.setBackground(bgcolor);
|
||||
updateInternalFrameBg(bgcolor);
|
||||
} else {
|
||||
desktopPane.setBackground(defDesktopBg);
|
||||
updateInternalFrameBg(null);
|
||||
}
|
||||
blackbg = !blackbg;
|
||||
}
|
||||
|
||||
private void updateInternalFrameBg(Color bg) {
|
||||
for (JInternalFrame frame : desktopPane.getAllFrames()) {
|
||||
Container content = frame.getContentPane();
|
||||
if (bg != null) {
|
||||
content.setBackground(bg);
|
||||
} else content.setBackground(null); // restore default
|
||||
content.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleFullscreen() {
|
||||
if (!fullscreen) {
|
||||
// We set the window to borderless windowed mode, so it doesnt
|
||||
// lag like shit when we drag windows around, which is annoying
|
||||
windowedBounds = mainFrame.getBounds();
|
||||
mainFrame.dispose();
|
||||
mainFrame.setUndecorated(true);
|
||||
mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
|
||||
mainFrame.setVisible(true);
|
||||
} else {
|
||||
// do the opposite
|
||||
mainFrame.dispose();
|
||||
mainFrame.setUndecorated(false);
|
||||
mainFrame.setExtendedState(JFrame.NORMAL);
|
||||
mainFrame.setBounds(windowedBounds);
|
||||
mainFrame.setVisible(true);
|
||||
}
|
||||
|
||||
fullscreen = !fullscreen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private JPanel createCapturePanel(CameraPanel cameraPanel, Webcam webcam) {
|
||||
JPanel panel = new JPanel(new BorderLayout(10, 10));
|
||||
panel.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
// Screenshot section
|
||||
JPanel screenshotPanel = new JPanel();
|
||||
screenshotPanel.setLayout(new BoxLayout(screenshotPanel, BoxLayout.Y_AXIS));
|
||||
screenshotPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Screenshot",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JPanel screenshotPathPanel = new JPanel(new BorderLayout(5, 5));
|
||||
JTextField screenshotPath = new JTextField(System.getProperty("user.home") + File.separator + "screenshots");
|
||||
JButton browseSS = new JButton("...");
|
||||
browseSS.setPreferredSize(new Dimension(40, 25));
|
||||
browseSS.addActionListener(e -> browseDirectory(screenshotPath, panel));
|
||||
screenshotPathPanel.add(screenshotPath, BorderLayout.CENTER);
|
||||
screenshotPathPanel.add(browseSS, BorderLayout.EAST);
|
||||
|
||||
JButton takeScreenshot = new JButton("Take Screenshot (S)");
|
||||
takeScreenshot.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
takeScreenshot.addActionListener(e -> saveSnapshot(cameraPanel, webcam, screenshotPath.getText(), panel));
|
||||
|
||||
screenshotPanel.add(screenshotPathPanel);
|
||||
screenshotPanel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
screenshotPanel.add(takeScreenshot);
|
||||
|
||||
// Recording section
|
||||
JPanel recordPanel = new JPanel();
|
||||
recordPanel.setLayout(new BoxLayout(recordPanel, BoxLayout.Y_AXIS));
|
||||
recordPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Recording",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JPanel recordPathPanel = new JPanel(new BorderLayout(5, 5));
|
||||
JTextField recordPath = new JTextField(System.getProperty("user.home") + File.separator + "recordings");
|
||||
JButton browseRec = new JButton("...");
|
||||
browseRec.setPreferredSize(new Dimension(40, 25));
|
||||
browseRec.addActionListener(e -> browseDirectory(recordPath, panel));
|
||||
recordPathPanel.add(recordPath, BorderLayout.CENTER);
|
||||
recordPathPanel.add(browseRec, BorderLayout.EAST);
|
||||
|
||||
VideoRecorder recorder = new VideoRecorder();
|
||||
JButton recordButton = new JButton("Start Recording (R)");
|
||||
JLabel recordingStatus = new JLabel("Ready");
|
||||
recordingStatus.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
recordButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
recordButton.addActionListener(e -> {
|
||||
if (!recorder.isRecording()) {
|
||||
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String filename = "recording_" + webcam.getName().replaceAll("[^a-zA-Z0-9]", "_")
|
||||
+ "_" + timestamp + ".mp4";
|
||||
File dir = new File(recordPath.getText());
|
||||
dir.mkdirs();
|
||||
|
||||
try {
|
||||
recorder.startRecording(cameraPanel, new File(dir, filename));
|
||||
recordButton.setText("Stop Recording");
|
||||
recordingStatus.setText("Recording...");
|
||||
recordingStatus.setForeground(Color.RED);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(panel,
|
||||
"Error starting recording: " + ex.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
File saved = recorder.stopRecording();
|
||||
recordButton.setText("Start Recording (R)");
|
||||
recordingStatus.setText("Saved: " + saved.getName());
|
||||
recordingStatus.setForeground(Color.BLACK);
|
||||
} catch (Exception ex) {
|
||||
recordButton.setText("Start Recording (R)");
|
||||
recordingStatus.setText("Error saving");
|
||||
recordingStatus.setForeground(Color.RED);
|
||||
JOptionPane.showMessageDialog(panel,
|
||||
"Error saving recording: " + ex.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
recordPanel.add(recordPathPanel);
|
||||
recordPanel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
recordPanel.add(recordButton);
|
||||
recordPanel.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||
recordPanel.add(recordingStatus);
|
||||
|
||||
mainPanel.add(screenshotPanel);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
mainPanel.add(recordPanel);
|
||||
mainPanel.add(Box.createVerticalGlue());
|
||||
|
||||
panel.add(mainPanel, BorderLayout.NORTH);
|
||||
JButton takeScreenshot = new JButton("Take Screenshot");
|
||||
takeScreenshot.addActionListener(e -> saveSnapshot(cameraPanel, webcam, System.getProperty("user.home"), panel));
|
||||
|
||||
panel.add(takeScreenshot, BorderLayout.NORTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createEffectsPanel(CameraPanel cameraPanel) {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
panel.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
|
||||
JPanel transformPanel = new JPanel(new GridLayout(0, 1, 5, 5));
|
||||
transformPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Transform",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JCheckBox mirrorCheck = new JCheckBox("Mirror Horizontal");
|
||||
JCheckBox flipCheck = new JCheckBox("Flip Vertical");
|
||||
JCheckBox rotateCheck = new JCheckBox("Rotate 180°");
|
||||
|
||||
mirrorCheck.addActionListener(e -> cameraPanel.setMirror(mirrorCheck.isSelected()));
|
||||
flipCheck.addActionListener(e -> cameraPanel.setFlip(flipCheck.isSelected()));
|
||||
rotateCheck.addActionListener(e -> cameraPanel.setRotate(rotateCheck.isSelected()));
|
||||
|
||||
transformPanel.add(mirrorCheck);
|
||||
transformPanel.add(flipCheck);
|
||||
transformPanel.add(rotateCheck);
|
||||
|
||||
JPanel colorPanel = new JPanel();
|
||||
colorPanel.setLayout(new BoxLayout(colorPanel, BoxLayout.Y_AXIS));
|
||||
colorPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Color",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JCheckBox grayscaleCheck = new JCheckBox("Grayscale");
|
||||
JCheckBox invertCheck = new JCheckBox("Invert Colors");
|
||||
|
||||
grayscaleCheck.addActionListener(e -> cameraPanel.setGrayscale(grayscaleCheck.isSelected()));
|
||||
invertCheck.addActionListener(e -> cameraPanel.setInvert(invertCheck.isSelected()));
|
||||
|
||||
colorPanel.add(grayscaleCheck);
|
||||
colorPanel.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||
colorPanel.add(invertCheck);
|
||||
|
||||
JPanel brightnessPanel = new JPanel();
|
||||
brightnessPanel.setLayout(new BoxLayout(brightnessPanel, BoxLayout.Y_AXIS));
|
||||
brightnessPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Adjustments",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JPanel brightnessSliderPanel = new JPanel(new BorderLayout());
|
||||
JLabel brightnessLabel = new JLabel("Brightness: 0");
|
||||
JSlider brightnessSlider = new JSlider(-100, 100, 0);
|
||||
brightnessSlider.addChangeListener(e -> {
|
||||
int value = brightnessSlider.getValue();
|
||||
brightnessLabel.setText("Brightness: " + value);
|
||||
cameraPanel.setBrightness(value);
|
||||
});
|
||||
brightnessSliderPanel.add(brightnessLabel, BorderLayout.NORTH);
|
||||
brightnessSliderPanel.add(brightnessSlider, BorderLayout.CENTER);
|
||||
|
||||
JPanel contrastSliderPanel = new JPanel(new BorderLayout());
|
||||
JLabel contrastLabel = new JLabel("Contrast: 1.0");
|
||||
JSlider contrastSlider = new JSlider(0, 200, 100);
|
||||
contrastSlider.addChangeListener(e -> {
|
||||
float value = contrastSlider.getValue() / 100f;
|
||||
contrastLabel.setText("Contrast: " + String.format("%.1f", value));
|
||||
cameraPanel.setContrast(value);
|
||||
});
|
||||
contrastSliderPanel.add(contrastLabel, BorderLayout.NORTH);
|
||||
contrastSliderPanel.add(contrastSlider, BorderLayout.CENTER);
|
||||
|
||||
brightnessPanel.add(brightnessSliderPanel);
|
||||
brightnessPanel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
brightnessPanel.add(contrastSliderPanel);
|
||||
|
||||
JButton resetButton = new JButton("Reset All Effects");
|
||||
resetButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
resetButton.addActionListener(e -> {
|
||||
mirrorCheck.setSelected(false);
|
||||
flipCheck.setSelected(false);
|
||||
rotateCheck.setSelected(false);
|
||||
grayscaleCheck.setSelected(false);
|
||||
invertCheck.setSelected(false);
|
||||
brightnessSlider.setValue(0);
|
||||
contrastSlider.setValue(100);
|
||||
cameraPanel.resetEffects();
|
||||
});
|
||||
|
||||
panel.add(transformPanel);
|
||||
panel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
panel.add(colorPanel);
|
||||
panel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
panel.add(brightnessPanel);
|
||||
panel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
panel.add(resetButton);
|
||||
panel.add(Box.createVerticalGlue());
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void browseDirectory(JTextField field, Component parent) {
|
||||
JFileChooser chooser = new JFileChooser(field.getText());
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) {
|
||||
field.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSnapshot(CameraPanel cameraPanel, Webcam webcam, String directory, Component parent) {
|
||||
BufferedImage img = cameraPanel.getCurrentProcessedImage();
|
||||
if (img != null) {
|
||||
try {
|
||||
File dir = new File(directory);
|
||||
dir.mkdirs();
|
||||
|
||||
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String filename = "screenshot_" + webcam.getName().replaceAll("[^a-zA-Z0-9]", "_")
|
||||
+ "_" + timestamp + ".png";
|
||||
File file = new File(dir, filename);
|
||||
File file = new File(directory, "snap.png");
|
||||
ImageIO.write(img, "PNG", file);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(parent,
|
||||
"Error: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,7 +220,69 @@ public class SwingIFrame {
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
SwingIFrame dashboard = new SwingIFrame();
|
||||
// Example usage: dashboard.addCameraInternalFrame(Webcam.getDefault());
|
||||
dashboard.show();
|
||||
});
|
||||
}
|
||||
|
||||
// --- Inner Classes for Blender Styling ---
|
||||
|
||||
static class BlenderDesktopPane extends JDesktopPane {
|
||||
private final Map<JInternalFrame, EffectsPanelFrame> connections;
|
||||
|
||||
public BlenderDesktopPane(Map<JInternalFrame, EffectsPanelFrame> connections) {
|
||||
this.connections = connections;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintChildren(Graphics g) {
|
||||
super.paintChildren(g);
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
for (Map.Entry<JInternalFrame, EffectsPanelFrame> entry : connections.entrySet()) {
|
||||
JInternalFrame camera = entry.getKey();
|
||||
EffectsPanelFrame effects = entry.getValue();
|
||||
|
||||
if (camera.isVisible() && effects.isVisible() && !camera.isIcon() && !effects.isIcon()) {
|
||||
drawBezierConnection(g2d, camera, effects);
|
||||
}
|
||||
}
|
||||
g2d.dispose();
|
||||
}
|
||||
|
||||
private void drawBezierConnection(Graphics2D g2d, JInternalFrame from, JInternalFrame to) {
|
||||
Rectangle f = from.getBounds();
|
||||
Rectangle t = to.getBounds();
|
||||
|
||||
int x1 = f.x + f.width;
|
||||
int y1 = f.y + f.height / 2;
|
||||
int x2 = t.x;
|
||||
int y2 = t.y + t.height / 2;
|
||||
|
||||
int ctrlOffset = Math.min(Math.abs(x2 - x1) / 2, 150);
|
||||
CubicCurve2D curve = new CubicCurve2D.Double(x1, y1, x1 + ctrlOffset, y1, x2 - ctrlOffset, y2, x2, y2);
|
||||
|
||||
g2d.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
g2d.setColor(new Color(100, 200, 255, 200));
|
||||
g2d.draw(curve);
|
||||
|
||||
g2d.fillOval(x1 - 5, y1 - 5, 10, 10);
|
||||
g2d.fillOval(x2 - 5, y2 - 5, 10, 10);
|
||||
}
|
||||
}
|
||||
|
||||
static class EffectsPanelFrame extends JInternalFrame {
|
||||
public EffectsPanelFrame(String title, CameraPanel cameraPanel, JInternalFrame cameraFrame) {
|
||||
super(title, true, true, true, true);
|
||||
add(new FilterPanel(cameraPanel));
|
||||
|
||||
// Repaint curves when moving or iconifying
|
||||
addComponentListener(new java.awt.event.ComponentAdapter() {
|
||||
@Override public void componentMoved(java.awt.event.ComponentEvent e) {
|
||||
if(getDesktopPane() != null) getDesktopPane().repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user