Remember how tall the user made the info panel

Kept as a share of the tree/info column, so it survives new tabs, a resized
window and the next session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 08:11:29 +00:00
parent 02533b543d
commit a01c598fe6
3 changed files with 32 additions and 0 deletions

View File

@@ -139,6 +139,8 @@ public final class Settings {
public boolean showStatusBar = true; public boolean showStatusBar = true;
/** Whether the Master Volume slider is shown on the toolbar. */ /** Whether the Master Volume slider is shown on the toolbar. */
public boolean showMasterVolumeSlider = true; public boolean showMasterVolumeSlider = true;
/** Share of the tree/info column the tree takes, i.e. where their divider sits. */
public double infoSplitRatio = 0.68;
/** Which actions make a sound, and which ones are important enough to survive muting. */ /** Which actions make a sound, and which ones are important enough to survive muting. */
public final NotificationSettings notifications = new NotificationSettings(); public final NotificationSettings notifications = new NotificationSettings();
@@ -230,6 +232,7 @@ public final class Settings {
appearance = parseAppearance(props.getProperty("appearance"), appearance); appearance = parseAppearance(props.getProperty("appearance"), appearance);
showStatusBar = parseB(props.getProperty("showStatusBar"), showStatusBar); showStatusBar = parseB(props.getProperty("showStatusBar"), showStatusBar);
showMasterVolumeSlider = parseB(props.getProperty("showMasterVolumeSlider"), showMasterVolumeSlider); showMasterVolumeSlider = parseB(props.getProperty("showMasterVolumeSlider"), showMasterVolumeSlider);
infoSplitRatio = parseD(props.getProperty("infoSplitRatio"), infoSplitRatio);
notifications.load(props); notifications.load(props);
} }
@@ -272,6 +275,7 @@ public final class Settings {
props.setProperty("appearance", appearance.name()); props.setProperty("appearance", appearance.name());
props.setProperty("showStatusBar", Boolean.toString(showStatusBar)); props.setProperty("showStatusBar", Boolean.toString(showStatusBar));
props.setProperty("showMasterVolumeSlider", Boolean.toString(showMasterVolumeSlider)); props.setProperty("showMasterVolumeSlider", Boolean.toString(showMasterVolumeSlider));
props.setProperty("infoSplitRatio", Double.toString(infoSplitRatio));
notifications.store(props); notifications.store(props);
} }

View File

@@ -723,6 +723,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
private void shutdown() { private void shutdown() {
if (!shuttingDown.compareAndSet(false, true)) return; if (!shuttingDown.compareAndSet(false, true)) return;
if (statusTimer != null) statusTimer.stop(); if (statusTimer != null) statusTimer.stop();
settings.save(); // keeps the layout the user left behind (info panel height, …)
try { try {
Runtime.getRuntime().removeShutdownHook(shutdownHook); Runtime.getRuntime().removeShutdownHook(shutdownHook);
} catch (IllegalStateException ignored) { } catch (IllegalStateException ignored) {

View File

@@ -16,6 +16,8 @@ import javax.swing.SwingUtilities;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
/** /**
* One server connection and the views bound to it (tree, info and chat). The * One server connection and the views bound to it (tree, info and chat). The
@@ -45,6 +47,8 @@ final class ServerTab implements ServerTabConnectionEvents.Listener {
private JSplitPane leftColumn; private JSplitPane leftColumn;
private int normalDividerSize; private int normalDividerSize;
private int savedDividerLocation = -1; private int savedDividerLocation = -1;
/** Whether the saved divider share has been applied; before that, layout is not the user's doing. */
private boolean splitRestored;
/** Label shown in the tab bar: the server name once known, the address before that. */ /** Label shown in the tab bar: the server name once known, the address before that. */
private String title = "New connection"; private String title = "New connection";
@@ -109,6 +113,16 @@ final class ServerTab implements ServerTabConnectionEvents.Listener {
leftColumn.setResizeWeight(0.68); leftColumn.setResizeWeight(0.68);
leftColumn.setContinuousLayout(true); leftColumn.setContinuousLayout(true);
normalDividerSize = leftColumn.getDividerSize(); normalDividerSize = leftColumn.getDividerSize();
// The info panel keeps the height the user gave it, in this tab and the next session.
leftColumn.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (splitRestored) return;
splitRestored = true;
leftColumn.setDividerLocation(settings.infoSplitRatio);
}
});
leftColumn.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, e -> rememberSplit());
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftColumn, chatPanel); JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftColumn, chatPanel);
split.setResizeWeight(0.55); split.setResizeWeight(0.55);
@@ -377,6 +391,19 @@ final class ServerTab implements ServerTabConnectionEvents.Listener {
treeActions.browseFiles(channel); treeActions.browseFiles(channel);
} }
/**
* Records where the user put the divider, as a share of the column. Collapsed and
* pre-layout positions are not the user's choice, so they are left out.
*/
private void rememberSplit() {
if (!splitRestored || !infoPanel.isVisible()) return;
int span = leftColumn.getHeight() - leftColumn.getDividerSize();
if (span <= 0) return;
double ratio = leftColumn.getDividerLocation() / (double) span;
if (ratio <= 0.05 || ratio >= 0.98) return;
settings.infoSplitRatio = ratio;
}
/** Collapses the info panel to nothing while its content lives in the chat tab, or restores it. */ /** Collapses the info panel to nothing while its content lives in the chat tab, or restores it. */
private void setInfoPanelHidden(boolean hidden) { private void setInfoPanelHidden(boolean hidden) {
if (hidden == !infoPanel.isVisible()) return; if (hidden == !infoPanel.isVisible()) return;