Exploded view remembers where you drag its windows
Ported from BT411's BT_GLASS_LAYOUT (29c502d).
The exploded view's panes are draggable desktop windows, but the
arrangement is recomputed on every launch, so dragging one somewhere
useful never survived the menu-race-menu loop.
RP412MFDLAYOUT persists it to mfd_layout.cfg beside bindings.txt:
off / 0 / unset computed arrangement only, no file (default)
load / restore restore saved positions at startup, never write
save / adjust restore, then rewrite on each finished drag
(WM_EXITSIZEMOVE) and on teardown
One "<title>=x,y,w,h" line per pane. Position is restored and the size
read and discarded: a pane's size follows its content and its button
banks, so letting an old size back in would misshape it after any
geometry change - and this port has changed that geometry twice already.
Load runs after the computed arrangement rather than instead of it, so a
pane the file does not mention simply keeps its computed spot. Only the
exploded view registers: the composited cockpit's panes are chrome-less
children with nothing to drag, so they have no position worth keeping.
RP412 needs no equivalent of BT411's "restored" flag. Its re-snap is
LayoutCockpit on WM_SIZE, which only runs in cockpit mode, so nothing
comes back later to overwrite a hand-placed window.
Verified by round trip: dragged Map to 777,333 in save mode, the file
took all six panes, and a fresh launch in load mode put it physically
back at 777,333. The harness also resized the window while moving it,
which incidentally proved the saved size really is ignored - the pane
came back correctly sized from a cfg that recorded 136x39.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,10 @@ ipch/
|
||||
*.log
|
||||
rpl4.log
|
||||
|
||||
# Where the exploded view's panes were last dragged to (RP412MFDLAYOUT).
|
||||
# Per-machine by nature - it is screen coordinates on somebody's desk.
|
||||
mfd_layout.cfg
|
||||
|
||||
# Build-output static libs that land in lib/ (the two committed dependency
|
||||
# libs, OpenAL32.lib and libsndfile-1.lib, stay tracked).
|
||||
/lib/Munga_L4.lib
|
||||
|
||||
@@ -10,6 +10,52 @@ namespace
|
||||
|
||||
const int buttonGap = 4;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Sticky placement for the exploded view. See L4MFDVIEW.h; ported
|
||||
// from BT411's BT_GLASS_LAYOUT.
|
||||
//---------------------------------------------------------------
|
||||
enum { LayoutOff = 0, LayoutLoad, LayoutSave };
|
||||
|
||||
const char layoutFileName[] = "mfd_layout.cfg";
|
||||
|
||||
// the panes that own a desktop window, so Save can walk them
|
||||
enum { maxLayoutPanes = 8 };
|
||||
MFDSplitView *layoutPanes[maxLayoutPanes];
|
||||
int layoutPaneCount = 0;
|
||||
|
||||
int LayoutMode()
|
||||
{
|
||||
static int mode = -1;
|
||||
if (mode < 0)
|
||||
{
|
||||
const char *setting = getenv("RP412MFDLAYOUT");
|
||||
if (setting == NULL || setting[0] == '\0')
|
||||
{
|
||||
mode = LayoutOff;
|
||||
}
|
||||
else if (!stricmp(setting, "off") || !stricmp(setting, "0"))
|
||||
{
|
||||
mode = LayoutOff;
|
||||
}
|
||||
else if (!stricmp(setting, "load") || !stricmp(setting, "restore"))
|
||||
{
|
||||
mode = LayoutLoad;
|
||||
}
|
||||
else // save / adjust / on / 1
|
||||
{
|
||||
mode = LayoutSave;
|
||||
}
|
||||
|
||||
if (mode != LayoutOff)
|
||||
{
|
||||
DEBUG_STREAM << "MFDLayout: RP412MFDLAYOUT="
|
||||
<< ((mode == LayoutSave) ? "save" : "load")
|
||||
<< " (" << layoutFileName << ")\n" << std::flush;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Button banks run UNDER the glass: a button reaches this far in
|
||||
// from the display edge, but only the indicator strip clears that
|
||||
@@ -108,6 +154,15 @@ namespace
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_EXITSIZEMOVE:
|
||||
//
|
||||
// A drag just finished. Write the whole arrangement now rather
|
||||
// than trusting teardown, so a hard kill still leaves the
|
||||
// layout that was on screen. No-op unless the mode is save.
|
||||
//
|
||||
MFDSplitView_SaveLayout();
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
// Part of the cockpit; just hide it.
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
@@ -148,6 +203,8 @@ MFDSplitView::MFDSplitView(
|
||||
sourceHeight = source_height;
|
||||
buttonCount = 0;
|
||||
pressedIndex = -1;
|
||||
paneTitle = title;
|
||||
ownWindow = (parent == NULL) ? True : False;
|
||||
|
||||
pixels = new unsigned long[source_width * source_height];
|
||||
memset(pixels, 0, source_width * source_height * sizeof(unsigned long));
|
||||
@@ -230,6 +287,13 @@ MFDSplitView::MFDSplitView(
|
||||
{
|
||||
SetWindowLongPtrA((HWND) window, GWLP_USERDATA, (LONG_PTR) this);
|
||||
ShowWindow((HWND) window, SW_SHOWNOACTIVATE);
|
||||
|
||||
// only the exploded view's own windows can be dragged, so only
|
||||
// those have a position worth remembering
|
||||
if (ownWindow && layoutPaneCount < maxLayoutPanes)
|
||||
{
|
||||
layoutPanes[layoutPaneCount++] = this;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -241,6 +305,17 @@ MFDSplitView::~MFDSplitView()
|
||||
{
|
||||
Check_Pointer(this);
|
||||
|
||||
// out of the layout registry before the window goes
|
||||
for (int i = 0; i < layoutPaneCount; ++i)
|
||||
{
|
||||
if (layoutPanes[i] == this)
|
||||
{
|
||||
layoutPanes[i] = layoutPanes[--layoutPaneCount];
|
||||
layoutPanes[layoutPaneCount] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window != NULL)
|
||||
{
|
||||
SetWindowLongPtrA((HWND) window, GWLP_USERDATA, 0);
|
||||
@@ -259,6 +334,139 @@ Logical
|
||||
return pixels != NULL;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Sticky placement: restore saved positions over the computed ones.
|
||||
//
|
||||
// Called after the exploded view has built and placed every pane, so a
|
||||
// pane the file does not mention simply keeps where the layout put it.
|
||||
// Position only - the saved size is read and discarded, because a pane's
|
||||
// size comes from its content and its button banks, and letting an old
|
||||
// w,h back in would misshape it after any geometry change.
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
void
|
||||
MFDSplitView_LoadLayout()
|
||||
{
|
||||
if (LayoutMode() == LayoutOff || layoutPaneCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file = fopen(layoutFileName, "rt");
|
||||
if (file == NULL)
|
||||
{
|
||||
DEBUG_STREAM << "MFDLayout: no " << layoutFileName
|
||||
<< " yet (using the computed arrangement)\n" << std::flush;
|
||||
return;
|
||||
}
|
||||
|
||||
int restored = 0;
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), file) != NULL)
|
||||
{
|
||||
char *cursor = line;
|
||||
while (*cursor == ' ' || *cursor == '\t')
|
||||
{
|
||||
++cursor;
|
||||
}
|
||||
if (*cursor == '#' || *cursor == '\r' || *cursor == '\n' || *cursor == '\0')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// titles carry no '=', so the first one splits the line
|
||||
char *equals = strchr(cursor, '=');
|
||||
if (equals == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
*equals = '\0';
|
||||
char *end = equals;
|
||||
while (end > cursor && (end[-1] == ' ' || end[-1] == '\t'))
|
||||
{
|
||||
--end;
|
||||
}
|
||||
*end = '\0';
|
||||
|
||||
int x = 0, y = 0, w = 0, h = 0;
|
||||
if (sscanf(equals + 1, "%d,%d,%d,%d", &x, &y, &w, &h) < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < layoutPaneCount; ++i)
|
||||
{
|
||||
MFDSplitView *pane = layoutPanes[i];
|
||||
if (pane != NULL && pane->Title() != NULL &&
|
||||
!strcmp(pane->Title(), cursor))
|
||||
{
|
||||
pane->SetPosition(x, y);
|
||||
++restored;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
DEBUG_STREAM << "MFDLayout: restored " << restored << " pane position(s) from "
|
||||
<< layoutFileName << "\n" << std::flush;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Write where every open pane currently is. The whole file each time - it
|
||||
// is a handful of lines, and a rewrite leaves nothing half-written for a
|
||||
// hard kill to catch.
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
void
|
||||
MFDSplitView_SaveLayout()
|
||||
{
|
||||
if (LayoutMode() != LayoutSave || layoutPaneCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file = fopen(layoutFileName, "wt");
|
||||
if (file == NULL)
|
||||
{
|
||||
DEBUG_STREAM << "MFDLayout: could not write " << layoutFileName
|
||||
<< "\n" << std::flush;
|
||||
return;
|
||||
}
|
||||
|
||||
fputs("# RP412 exploded-view pane placement. RP412MFDLAYOUT=save writes\n"
|
||||
"# this on each finished drag and on exit; =load restores it.\n"
|
||||
"# <title>=<x>,<y>,<w>,<h> - the size is written for reference and\n"
|
||||
"# ignored on load.\n", file);
|
||||
|
||||
int wrote = 0;
|
||||
for (int i = 0; i < layoutPaneCount; ++i)
|
||||
{
|
||||
MFDSplitView *pane = layoutPanes[i];
|
||||
if (pane == NULL || pane->Title() == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
HWND pane_window = (HWND) pane->Window();
|
||||
if (pane_window == NULL || !IsWindow(pane_window))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
RECT bounds;
|
||||
if (!GetWindowRect(pane_window, &bounds))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
fprintf(file, "%s=%ld,%ld,%ld,%ld\n", pane->Title(),
|
||||
(long) bounds.left, (long) bounds.top,
|
||||
(long) (bounds.right - bounds.left),
|
||||
(long) (bounds.bottom - bounds.top));
|
||||
++wrote;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
DEBUG_STREAM << "MFDLayout: saved " << wrote << " pane position(s) to "
|
||||
<< layoutFileName << "\n" << std::flush;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Compute the display rectangle and the button rectangles, growing the
|
||||
// client area to make room for the strips.
|
||||
|
||||
@@ -77,6 +77,14 @@ public:
|
||||
void
|
||||
Hide();
|
||||
|
||||
// caption and HWND, for the sticky-placement file
|
||||
const char *
|
||||
Title() const
|
||||
{ return paneTitle; }
|
||||
void *
|
||||
Window()
|
||||
{ return window; }
|
||||
|
||||
~MFDSplitView();
|
||||
|
||||
Logical
|
||||
@@ -147,4 +155,42 @@ protected:
|
||||
int
|
||||
buttonAnchorA,
|
||||
buttonAnchorB;
|
||||
|
||||
// the window caption, and the key this pane is saved under
|
||||
const char
|
||||
*paneTitle;
|
||||
// True for a pane of its own on the desktop (the exploded view); the
|
||||
// composited cockpit's panes are chrome-less children and cannot be
|
||||
// dragged, so there is nothing to remember for them.
|
||||
Logical
|
||||
ownWindow;
|
||||
};
|
||||
|
||||
//########################################################################
|
||||
// Sticky window placement for the exploded view (L4MFDSPLIT=2).
|
||||
//
|
||||
// Those panes are draggable desktop windows, but the arrangement is
|
||||
// recomputed on every launch, so dragging one somewhere useful never
|
||||
// survived the menu-race-menu loop. RP412MFDLAYOUT persists it to
|
||||
// mfd_layout.cfg beside bindings.txt:
|
||||
//
|
||||
// off / 0 / unset computed arrangement only, no file (default)
|
||||
// load / restore restore saved positions at startup, never write
|
||||
// save / adjust restore, then rewrite on each finished drag and
|
||||
// on teardown - the round trip
|
||||
//
|
||||
// One "<title>=x,y,w,h" line per pane. Position is restored and size
|
||||
// ignored: a pane's size follows its content and its button banks, so an
|
||||
// old w,h from a different build must not distort it.
|
||||
//
|
||||
// Ported from BT411's BT_GLASS_LAYOUT.
|
||||
//########################################################################
|
||||
|
||||
// Apply saved positions over the computed ones. Call after the panes are
|
||||
// built and placed.
|
||||
void
|
||||
MFDSplitView_LoadLayout();
|
||||
|
||||
// Write every open pane's position. No-op unless the mode is save.
|
||||
void
|
||||
MFDSplitView_SaveLayout();
|
||||
|
||||
@@ -4457,6 +4457,13 @@ SVGA16::SVGA16(
|
||||
splitView[SplitMFDLowerRight]->SetPosition(
|
||||
work.left + right_x, work.top + bottom_y);
|
||||
splitView[SplitMap]->SetPosition(work.left + map_x, work.top + map_y);
|
||||
|
||||
//
|
||||
// ...and then let RP412MFDLAYOUT put back anywhere they were
|
||||
// dragged to last time. Panes the file does not mention keep the
|
||||
// arrangement just computed above.
|
||||
//
|
||||
MFDSplitView_LoadLayout();
|
||||
}
|
||||
else if (splitViews)
|
||||
{
|
||||
@@ -4863,6 +4870,14 @@ SVGA16::~SVGA16()
|
||||
//---------------------------------------------------------
|
||||
//SVGASetSplitterClock(False);
|
||||
|
||||
//
|
||||
// Backstop for the sticky exploded-view placement: every finished drag
|
||||
// has already been written, but a pane moved and then closed straight
|
||||
// away would otherwise be missed. Must run before the panes go - it
|
||||
// reads their live window rects.
|
||||
//
|
||||
MFDSplitView_SaveLayout();
|
||||
|
||||
for (int view = 0; view < SplitViewCount; ++view)
|
||||
{
|
||||
delete splitView[view];
|
||||
|
||||
@@ -166,6 +166,18 @@ L4PLASMA=SCREEN
|
||||
# 480x640 - decoded exactly as the pod's VDB split them, no downscale).
|
||||
L4MFDSPLIT=1
|
||||
|
||||
# In the exploded view (L4MFDSPLIT=2) each display is a window you can
|
||||
# drag, but the arrangement is recomputed every launch, so moving one
|
||||
# somewhere useful never survived the menu-race-menu loop. This remembers
|
||||
# it, in mfd_layout.cfg beside this file:
|
||||
# off / 0 / unset computed arrangement only, no file (default)
|
||||
# load put the panes back where they were saved
|
||||
# save the same, and re-save on every finished drag
|
||||
# Only positions are remembered - a pane's size follows its content and
|
||||
# its button banks, so an old size is never restored over it. Drag the
|
||||
# displays where you want them once with save, then leave it on load.
|
||||
#RP412MFDLAYOUT=off
|
||||
|
||||
# Size of the six secondary displays in the glass cockpit, as a
|
||||
# percentage of their pod size. The pod bolted them down at one size;
|
||||
# on a big panel there is room to trade viewscreen for instrument, so
|
||||
|
||||
Reference in New Issue
Block a user