Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore

# MacOS
.DS_Store

# User-specific files
*.rsuser
*.suo
Expand Down
59 changes: 52 additions & 7 deletions Origami/Origami/Docking/DockSpace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,15 @@ private void DrawNodeTree(Paper paper, DockNode node, DockNode? parent,
float aw = (w - sp) * node.SplitRatio;
float bw = w - aw - sp;
DrawNodeTree(paper, node.ChildA!, node, x, y, aw, h, fw, theme, m, icons, font);
DrawSplitter(paper, node, x + aw, y, sp, h, true, theme);
DrawSplitter(paper, node, x + aw, y, sp, h, true, theme, icons);
DrawNodeTree(paper, node.ChildB!, node, x + aw + sp, y, bw, h, fw, theme, m, icons, font);
}
else
{
float ah = (h - sp) * node.SplitRatio;
float bh = h - ah - sp;
DrawNodeTree(paper, node.ChildA!, node, x, y, w, ah, fw, theme, m, icons, font);
DrawSplitter(paper, node, x, y + ah, w, sp, false, theme);
DrawSplitter(paper, node, x, y + ah, w, sp, false, theme, icons);
DrawNodeTree(paper, node.ChildB!, node, x, y + ah + sp, w, bh, fw, theme, m, icons, font);
}
}
Expand All @@ -181,14 +181,15 @@ private void DrawNodeTree(Paper paper, DockNode node, DockNode? parent,
// SPLITTER
// ================================================================

private const float SplitterGripLength = 28f;

private void DrawSplitter(Paper paper, DockNode node, float x, float y, float w, float h, bool horiz,
OrigamiTheme theme)
OrigamiTheme theme, OrigamiIcons icons)
{
bool active = _splitterDragNode == node;
paper.Box($"spl_{node.GetHashCode()}")

using (paper.Box($"spl_{node.GetHashCode()}")
.PositionType(PositionType.SelfDirected).Position(x, y).Size(w, h)
.Hovered.BackgroundColor(theme.Primary.C500).End()
.Active.BackgroundColor(theme.Primary.C400).End()
.OnDragStart(node, (n, e) => _splitterDragNode = n)
.OnDragging(node, (n, e) =>
{
Expand All @@ -198,7 +199,51 @@ private void DrawSplitter(Paper paper, DockNode node, float x, float y, float w,
n.SplitRatio = Math.Clamp(n.SplitRatio + (horiz ? e.Delta.X : e.Delta.Y) / total, 0.1f, 0.9f);
})
.OnDragEnd(e => _splitterDragNode = null)
.Cursor(horiz ? PaperCursor.ResizeHorizontal : PaperCursor.ResizeVertical);
.Cursor(horiz ? PaperCursor.ResizeHorizontal : PaperCursor.ResizeVertical)
.Enter())
{
// A hairline divider spans the length of the splitter at all times, inset a few
// pixels at each end with rounded caps.
const float lineThickness = 3f;
const float lineInset = 4f;
float lineW = horiz ? lineThickness : w - lineInset * 2f;
float lineH = horiz ? h - lineInset * 2f : lineThickness;
paper.Box($"spl_line_{node.GetHashCode()}")
.PositionType(PositionType.SelfDirected)
.Position((w - lineW) / 2f, (h - lineH) / 2f).Size(lineW, lineH)
.Rounded(lineThickness / 2f)
.IsNotInteractable()
.BackgroundColor(theme.BorderSoft);

// The grip is nested below the splitter so it can stay invisible (no hit-test, no
// background) until the splitter itself is hovered or being dragged.
if (paper.IsParentHovered || active)
{
float thickness = MathF.Min(w, h);
float gripW = horiz ? thickness : SplitterGripLength;
float gripH = horiz ? SplitterGripLength : thickness;

// Slide the grip along the splitter's long axis to track the pointer, clamped so
// it stays fully inside the (inset) hairline.
float axisLen = horiz ? h : w;
float gripLenAlong = horiz ? gripH : gripW;
float localPointer = horiz ? paper.PointerPos.Y - y : paper.PointerPos.X - x;
float alongPos = Math.Clamp(localPointer - gripLenAlong / 2f,
lineInset, axisLen - lineInset - gripLenAlong);

float gx = horiz ? (w - gripW) / 2f : alongPos;
float gy = horiz ? alongPos : (h - gripH) / 2f;

var grip = horiz ? icons.GripVertical : icons.GripHorizontal;
paper.Box($"spl_grip_{node.GetHashCode()}")
.PositionType(PositionType.SelfDirected)
.Position(gx, gy).Size(gripW, gripH)
.Rounded(thickness / 2f)
.IsNotInteractable()
.BackgroundColor(active ? theme.Primary.C400 : theme.Primary.C500)
.Icon(paper, grip, theme.Ink.C300, size: thickness);
}
}
}

private float EstimateSplitSize(DockNode n, bool horiz)
Expand Down
3 changes: 3 additions & 0 deletions Origami/Origami/Icons/OrigamiIconSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public static class OrigamiIconSet
public static readonly SvgIcon List = new("M5.5 4h8M5.5 8h8M5.5 12h8M2.5 4v.1M2.5 8v.1M2.5 12v.1");
// Drag-handle grip: two columns of three dots (tiny strokes render as dots with round caps).
public static readonly SvgIcon Grip = new("M6 4v.1M10 4v.1M6 8v.1M10 8v.1M6 12v.1M10 12v.1");
// Splitter grips: a single line of three dots, oriented to match the splitter bar itself.
public static readonly SvgIcon GripVertical = new("M8 4v.1M8 8v.1M8 12v.1");
public static readonly SvgIcon GripHorizontal = new("M4 8v.1M8 8v.1M12 8v.1");
// Sort: descending-width lines.
public static readonly SvgIcon Sort = new("M3 4.5h10M4.5 8h7M6 11.5h4");

Expand Down
5 changes: 5 additions & 0 deletions Origami/Origami/OrigamiIcons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public sealed class OrigamiIcons
public IOrigamiIcon? Download = OrigamiIconSet.Download;
public IOrigamiIcon? User = OrigamiIconSet.User;

// ── Layout / resize ───────────────────────────────────────
public IOrigamiIcon? GripVertical = OrigamiIconSet.GripVertical;
public IOrigamiIcon? GripHorizontal = OrigamiIconSet.GripHorizontal;

/// <summary>Shallow copy (icons are immutable, so the references are shared).</summary>
public OrigamiIcons Clone() => new()
{
Expand All @@ -65,5 +69,6 @@ public sealed class OrigamiIcons
Search = Search, More = More, Eye = Eye, EyeOff = EyeOff, Plus = Plus, Pencil = Pencil, Trash = Trash, Duplicate = Duplicate,
Folder = Folder, FolderPlus = FolderPlus, File = File, Document = Document, Drive = Drive,
Star = Star, Clock = Clock, Desktop = Desktop, Download = Download, User = User,
GripVertical = GripVertical, GripHorizontal = GripHorizontal,
};
}