Graph: options for depth colours and dot sizes

This commit is contained in:
sarah committed 2026-10-09 09:56:28 +02:00
1 parent cc7d624a57
commit 0372a73dd2
8 files changed
+78 -11

No files matched your search

+4
View File
@@ -53,6 +53,10 @@ DefaultSettings: dict[str, Any] = {
# other one opens a dot).
"GraphShowDirectories": True,
"GraphPanButton": "Right",
# Directory dots: heading colours by depth (off: all Purple), and smaller
# the deeper they are.
"GraphColourByDepth": True,
"GraphShrinkByDepth": False,
}
DefaultThemesDirectory = ApplicationDirectory / "DefaultThemes"
+4
View File
@@ -172,6 +172,10 @@ class AppConfig:
# other one opens a dot).
GraphShowDirectories: bool = True
GraphPanButton: str = "Right"
# Directory dots: heading colours by depth (off: all Mauve), and smaller
# the deeper they are.
GraphColourByDepth: bool = True
GraphShrinkByDepth: bool = False
# Double-click on a file: True opens it in SaraView's own viewer, False
# hands it to the system's default program. The menu always offers the other.
OpenFilesInApp: bool = True
+15 -4
View File
@@ -220,6 +220,8 @@ class Layout:
# Directories go down the heading colours by depth (h1 Mauve, h2 Blue, ...),
# starting again after Red. Files are White.
DepthColours = ["Mauve", "Blue", "Green", "Yellow", "Peach", "Red"]
ShrinkPerDepth = 0.8 # with GraphShrinkByDepth, each level's directory dots are this much smaller
SmallestDirectory = 3 # ...but never smaller than a file's dot
def DirectoryColour(Depth: int) -> str:
@@ -251,6 +253,9 @@ class GraphView(ft.Container):
self.PanButton = "Left" if TheApp.Config.GraphPanButton == "Left" else "Right"
self.OpenButton = "Right" if self.PanButton == "Left" else "Left"
self.ShowDirectories = TheApp.Config.GraphShowDirectories
self.ColourByDepth = TheApp.Config.GraphColourByDepth
self.ShrinkByDepth = TheApp.Config.GraphShrinkByDepth
self.Depths = np.zeros(0) # each dot's depth below the graph's directory
self.Layout: Optional[Layout] = None
self.ScopePath = ""
self.Size = (800.0, 600.0)
@@ -276,7 +281,9 @@ class GraphView(ft.Container):
)
self.CountText = ft.Text("", size=12, color=Palette.Overlay0)
LegendItems: list[ft.Control] = []
if self.ShowDirectories:
if self.ShowDirectories and not self.ColourByDepth:
LegendItems += [ft.Text("●", color=Palette.Mauve), ft.Text("Directory", size=12, color=Palette.Overlay0)]
elif self.ShowDirectories:
LegendItems += [ft.Text("●", color=Palette.Mauve), ft.Text("●", color=Palette.Blue), ft.Text("●", color=Palette.Green),
ft.Text("Directories by depth", size=12, color=Palette.Overlay0)]
LegendItems += [ft.Text("●", color=Palette.White), ft.Text("File", size=12, color=Palette.Overlay0),
@@ -365,8 +372,9 @@ class GraphView(ft.Container):
return
self.Layout = Layout(Graph)
Base = len(Path.split("/")) if Path else 0
self.Colours = [DirectoryColour((len(Node["Id"].split("/")) if Node["Id"] else 0) - Base) if Node["IsDirectory"]
else Palette.White for Node in self.Layout.Nodes]
self.Depths = np.array([(len(Node["Id"].split("/")) if Node["Id"] else 0) - Base for Node in self.Layout.Nodes])
self.Colours = [(DirectoryColour(int(Depth)) if self.ColourByDepth else Palette.Mauve) if Node["IsDirectory"]
else Palette.White for Node, Depth in zip(self.Layout.Nodes, self.Depths)]
self.StatusBox.visible = False
self.StatusBox.update()
# Settle in steps, drawing as it goes, like the web view's animation.
@@ -393,7 +401,10 @@ class GraphView(ft.Container):
Zoom = math.sqrt(Scale)
IsDirectory = np.array([Node["IsDirectory"] for Node in self.Layout.Nodes])
Degree = np.sqrt(self.Layout.Degree)
self.Radius = np.maximum(2, np.where(IsDirectory, 5 + Degree * 1.6, 3 + Degree * 0.8) * Zoom)
DirectoryRadius = 5 + Degree * 1.6
if self.ShrinkByDepth:
DirectoryRadius = np.maximum(SmallestDirectory, DirectoryRadius * ShrinkPerDepth ** self.Depths)
self.Radius = np.maximum(2, np.where(IsDirectory, DirectoryRadius, 3 + Degree * 0.8) * Zoom)
def _Label(self, Number: int, Colour: str) -> cv.Text:
X, Y = self.Screen[Number]
+9 -1
View File
@@ -120,6 +120,10 @@ def ShowSettingsDialog(TheApp: App, OnSaved: Callable[[bool], None], StartCatego
# Graph.
GraphDirectoriesSwitch = ft.Switch(label="Show directories (off: only files and the links between them)",
value=Config.GraphShowDirectories)
ColourByDepthSwitch = ft.Switch(label="Colour directories by depth (off: all purple)",
value=Config.GraphColourByDepth)
ShrinkByDepthSwitch = ft.Switch(label="Smaller directory dots the deeper they are",
value=Config.GraphShrinkByDepth)
PanButtonPicker = ft.RadioGroup(
ft.Column([
ft.Radio(value="Right", label="Right (left-click opens a dot)"),
@@ -217,9 +221,11 @@ def ShowSettingsDialog(TheApp: App, OnSaved: Callable[[bool], None], StartCatego
OwnAppearance,
], spacing=12, scroll=ft.ScrollMode.AUTO),
"Graph": ft.Column([
_Note("The graph view (Ctrl+G): directories in the heading colours by depth, files white, "
_Note("The graph view (Ctrl+G): directories purple or in the heading colours by depth, files white, "
"joined to their directory and to the files that reference them."),
GraphDirectoriesSwitch,
ColourByDepthSwitch,
ShrinkByDepthSwitch,
ft.Divider(height=1, color=Palette.Surface1),
ft.Text("Pan with mouse button"),
PanButtonPicker,
@@ -261,6 +267,8 @@ def ShowSettingsDialog(TheApp: App, OnSaved: Callable[[bool], None], StartCatego
Config.ShowFilesInTree = TreeFilesSwitch.value
Config.MarkdownEditing = MarkdownEditingPicker.value or Config.MarkdownEditing
Config.GraphShowDirectories = GraphDirectoriesSwitch.value
Config.GraphColourByDepth = ColourByDepthSwitch.value
Config.GraphShrinkByDepth = ShrinkByDepthSwitch.value
Config.GraphPanButton = PanButtonPicker.value or Config.GraphPanButton
Config.FontSize = _ReadInt(FontSizeField, Config.FontSize, 8, 32)
Config.Mode = ModePicker.value or Config.Mode
+2
View File
@@ -384,6 +384,8 @@ export default function App() {
DirectoryPath={CurrentDirectoryPath}
ShowDirectories={Settings ? Settings.GraphShowDirectories : true}
PanButton={Settings ? Settings.GraphPanButton : "Right"}
ColourByDepth={Settings ? Settings.GraphColourByDepth : true}
ShrinkByDepth={Settings ? Settings.GraphShrinkByDepth : false}
OnOpenNode={HandleOpenGraphNode}
OnClose={() => SetIsGraphOpen(false)}
/>
+27 -4
View File
@@ -20,6 +20,8 @@ const LabelZoom = 0.7; // below this, only hovered labels are drawn
const ClickSlop = 4; // pixels a click may wander and still count as a click
// Directories by depth, like the headings: h1 Purple, h2 Blue, ...
const DepthRoles = ["Purple", "Blue", "Green", "Yellow", "Orange", "Red"];
const ShrinkPerDepth = 0.8; // with ShrinkByDepth, each level's directory dots are this much smaller
const SmallestDirectory = 3; // ...but never smaller than a file's dot
function ReadRole(Name) {
return getComputedStyle(document.documentElement).getPropertyValue(`--${Name}`).trim();
@@ -132,7 +134,16 @@ function Tick(Nodes, Links, Alpha) {
}
}
export default function GraphPane({ RootName, DirectoryPath, ShowDirectories, PanButton, OnOpenNode, OnClose }) {
export default function GraphPane({
RootName,
DirectoryPath,
ShowDirectories,
PanButton,
ColourByDepth,
ShrinkByDepth,
OnOpenNode,
OnClose,
}) {
const [Scope, SetScope] = useState("Root");
const [Graph, SetGraph] = useState(null);
const [Error, SetError] = useState(null);
@@ -189,7 +200,14 @@ export default function GraphPane({ RootName, DirectoryPath, ShowDirectories, Pa
const BaseDepth = Graph.Path ? Graph.Path.split("/").length : 0;
for (const Node of Nodes) {
const Depth = (Node.Id ? Node.Id.split("/").length : 0) - BaseDepth;
Node.Colour = Node.IsDirectory ? DepthColours[Depth % DepthColours.length] : Colours.File;
if (!Node.IsDirectory) {
Node.Colour = Colours.File;
continue;
}
Node.Colour = ColourByDepth ? DepthColours[Depth % DepthColours.length] : Colours.Directory;
if (ShrinkByDepth) {
Node.Radius = Math.max(SmallestDirectory, Node.Radius * Math.pow(ShrinkPerDepth, Depth));
}
}
// Anything but "Left" (including an old "Middle") pans with Right.
const PanNumber = PanButton === "Left" ? 0 : 2;
@@ -394,7 +412,7 @@ export default function GraphPane({ RootName, DirectoryPath, ShowDirectories, Pa
Canvas.removeEventListener("contextmenu", OnContextMenu);
Canvas.removeEventListener("wheel", OnWheel);
};
}, [Graph, ShowDirectories, PanButton]);
}, [Graph, ShowDirectories, PanButton, ColourByDepth, ShrinkByDepth]);
const FileCount = Graph ? Graph.Nodes.filter((Node) => !Node.IsDirectory).length : 0;
const DirectoryCount = Graph ? Graph.Nodes.length - FileCount : 0;
@@ -410,7 +428,12 @@ export default function GraphPane({ RootName, DirectoryPath, ShowDirectories, Pa
</option>
</select>
<span className="GraphLegend">
{ShowDirectories && (
{ShowDirectories && !ColourByDepth && (
<>
<span className="GraphLegendDirectory">●</span> Directory
</>
)}
{ShowDirectories && ColourByDepth && (
<>
<span className="GraphLegendDirectory">●</span>
<span className="GraphLegendDepth1">●</span>
+14
View File
@@ -410,6 +410,20 @@ export default function SettingsPanel({
OnChange={(Event) => UpdateField("GraphShowDirectories", Event.target.checked)}
/>
</div>
<div className="SettingsRowInline">
<label className="SettingsLabel">Colour directories by depth (off: all purple)</label>
<Switch
Checked={Settings.GraphColourByDepth}
OnChange={(Event) => UpdateField("GraphColourByDepth", Event.target.checked)}
/>
</div>
<div className="SettingsRowInline">
<label className="SettingsLabel">Smaller directory dots the deeper they are</label>
<Switch
Checked={Settings.GraphShrinkByDepth}
OnChange={(Event) => UpdateField("GraphShrinkByDepth", Event.target.checked)}
/>
</div>
<div className="SettingsRow">
<label className="SettingsLabel">Pan with mouse button</label>
<select
+3 -2
View File
@@ -304,8 +304,9 @@ joined to its parent directory and to the files that reference it (Markdown
links and images, `[[wiki links]]` and portals). Hover a dot to light up its
links, scroll to zoom. One mouse button pans and the other opens the dot
you click: right-drag pans and left-click opens by default, or the other way
round in Settings > Graph, which can also hide directories, leaving only
files and the links between them.
round in Settings > Graph. That's also where directories can be hidden
(leaving only files and the links between them), all made purple instead
of coloured by depth, or made smaller the deeper they are.
Rename takes a `/` to move, like the web view (`../Archive/Notes.md`).
Shortcuts: Enter opens, Backspace goes up, F2 renames, Delete deletes,