Skip to content

Drag and Drop

MorquinDevlar edited this page Sep 8, 2026 · 5 revisions

Drag and Drop

MDW widgets are moved, docked, grouped, and resized by dragging. A short movement threshold (5 pixels by default, dragThreshold) distinguishes clicks from drags, so a plain click never repositions anything.

Moving a floating widget

Drag a floating widget by its title bar to move it. The widget follows the cursor and stays floating - a title-bar drag never docks it. To dock a widget, drag its tab (see below).

Snapping

A dragged float snaps flush when an edge comes within floatSnapDistance (8 pixels) of something to line up with:

  • the main console area's own edges - the window less the visible sidebars, the header, the prompt bar and any chrome bars;
  • the edges of the other floating groups, both ways round: left to left and right to right so two panels line up in a column, and right to left or bottom to top so they sit side by side or stacked.

The right-hand edge stops short of the main console's scrollbar (mainScrollBarWidth), so a float snapped to that side never covers it.

A panel already joined to another (see below) snaps only against the panel it is joined to; one that is joined to nothing snaps against every float on screen. That is what keeps a column unambiguous - without it, a panel with two misaligned panels above it gets a left-edge target from each, a few pixels apart, and nothing tells you which one you landed on.

The two axes are decided independently, so a drag can snap to the top edge while staying free horizontally. The nearest target wins, and the container's edges are weighed before the neighbours' - against a float already sitting flush at an edge, both land on the same line rather than a pixel apart. Set floatSnapDistance to 0 to turn snapping off and drag by the pixel.

Resizing snaps as well. Drag a floating panel's border and that border lands on the same lines: the area's edge, a neighbour's matching edge, or a gap off its facing one. Pulling a panel's right border out to the right border of the one above it therefore gives the two the same width. Corners snap each axis independently.

Nothing lands truly flush. Against an area edge a float stops floatSnapInset (5) short, and two floats snapped against each other keep floatSnapGap (7) between them - the two figures are chosen so a column of snapped panels sits the same distance apart as the top one sits off the header. The line-up alignments (left to left, right to right) take no gap, being the same edge on both panels.

Attached and stuck floats

A float left sitting on one of the main console area's edges is attached to it, and lights its resize border along that edge to say so - that side only, so the highlight names the edge rather than ringing the panel. An attached float travels with its edge when the chrome underneath moves: a sidebar dragged wider or toggled off, the prompt bar hidden, a chrome bar appearing, the window resized. Each axis attaches on its own, so a panel on the right edge follows the sidebar in while keeping its own distance from the top. Drag it clear and it is an ordinary float again.

Two floats left sitting against each other are joined: both light the borders where they meet, and one of them travels with the other when that one is dragged, resized, or carried along by an edge of its own. Exactly one of a joined pair follows:

  • A panel attached to an edge is boss. It never follows on the axis it is attached on, so nothing you snap onto a sidebar-, top- or bottom-attached panel can drag it off its edge - and the pair reverses, the attached panel leading. A panel resting on a bottom-attached one follows it up when you hide the prompt bar, which is how a column grows off the bottom of the screen.
  • Otherwise the panel on the far side follows: a column hangs off its top panel, a row off its leftmost.
  • Two panels both attached to edges on that axis are both boss, and neither follows.

So in the ordinary case:

  • Drag the anchor and the whole group comes along, chaining down a column of any depth.
  • Drag a follower and it comes away on its own - that is how you take a panel back out of a group.
  • A follower tracks the edge it is joined to: drag the anchor's bottom border down and the panel below it moves down; drag its top border up and nothing below moves.
  • On the other axis a follower keeps its own offset, so a left-aligned column stays aligned - unless an edge holds it there, in which case only the joined axis moves.

Only the touching snaps join. Two panels merely lined up left-to-left across the screen are aligned, not joined, and panels that do not overlap on the other axis never catch each other.

Docking and the tab drag

Every widget carries a tab (its title reads as a tab even when it is on its own). Dragging that tab is how you dock, undock, and combine widgets:

  • Drag a tab over a sidebar to dock it there.
  • Drag a docked widget's tab away from the dock to tear it out and float it.
  • Drop a tab onto another widget to place it beside, above, below, or grouped with that widget.

While dragging over a sidebar, MDW shows where the widget will land:

  • Dock highlight - the target sidebar lights up (dockHighlight color) when the tab is over it.
  • Drop preview block - a grey block shows the exact slot the widget will take.
  • End band - when the tab is over a sidebar but not over any specific widget, a band at the top or bottom edge (dropEndBandHeight, 48px) previews docking at that end of the column.

Dropping somewhere that is not a dock zone leaves the widget floating.

Where a drop lands on a target widget

When you drop a tab onto an existing widget, the region of that widget you release over decides the result:

  • Tab bar (the strip across the top): the two widgets are combined into a tabbed group. A ghost tab shows where the new tab will slot in.
  • Left or right edge: the widget is placed side-by-side in the same row.
  • Top or bottom edge: the widget is inserted as a new row above or below.
  • Bottom of a multi-column row: the widget is stacked as a sub-column.

Side-by-side docking

Widgets in the same dock can share a row. Drop a widget on the left or right edge of an existing widget to place them side-by-side; the new widget matches the target's height.

Once side-by-side, a splitter appears between the widgets. Drag it horizontally to adjust their relative widths (down to minWidgetWidth, 50px). The width ratios are preserved in the saved layout.

Grouping widgets

Dropping a tab onto another widget's tab bar combines the two into a single tabbed group that shares one dock slot. This is a distinct feature with its own page - see Grouped Widgets for grouping, ungrouping, and the mdw.Stack API.

Resizing

Docked widgets have a bottom resize handle for adjusting height. When resizing within a shared row, heights snap to adjacent widgets and column edges when they are within snapThreshold (15px). The bottom widget in a dock column fills the remaining height automatically, so it has no manual bottom handle.

Dock edges have splitters that resize the whole sidebar width, clamped between minDockWidth (150) and maxDockWidth (1000). Their grab area extends dockGap pixels into the gap beside the main display, so the thin line is easy to catch; the prompt bar's separator works the same way.

Floating widgets have resize handles on all four edges and four corners. Drag an edge to resize one dimension or a corner to resize both. Minimum dimensions are enforced (minFloatingWidth, minWidgetHeight), and growth is capped so the widget stays inside the main window.

Clone this wiki locally