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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user