Skip to content
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
.PHONY: all corpus
.PHONY: all run
all:
bazel test //... //:fluxengine
bazel run //java/com/cowlark/fluxengine/buildtools:mkdocindex -- $(PWD)/README.md
rm $(PWD)/doc/disk-*.md
bazel run //java/com/cowlark/fluxengine/buildtools:mkdoc -- $(PWD)/doc


run:
bazel run //:fluxengine -- gui --jvm_flag=-Dorg.slf4j.simpleLogger.defaultLogLevel=info
57 changes: 43 additions & 14 deletions java/com/cowlark/fluxengine/gui/ApplicationFrame.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.cowlark.fluxengine.gui;

import static com.cowlark.fluxengine.gui.UiUtils.EDT_EXECUTOR;
import static com.cowlark.fluxengine.gui.PreferencesReaderWriter.WORKFLOW;
import static com.cowlark.fluxengine.gui.UiUtils.EDT_EXECUTOR;
import static com.cowlark.fluxengine.gui.UiUtils.createSplitButton;
import static com.cowlark.fluxengine.gui.UiUtils.sizePts;
import static swingtree.UI.Axis.HORIZONTAL;
Expand All @@ -18,6 +18,7 @@
import static swingtree.UILayoutConstants.TOP;

import com.cowlark.fluxengine.gui.UiUtils.SplitPanelAction;
import io.reactivex.rxjava3.disposables.Disposable;
import org.jspecify.annotations.NonNull;
import sprouts.Val;
import sprouts.Var;
Expand Down Expand Up @@ -47,8 +48,11 @@ public class ApplicationFrame extends JFrame
private final VisualiserPanel visualiserPanel;
private final ImagePanel imagePanel;
private final LogPanel logPanel;
private final FluxPanel fluxPanel;
private final SummaryPanel summaryPanel;
private final FilesystemPanel filesystemPanel;
private final JTabbedPane centreTabs;
private Disposable fluxViewSubscription;

private final ImageViewModel model;
private final Var<Workflow> currentWorkflow;
Expand All @@ -59,6 +63,7 @@ public class ApplicationFrame extends JFrame
currentWorkflow = preferences.makeEnumPreference(WORKFLOW, Workflow.DISK_READING);
summaryPanel = new SummaryPanel(model);
logPanel = new LogPanel(model);
fluxPanel = new FluxPanel(model);
imagePanel = new ImagePanel(model);
visualiserPanel = new VisualiserPanel(model);
configurationPanel = new ConfigurationPanel(model);
Expand All @@ -78,19 +83,20 @@ public class ApplicationFrame extends JFrame
.withVerticalScrollBarPolicy(UI.Active.ALWAYS)
.add(of(configurationPanel))));

UIForSplitPane<JSplitPane> topPane = splitPane(HORIZONTAL).peek(pane -> {
pane.setResizeWeight(1.0);
})
.add(
TOP,
tabbedPane().withMinSize(100, 0)
.add(tab("Image").add(of(imagePanel)))
.add(tab("Log").add(of(logPanel)))
.add(tab("Files").add(of(filesystemPanel))))
.add(
BOTTOM,
tabbedPane().withMinSize(100, 0)
.add(tab("Visualiser").add(of(visualiserPanel))));
/* Retain the centre tabs so flux-view requests can select their destination. */
centreTabs = tabbedPane().withMinSize(100, 0)
.add(tab("Image").add(of(imagePanel)))
.add(tab("Log").add(of(logPanel)))
.add(tab("Flux").add(of(fluxPanel)))
.add(tab("Files").add(of(filesystemPanel))).get(JTabbedPane.class);

UIForSplitPane<JSplitPane> topPane =
splitPane(HORIZONTAL).peek(pane -> pane.setResizeWeight(1.0))
.add(TOP, of(centreTabs))
.add(
BOTTOM,
tabbedPane().withMinSize(100, 0)
.add(tab("Visualiser").add(of(visualiserPanel))));

UIForPanel<JPanel> controlPanel = panel("wrap 4, center, nogrid")
.add("aligny center", label("I want to"))
Expand Down Expand Up @@ -148,6 +154,29 @@ public class ApplicationFrame extends JFrame
.add(outerSplit);
}

/* Activate the flux tab for each request while this frame is attached. */
@Override
public void addNotify()
{
super.addNotify();
fluxViewSubscription = model
.getFluxViewRequests()
.observeOn(UiUtils.EDT)
.subscribe(tracks -> centreTabs.setSelectedComponent(fluxPanel));
}

/* Stop listening when the window is disposed, allowing it to be collected. */
@Override
public void removeNotify()
{
if (fluxViewSubscription != null)
{
fluxViewSubscription.dispose();
fluxViewSubscription = null;
}
super.removeNotify();
}

private static @NonNull ViewSupplier<Workflow> createControlPanelCard(
ImageViewModel model)
{
Expand Down
4 changes: 4 additions & 0 deletions java/com/cowlark/fluxengine/gui/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ java_library(
"//java/com/cowlark/fluxengine/config:config_java_proto",
"//java/com/cowlark/fluxengine/core",
"//java/com/cowlark/fluxengine/data",
"//java/com/cowlark/fluxengine/decoders",
"//java/com/cowlark/fluxengine/decoders:decoders_java_proto",
"//java/com/cowlark/fluxengine/external",
"//java/com/cowlark/fluxengine/fluxsource",
"//java/com/cowlark/fluxengine/imagewriter",
"//java/com/cowlark/fluxengine/usb",
"//java/com/cowlark/fluxengine/vfs",
"//java/com/cowlark/fluxengine/wiring",
"@maven//:com_formdev_flatlaf",
"@maven//:com_formdev_flatlaf_swingx",
"@maven//:com_google_guava_guava",
Expand Down
120 changes: 120 additions & 0 deletions java/com/cowlark/fluxengine/gui/FluxPanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.cowlark.fluxengine.gui;

import static swingtree.UI.of;
import static swingtree.UIFactoryMethods.label;

import com.cowlark.fluxengine.data.Track;
import com.google.common.collect.ImmutableList;
import io.reactivex.rxjava3.disposables.Disposable;
import swingtree.UI.HorizontalAlignment;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import java.awt.Dimension;
import java.awt.Rectangle;

public class FluxPanel extends JPanel
{
private final ImageViewModel model;
private final ViewerRows viewerRows;
private Disposable fluxViewSubscription;

public FluxPanel(ImageViewModel model)
{
/* Create a flux viewer sharing the tab's model. */
this.model = model;
viewerRows = new ViewerRows();

/* Stack fixed-height viewers in rows, retaining the centre-window panels' padding. */
of(viewerRows).withLayout("fillx, wrap 1, insets 3, gapy 2pt, aligny top")
.add("growx, pushx", label("To add flux views, select a track below")
.withHorizontalAlignment(HorizontalAlignment.CENTER));

/* Scroll overflowing rows vertically while fitting them to the viewport width. */
JScrollPane scrollPane = new JScrollPane(
viewerRows,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
of(this).withLayout("fill, insets 0").add("grow, push", of(scrollPane));
}

/* Listen while attached, delivering viewer requests on the Swing event thread. */
@Override
public void addNotify()
{
super.addNotify();
fluxViewSubscription = model.getFluxViewRequests()
.observeOn(UiUtils.EDT)
.subscribe(this::openFluxView);
}

/* Release the model's reference to this panel when it leaves the component hierarchy. */
@Override
public void removeNotify()
{
if (fluxViewSubscription != null)
{
fluxViewSubscription.dispose();
fluxViewSubscription = null;
}
super.removeNotify();
}

/* Handle a request to display captured tracks. */
private void openFluxView(ImmutableList<Track> tracks)
{
/* Insert new rows immediately before the instruction label. */
FluxViewerPanel viewer = new FluxViewerPanel(model, tracks);
viewer.addInternalFrameListener(new InternalFrameAdapter()
{
@Override
public void internalFrameClosed(InternalFrameEvent event)
{
viewerRows.remove(viewer);
viewerRows.revalidate();
viewerRows.repaint();
}
});
viewerRows.add(viewer, "growx, pushx", viewerRows.getComponentCount() - 1);
viewerRows.revalidate();
viewerRows.repaint();
}

/* Give every viewer the same close behavior, including the initial row. */

/* Track the available width while preserving the full combined height of the rows. */
private static class ViewerRows extends JPanel implements Scrollable
{
@Override
public Dimension getPreferredScrollableViewportSize()
{
return getPreferredSize();
}

@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
{
return UiUtils.pointsToPixels(16);
}

@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
{
return Math.max(1, visibleRect.height - UiUtils.pointsToPixels(16));
}

@Override
public boolean getScrollableTracksViewportWidth()
{
return true;
}

@Override
public boolean getScrollableTracksViewportHeight()
{
return false;
}
}
}
Loading
Loading