fix desktop icon grid gap at right edge: stretch column pitch so right-anchored stacks sit flush
This commit is contained in:
parent
74a442d9fd
commit
eecf8cf339
2 changed files with 56 additions and 20 deletions
|
|
@ -53,11 +53,20 @@ export function useDesktopIcons(
|
|||
const [positions, setPositions] = useState<Record<string, IconPosition>>({});
|
||||
const [dragId, setDragId] = useState<string | null>(null);
|
||||
const cellRef = useRef({ w: DEFAULT_CELL_WIDTH, h: DEFAULT_CELL_HEIGHT });
|
||||
const gridRef = useRef<{ cols: number; rows: number; originX: number; originY: number }>({
|
||||
const gridRef = useRef<{
|
||||
cols: number;
|
||||
rows: number;
|
||||
originX: number;
|
||||
originY: number;
|
||||
pitchX: number;
|
||||
pitchY: number;
|
||||
}>({
|
||||
cols: 1,
|
||||
rows: 1,
|
||||
originX: 10,
|
||||
originY: 12,
|
||||
pitchX: DEFAULT_CELL_WIDTH + GRID_GAP,
|
||||
pitchY: DEFAULT_CELL_HEIGHT + GRID_GAP,
|
||||
});
|
||||
const suppressRef = useRef(false);
|
||||
|
||||
|
|
@ -71,8 +80,10 @@ export function useDesktopIcons(
|
|||
|
||||
/**
|
||||
* Work out the grid from where the launcher stacks actually sit. The icon
|
||||
* width/height (plus the flex gap) is the cell pitch, and the left/top stack
|
||||
* offsets give column 0 / row 0. Falls back to sensible values before mount.
|
||||
* width/height is the cell size and the left/top stack offsets give column 0 /
|
||||
* row 0; the column pitch is stretched a touch so the right-hand stacks stay
|
||||
* flush with the right margin instead of stopping up to a cell short. Falls
|
||||
* back to sensible values before mount.
|
||||
*/
|
||||
const measure = useCallback(() => {
|
||||
const root = rootRef.current;
|
||||
|
|
@ -86,11 +97,13 @@ export function useDesktopIcons(
|
|||
|
||||
// Each stack renders from its own top offset; the left stack defines row 0,
|
||||
// the top-right stack shares it and the bottom-right stack defines the last
|
||||
// row band. Use the smallest top as the grid origin.
|
||||
// row band. Use the smallest top as the grid origin. The right-anchored
|
||||
// stacks also give the true right margin.
|
||||
let cellW = 0;
|
||||
let cellH = 0;
|
||||
let originX = Infinity;
|
||||
let originY = Infinity;
|
||||
let rightMargin = Infinity;
|
||||
|
||||
stacks.forEach((stack) => {
|
||||
const stackRect = stack.getBoundingClientRect();
|
||||
|
|
@ -109,45 +122,60 @@ export function useDesktopIcons(
|
|||
const r = el.getBoundingClientRect();
|
||||
cellW = Math.max(cellW, r.width);
|
||||
cellH = Math.max(cellH, r.height);
|
||||
rightMargin = Math.min(rightMargin, rootRect.right - r.right);
|
||||
});
|
||||
});
|
||||
|
||||
if (!Number.isFinite(originX)) originX = 10;
|
||||
if (!Number.isFinite(originY)) originY = 12;
|
||||
if (!Number.isFinite(rightMargin)) rightMargin = 10;
|
||||
if (cellW <= 0 || cellH <= 0) return;
|
||||
|
||||
const pitchX = cellW + GRID_GAP;
|
||||
const pitchY = cellH + GRID_GAP;
|
||||
const width = root.clientWidth || window.innerWidth;
|
||||
const height = (root.clientHeight || window.innerHeight) - TASKBAR_HEIGHT;
|
||||
|
||||
// Columns tile from the left, but the services/friends stacks are anchored
|
||||
// to the right edge. A fixed pitch leaves up to a full cell of slack on the
|
||||
// right (the remainder of width / pitch), which pulled those icons away from
|
||||
// the border. Stretch the column pitch a hair so the first and last columns
|
||||
// land flush on both margins; the extra gap is a few px and stays uniform.
|
||||
const minPitchX = cellW + GRID_GAP;
|
||||
const span = width - originX - rightMargin;
|
||||
const cols = Math.max(1, Math.floor((span + GRID_GAP) / minPitchX));
|
||||
let pitchX = minPitchX;
|
||||
if (cols > 1) {
|
||||
const stretched = (span - cellW) / (cols - 1);
|
||||
if (stretched > pitchX) pitchX = stretched;
|
||||
}
|
||||
|
||||
cellRef.current = { w: cellW, h: cellH };
|
||||
gridRef.current = {
|
||||
cols: Math.max(1, Math.floor((width - originX) / pitchX)),
|
||||
cols,
|
||||
rows: Math.max(1, Math.floor((height - originY) / pitchY)),
|
||||
originX,
|
||||
originY,
|
||||
pitchX,
|
||||
pitchY,
|
||||
};
|
||||
}, [rootRef]);
|
||||
|
||||
const posToCell = useCallback(
|
||||
(p: IconPosition): Cell => {
|
||||
const { w: cellW, h: cellH } = cellRef.current;
|
||||
const g = gridRef.current;
|
||||
return clampCell(
|
||||
Math.round((p.x - g.originX) / (cellW + GRID_GAP)),
|
||||
Math.round((p.y - g.originY) / (cellH + GRID_GAP))
|
||||
Math.round((p.x - g.originX) / g.pitchX),
|
||||
Math.round((p.y - g.originY) / g.pitchY)
|
||||
);
|
||||
},
|
||||
[clampCell]
|
||||
);
|
||||
|
||||
const cellToPos = useCallback((c: Cell): IconPosition => {
|
||||
const { w: cellW, h: cellH } = cellRef.current;
|
||||
const g = gridRef.current;
|
||||
return {
|
||||
x: g.originX + c.col * (cellW + GRID_GAP),
|
||||
y: g.originY + c.row * (cellH + GRID_GAP),
|
||||
x: g.originX + c.col * g.pitchX,
|
||||
y: g.originY + c.row * g.pitchY,
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
@ -217,7 +245,6 @@ export function useDesktopIcons(
|
|||
const defaultCells = useCallback(
|
||||
(keys: string[]): Record<string, IconPosition> => {
|
||||
const root = rootRef.current;
|
||||
const { w: cellW, h: cellH } = cellRef.current;
|
||||
const g = gridRef.current;
|
||||
const out: Record<string, IconPosition> = {};
|
||||
if (!root) return out;
|
||||
|
|
@ -225,8 +252,8 @@ export function useDesktopIcons(
|
|||
const stacks = Array.from(
|
||||
root.querySelectorAll<HTMLElement>(".win-icons")
|
||||
).filter((el) => el.querySelector("[data-drag-id]"));
|
||||
const pitchX = cellW + GRID_GAP;
|
||||
const pitchY = cellH + GRID_GAP;
|
||||
const pitchX = g.pitchX;
|
||||
const pitchY = g.pitchY;
|
||||
const rootRect = root.getBoundingClientRect();
|
||||
|
||||
// Natural cell for each icon: the one its stack actually renders it in.
|
||||
|
|
@ -267,8 +294,8 @@ export function useDesktopIcons(
|
|||
const { w: cellW, h: cellH } = cellRef.current;
|
||||
const g = gridRef.current;
|
||||
return clampCell(
|
||||
Math.floor((x - g.originX + GRID_GAP / 2) / (cellW + GRID_GAP)),
|
||||
Math.floor((y - g.originY + GRID_GAP / 2) / (cellH + GRID_GAP))
|
||||
Math.floor((x - g.originX + (g.pitchX - cellW) / 2) / g.pitchX),
|
||||
Math.floor((y - g.originY + (g.pitchY - cellH) / 2) / g.pitchY)
|
||||
);
|
||||
},
|
||||
[clampCell]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue