archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 53;
objects = {
/* Begin PBXBuildFile section */
B523C73D25C98D9800C44061 /* NSImage+writePNG.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */; };
B523C74625C9BD3500C44061 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B523C74525C9BD3500C44061 /* CloudKit.framework */; };
- B523C74B25C9C1BA00C44061 /* DefaultMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C74A25C9C1BA00C44061 /* DefaultMapView.swift */; };
+ B523C74B25C9C1BA00C44061 /* EmptyMapDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C74A25C9C1BA00C44061 /* EmptyMapDetailScreen.swift */; };
B523C75A25C9FD4900C44061 /* MapAxes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C75925C9FD4900C44061 /* MapAxes.swift */; };
B523C76225CA05A300C44061 /* MapStages.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76125CA05A300C44061 /* MapStages.swift */; };
B523C76725CA071B00C44061 /* MapVertices.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76625CA071B00C44061 /* MapVertices.swift */; };
B523C76C25CA0DFA00C44061 /* MapEdges.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76B25CA0DFA00C44061 /* MapEdges.swift */; };
B523C77125CA121300C44061 /* MapBlockers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77025CA121300C44061 /* MapBlockers.swift */; };
- B523C77625CA181100C44061 /* MapColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77525CA181100C44061 /* MapColor.swift */; };
B523C77E25CA294C00C44061 /* MapOpportunities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77D25CA294C00C44061 /* MapOpportunities.swift */; };
B526257225C874F9003E73B7 /* MapApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257125C874F9003E73B7 /* MapApp.swift */; };
- B526257425C874F9003E73B7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257325C874F9003E73B7 /* ContentView.swift */; };
+ B526257425C874F9003E73B7 /* MapEditorWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257325C874F9003E73B7 /* MapEditorWindow.swift */; };
B526257625C874FA003E73B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257525C874FA003E73B7 /* Assets.xcassets */; };
B526257925C874FA003E73B7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257825C874FA003E73B7 /* Preview Assets.xcassets */; };
B526257B25C874FA003E73B7 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257A25C874FA003E73B7 /* Persistence.swift */; };
B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B526257C25C874FA003E73B7 /* Map.xcdatamodeld */; };
B526258A25C874FA003E73B7 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526258925C874FA003E73B7 /* MapTests.swift */; };
B526259525C874FA003E73B7 /* MapUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526259425C874FA003E73B7 /* MapUITests.swift */; };
- B52625A625C876C3003E73B7 /* MapDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625A525C876C3003E73B7 /* MapDetail.swift */; };
+ B52625A625C876C3003E73B7 /* MapDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625A525C876C3003E73B7 /* MapDetailScreen.swift */; };
B52625AB25C87909003E73B7 /* Date+format.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AA25C87909003E73B7 /* Date+format.swift */; };
- B52625B025C87C14003E73B7 /* MapRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AF25C87C14003E73B7 /* MapRender.swift */; };
+ B52625B025C87C14003E73B7 /* MapRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AF25C87C14003E73B7 /* MapRenderView.swift */; };
B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625BA25C884C2003E73B7 /* Map+parse.swift */; };
B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625C525C8BD2A003E73B7 /* Stage.swift */; };
B539516C25CB0C9300959F72 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539516B25CB0C9200959F72 /* Store.swift */; };
B539517425CB0CA400959F72 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539517325CB0CA400959F72 /* AppState.swift */; };
B539518125CB2D7A00959F72 /* MapTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539518025CB2D7A00959F72 /* MapTextEditor.swift */; };
- B587BB6025CDCECB00F328ED /* SlowMapRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B587BB5F25CDCECB00F328ED /* SlowMapRender.swift */; };
B5CF75C925CC19FC003BFF3D /* EvolutionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */; };
B5CF75CF25CC7965003BFF3D /* MapParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75CE25CC7965003BFF3D /* MapParser.swift */; };
B5CF75D825CC79BC003BFF3D /* VertexParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */; };
B5CF75EA25CC7A13003BFF3D /* OpportunityParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */; };
B5CF75EF25CC7A4A003BFF3D /* StageParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */; };
B5CF75F725CC97CA003BFF3D /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75F625CC97CA003BFF3D /* Debouncer.swift */; };
+ B5F8D3082A06DD8C000EEA24 /* Font+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D3072A06DD8C000EEA24 /* Font+Theme.swift */; };
+ B5F8D30B2A06E3E6000EEA24 /* Color+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D30A2A06E3E6000EEA24 /* Color+Theme.swift */; };
+ B5F8D30E2A06E5C2000EEA24 /* Patterns in Frameworks */ = {isa = PBXBuildFile; productRef = B5F8D30D2A06E5C2000EEA24 /* Patterns */; };
+ B5F8D3102A07B33D000EEA24 /* NSColor+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D30F2A07B33D000EEA24 /* NSColor+Theme.swift */; };
+ B5F8D3122A07B690000EEA24 /* NoteParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D3112A07B690000EEA24 /* NoteParserStrategy.swift */; };
+ B5F8D3142A07C05F000EEA24 /* MapNotes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D3132A07C05F000EEA24 /* MapNotes.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
/* Begin PBXFileReference section */
B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+writePNG.swift"; sourceTree = "<group>"; };
B523C74525C9BD3500C44061 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
- B523C74A25C9C1BA00C44061 /* DefaultMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultMapView.swift; sourceTree = "<group>"; };
+ B523C74A25C9C1BA00C44061 /* EmptyMapDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyMapDetailScreen.swift; sourceTree = "<group>"; };
B523C75925C9FD4900C44061 /* MapAxes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapAxes.swift; sourceTree = "<group>"; };
B523C76125CA05A300C44061 /* MapStages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapStages.swift; sourceTree = "<group>"; };
B523C76625CA071B00C44061 /* MapVertices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapVertices.swift; sourceTree = "<group>"; };
B523C76B25CA0DFA00C44061 /* MapEdges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEdges.swift; sourceTree = "<group>"; };
B523C77025CA121300C44061 /* MapBlockers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapBlockers.swift; sourceTree = "<group>"; };
- B523C77525CA181100C44061 /* MapColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapColor.swift; sourceTree = "<group>"; };
B523C77D25CA294C00C44061 /* MapOpportunities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapOpportunities.swift; sourceTree = "<group>"; };
B526256E25C874F9003E73B7 /* Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Map.app; sourceTree = BUILT_PRODUCTS_DIR; };
B526257125C874F9003E73B7 /* MapApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapApp.swift; sourceTree = "<group>"; };
- B526257325C874F9003E73B7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
+ B526257325C874F9003E73B7 /* MapEditorWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEditorWindow.swift; sourceTree = "<group>"; };
B526257525C874FA003E73B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B526257825C874FA003E73B7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
B526257A25C874FA003E73B7 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
B526259025C874FA003E73B7 /* MapUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B526259425C874FA003E73B7 /* MapUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUITests.swift; sourceTree = "<group>"; };
B526259625C874FA003E73B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- B52625A525C876C3003E73B7 /* MapDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDetail.swift; sourceTree = "<group>"; };
+ B52625A525C876C3003E73B7 /* MapDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDetailScreen.swift; sourceTree = "<group>"; };
B52625AA25C87909003E73B7 /* Date+format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+format.swift"; sourceTree = "<group>"; };
- B52625AF25C87C14003E73B7 /* MapRender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRender.swift; sourceTree = "<group>"; };
+ B52625AF25C87C14003E73B7 /* MapRenderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRenderView.swift; sourceTree = "<group>"; };
B52625BA25C884C2003E73B7 /* Map+parse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Map+parse.swift"; sourceTree = "<group>"; };
B52625C525C8BD2A003E73B7 /* Stage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stage.swift; sourceTree = "<group>"; };
B539516B25CB0C9200959F72 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
B539517325CB0CA400959F72 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
B539518025CB2D7A00959F72 /* MapTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTextEditor.swift; sourceTree = "<group>"; };
- B587BB5F25CDCECB00F328ED /* SlowMapRender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlowMapRender.swift; sourceTree = "<group>"; };
B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvolutionPicker.swift; sourceTree = "<group>"; };
B5CF75CE25CC7965003BFF3D /* MapParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapParser.swift; sourceTree = "<group>"; };
B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VertexParserStrategy.swift; sourceTree = "<group>"; };
B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpportunityParserStrategy.swift; sourceTree = "<group>"; };
B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StageParserStrategy.swift; sourceTree = "<group>"; };
B5CF75F625CC97CA003BFF3D /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
+ B5F8D3072A06DD8C000EEA24 /* Font+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+Theme.swift"; sourceTree = "<group>"; };
+ B5F8D30A2A06E3E6000EEA24 /* Color+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Theme.swift"; sourceTree = "<group>"; };
+ B5F8D30F2A07B33D000EEA24 /* NSColor+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Theme.swift"; sourceTree = "<group>"; };
+ B5F8D3112A07B690000EEA24 /* NoteParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteParserStrategy.swift; sourceTree = "<group>"; };
+ B5F8D3132A07C05F000EEA24 /* MapNotes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapNotes.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
buildActionMask = 2147483647;
files = (
B523C74625C9BD3500C44061 /* CloudKit.framework in Frameworks */,
+ B5F8D30E2A06E5C2000EEA24 /* Patterns in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
name = Frameworks;
sourceTree = "<group>";
};
- B523C75825C9FD3A00C44061 /* MapRenderComponents */ = {
+ B523C75825C9FD3A00C44061 /* MapRender */ = {
isa = PBXGroup;
children = (
B523C75925C9FD4900C44061 /* MapAxes.swift */,
B523C76125CA05A300C44061 /* MapStages.swift */,
B523C76625CA071B00C44061 /* MapVertices.swift */,
+ B5F8D3132A07C05F000EEA24 /* MapNotes.swift */,
B523C76B25CA0DFA00C44061 /* MapEdges.swift */,
B523C77025CA121300C44061 /* MapBlockers.swift */,
B523C77D25CA294C00C44061 /* MapOpportunities.swift */,
);
- path = MapRenderComponents;
+ path = MapRender;
sourceTree = "<group>";
};
B526256525C874F9003E73B7 = {
B526257025C874F9003E73B7 /* Map */ = {
isa = PBXGroup;
children = (
- B523C77525CA181100C44061 /* MapColor.swift */,
- B5CF75CD25CC7953003BFF3D /* MapParser */,
- B539516A25CB0C7800959F72 /* State */,
- B52625B425C87D54003E73B7 /* Extensions */,
- B539517925CB0D6100959F72 /* Views */,
- B523C75825C9FD3A00C44061 /* MapRenderComponents */,
+ B526257125C874F9003E73B7 /* MapApp.swift */,
+ B5F8D2FE2A06DB3A000EEA24 /* Data */,
+ B5F8D3092A06DE1A000EEA24 /* Logic */,
+ B5F8D2FF2A06DB40000EEA24 /* Presentation */,
+ B52625B425C87D54003E73B7 /* Core Extensions */,
B526257525C874FA003E73B7 /* Assets.xcassets */,
B526257F25C874FA003E73B7 /* Info.plist */,
B526258025C874FA003E73B7 /* Map.entitlements */,
B526257C25C874FA003E73B7 /* Map.xcdatamodeld */,
B526257725C874FA003E73B7 /* Preview Content */,
- B5CF75F625CC97CA003BFF3D /* Debouncer.swift */,
);
path = Map;
sourceTree = "<group>";
path = MapUITests;
sourceTree = "<group>";
};
- B52625B425C87D54003E73B7 /* Extensions */ = {
+ B52625B425C87D54003E73B7 /* Core Extensions */ = {
isa = PBXGroup;
children = (
- B52625BA25C884C2003E73B7 /* Map+parse.swift */,
B52625AA25C87909003E73B7 /* Date+format.swift */,
B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */,
+ B5F8D3072A06DD8C000EEA24 /* Font+Theme.swift */,
+ B5F8D30F2A07B33D000EEA24 /* NSColor+Theme.swift */,
+ B5F8D30A2A06E3E6000EEA24 /* Color+Theme.swift */,
);
- path = Extensions;
+ path = "Core Extensions";
sourceTree = "<group>";
};
- B539516A25CB0C7800959F72 /* State */ = {
+ B5CF75CD25CC7953003BFF3D /* MapParser */ = {
isa = PBXGroup;
children = (
+ B5CF75D625CC79A4003BFF3D /* Strategies */,
+ B5CF75CE25CC7965003BFF3D /* MapParser.swift */,
+ );
+ path = MapParser;
+ sourceTree = "<group>";
+ };
+ B5CF75D625CC79A4003BFF3D /* Strategies */ = {
+ isa = PBXGroup;
+ children = (
+ B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */,
+ B5F8D3112A07B690000EEA24 /* NoteParserStrategy.swift */,
+ B5CF75DC25CC79D7003BFF3D /* EdgeParserStrategy.swift */,
+ B5CF75E125CC79ED003BFF3D /* BlockerParserStrategy.swift */,
+ B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */,
+ B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */,
+ );
+ path = Strategies;
+ sourceTree = "<group>";
+ };
+ B5F8D2FE2A06DB3A000EEA24 /* Data */ = {
+ isa = PBXGroup;
+ children = (
+ B5F8D3012A06DB75000EEA24 /* Models */,
B52625C525C8BD2A003E73B7 /* Stage.swift */,
B526257A25C874FA003E73B7 /* Persistence.swift */,
B539516B25CB0C9200959F72 /* Store.swift */,
B539517325CB0CA400959F72 /* AppState.swift */,
);
- path = State;
+ path = Data;
sourceTree = "<group>";
};
- B539517925CB0D6100959F72 /* Views */ = {
+ B5F8D2FF2A06DB40000EEA24 /* Presentation */ = {
+ isa = PBXGroup;
+ children = (
+ B5F8D3022A06DBC3000EEA24 /* Windows */,
+ B5F8D3032A06DC2D000EEA24 /* Screens */,
+ B5F8D3052A06DCF3000EEA24 /* Complex Components */,
+ B5F8D3042A06DCC4000EEA24 /* Base Components */,
+ );
+ path = Presentation;
+ sourceTree = "<group>";
+ };
+ B5F8D3012A06DB75000EEA24 /* Models */ = {
+ isa = PBXGroup;
+ children = (
+ B52625BA25C884C2003E73B7 /* Map+parse.swift */,
+ );
+ path = Models;
+ sourceTree = "<group>";
+ };
+ B5F8D3022A06DBC3000EEA24 /* Windows */ = {
+ isa = PBXGroup;
+ children = (
+ B526257325C874F9003E73B7 /* MapEditorWindow.swift */,
+ );
+ path = Windows;
+ sourceTree = "<group>";
+ };
+ B5F8D3032A06DC2D000EEA24 /* Screens */ = {
+ isa = PBXGroup;
+ children = (
+ B523C74A25C9C1BA00C44061 /* EmptyMapDetailScreen.swift */,
+ B52625A525C876C3003E73B7 /* MapDetailScreen.swift */,
+ );
+ path = Screens;
+ sourceTree = "<group>";
+ };
+ B5F8D3042A06DCC4000EEA24 /* Base Components */ = {
isa = PBXGroup;
children = (
- B526257125C874F9003E73B7 /* MapApp.swift */,
- B526257325C874F9003E73B7 /* ContentView.swift */,
- B52625A525C876C3003E73B7 /* MapDetail.swift */,
- B523C74A25C9C1BA00C44061 /* DefaultMapView.swift */,
- B539518025CB2D7A00959F72 /* MapTextEditor.swift */,
- B587BB5F25CDCECB00F328ED /* SlowMapRender.swift */,
- B52625AF25C87C14003E73B7 /* MapRender.swift */,
B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */,
+ B539518025CB2D7A00959F72 /* MapTextEditor.swift */,
+ B523C75825C9FD3A00C44061 /* MapRender */,
);
- path = Views;
+ path = "Base Components";
sourceTree = "<group>";
};
- B5CF75CD25CC7953003BFF3D /* MapParser */ = {
+ B5F8D3052A06DCF3000EEA24 /* Complex Components */ = {
isa = PBXGroup;
children = (
- B5CF75D625CC79A4003BFF3D /* Strategies */,
- B5CF75CE25CC7965003BFF3D /* MapParser.swift */,
+ B5F8D3062A06DD2A000EEA24 /* MapRender */,
);
- path = MapParser;
+ path = "Complex Components";
sourceTree = "<group>";
};
- B5CF75D625CC79A4003BFF3D /* Strategies */ = {
+ B5F8D3062A06DD2A000EEA24 /* MapRender */ = {
isa = PBXGroup;
children = (
- B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */,
- B5CF75DC25CC79D7003BFF3D /* EdgeParserStrategy.swift */,
- B5CF75E125CC79ED003BFF3D /* BlockerParserStrategy.swift */,
- B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */,
- B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */,
+ B52625AF25C87C14003E73B7 /* MapRenderView.swift */,
);
- path = Strategies;
+ path = MapRender;
+ sourceTree = "<group>";
+ };
+ B5F8D3092A06DE1A000EEA24 /* Logic */ = {
+ isa = PBXGroup;
+ children = (
+ B5CF75CD25CC7953003BFF3D /* MapParser */,
+ B5CF75F625CC97CA003BFF3D /* Debouncer.swift */,
+ );
+ path = Logic;
sourceTree = "<group>";
};
/* End PBXGroup section */
dependencies = (
);
name = Map;
+ packageProductDependencies = (
+ B5F8D30D2A06E5C2000EEA24 /* Patterns */,
+ );
productName = Map;
productReference = B526256E25C874F9003E73B7 /* Map.app */;
productType = "com.apple.product-type.application";
B526256625C874F9003E73B7 /* Project object */ = {
isa = PBXProject;
attributes = {
+ BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1240;
- LastUpgradeCheck = 1240;
+ LastUpgradeCheck = 1430;
TargetAttributes = {
B526256D25C874F9003E73B7 = {
CreatedOnToolsVersion = 12.4;
Base,
);
mainGroup = B526256525C874F9003E73B7;
+ packageReferences = (
+ B5F8D30C2A06E5C2000EEA24 /* XCRemoteSwiftPackageReference "patterns" */,
+ );
productRefGroup = B526256F25C874F9003E73B7 /* Products */;
projectDirPath = "";
projectRoot = "";
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- B52625B025C87C14003E73B7 /* MapRender.swift in Sources */,
+ B52625B025C87C14003E73B7 /* MapRenderView.swift in Sources */,
B523C77E25CA294C00C44061 /* MapOpportunities.swift in Sources */,
B52625AB25C87909003E73B7 /* Date+format.swift in Sources */,
B5CF75D825CC79BC003BFF3D /* VertexParserStrategy.swift in Sources */,
B5CF75EA25CC7A13003BFF3D /* OpportunityParserStrategy.swift in Sources */,
+ B5F8D3142A07C05F000EEA24 /* MapNotes.swift in Sources */,
B523C77125CA121300C44061 /* MapBlockers.swift in Sources */,
B523C76725CA071B00C44061 /* MapVertices.swift in Sources */,
B5CF75CF25CC7965003BFF3D /* MapParser.swift in Sources */,
B5CF75E225CC79ED003BFF3D /* BlockerParserStrategy.swift in Sources */,
+ B5F8D3102A07B33D000EEA24 /* NSColor+Theme.swift in Sources */,
+ B5F8D30B2A06E3E6000EEA24 /* Color+Theme.swift in Sources */,
B539517425CB0CA400959F72 /* AppState.swift in Sources */,
B523C75A25C9FD4900C44061 /* MapAxes.swift in Sources */,
B539518125CB2D7A00959F72 /* MapTextEditor.swift in Sources */,
B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */,
B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */,
B523C73D25C98D9800C44061 /* NSImage+writePNG.swift in Sources */,
+ B5F8D3122A07B690000EEA24 /* NoteParserStrategy.swift in Sources */,
B526257B25C874FA003E73B7 /* Persistence.swift in Sources */,
+ B5F8D3082A06DD8C000EEA24 /* Font+Theme.swift in Sources */,
B5CF75EF25CC7A4A003BFF3D /* StageParserStrategy.swift in Sources */,
- B523C77625CA181100C44061 /* MapColor.swift in Sources */,
B5CF75DD25CC79D7003BFF3D /* EdgeParserStrategy.swift in Sources */,
- B526257425C874F9003E73B7 /* ContentView.swift in Sources */,
+ B526257425C874F9003E73B7 /* MapEditorWindow.swift in Sources */,
B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */,
B5CF75C925CC19FC003BFF3D /* EvolutionPicker.swift in Sources */,
- B523C74B25C9C1BA00C44061 /* DefaultMapView.swift in Sources */,
+ B523C74B25C9C1BA00C44061 /* EmptyMapDetailScreen.swift in Sources */,
B539516C25CB0C9300959F72 /* Store.swift in Sources */,
B526257225C874F9003E73B7 /* MapApp.swift in Sources */,
- B52625A625C876C3003E73B7 /* MapDetail.swift in Sources */,
+ B52625A625C876C3003E73B7 /* MapDetailScreen.swift in Sources */,
B523C76225CA05A300C44061 /* MapStages.swift in Sources */,
- B587BB6025CDCECB00F328ED /* SlowMapRender.swift in Sources */,
B5CF75F725CC97CA003BFF3D /* Debouncer.swift in Sources */,
B523C76C25CA0DFA00C44061 /* MapEdges.swift in Sources */,
);
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 3;
+ CURRENT_PROJECT_VERSION = 4;
+ DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\"";
DEVELOPMENT_TEAM = S68NHQVJXW;
ENABLE_HARDENED_RUNTIME = YES;
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11;
- MARKETING_VERSION = 1.2.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MARKETING_VERSION = 2.0.0;
PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 3;
+ CURRENT_PROJECT_VERSION = 4;
+ DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\"";
DEVELOPMENT_TEAM = S68NHQVJXW;
ENABLE_HARDENED_RUNTIME = YES;
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11;
- MARKETING_VERSION = 1.2.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MARKETING_VERSION = 2.0.0;
PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
+ DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = S68NHQVJXW;
INFOPLIST_FILE = MapTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
+ DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = S68NHQVJXW;
INFOPLIST_FILE = MapTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
+ DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = S68NHQVJXW;
INFOPLIST_FILE = MapUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
+ DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = S68NHQVJXW;
INFOPLIST_FILE = MapUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
};
/* End XCConfigurationList section */
+/* Begin XCRemoteSwiftPackageReference section */
+ B5F8D30C2A06E5C2000EEA24 /* XCRemoteSwiftPackageReference "patterns" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://git.sr.ht/~rbdr/patterns";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 2.0.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ B5F8D30D2A06E5C2000EEA24 /* Patterns */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = B5F8D30C2A06E5C2000EEA24 /* XCRemoteSwiftPackageReference "patterns" */;
+ productName = Patterns;
+ };
+/* End XCSwiftPackageProductDependency section */
+
/* Begin XCVersionGroup section */
B526257C25C874FA003E73B7 /* Map.xcdatamodeld */ = {
isa = XCVersionGroup;
--- /dev/null
+{
+ "pins" : [
+ {
+ "identity" : "patterns",
+ "kind" : "remoteSourceControl",
+ "location" : "https://git.sr.ht/~rbdr/patterns",
+ "state" : {
+ "revision" : "ba4ee0edf2aba19ad73fa53cb01dd0fb9b527526",
+ "version" : "2.0.0"
+ }
+ }
+ ],
+ "version" : 2
+}
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.969",
- "green" : "0.871",
- "red" : "1.000"
+ "blue" : "0xF0",
+ "green" : "0xB3",
+ "red" : "0xFF"
}
},
"idiom" : "universal"
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1F",
+ "green" : "0x26",
+ "red" : "0x0F"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1F",
+ "green" : "0x26",
+ "red" : "0x0F"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.902",
+ "green" : "0.561",
+ "red" : "0.310"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x00",
+ "green" : "0x2B",
+ "red" : "0xFA"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xA2",
+ "green" : "0xA6",
+ "red" : "0x90"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.474",
+ "green" : "0.671",
+ "red" : "0.180"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xCC",
+ "green" : "0xFF",
+ "red" : "0x80"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xA2",
+ "green" : "0xA6",
+ "red" : "0x90"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1F",
+ "green" : "0x26",
+ "red" : "0x0F"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x00",
+ "green" : "0x2B",
+ "red" : "0xFA"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xE3",
+ "green" : "0xE6",
+ "red" : "0xDA"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xCC",
+ "green" : "0xD1",
+ "red" : "0xB5"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xE6",
+ "green" : "0x8F",
+ "red" : "0x4F"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+import SwiftUI
+
+extension Color {
+ struct theme {
+ static let darkSlate = Color("Dark Slate")
+ static let jasperRed = Color("Jasper Red")
+ static let olympicBlue = Color("Olympic Blue")
+ static let neutralGray = Color("Neutral Gray")
+ static let lightNeutralGray = Color("Light Neutral Gray")
+ static let darkNeutralGray = Color("Dark Neutral Gray")
+ }
+
+ struct map {
+ static let labelColor = Color.theme.darkSlate
+ static let axisColor = Color.theme.darkSlate
+ static let vertexColor = Color.theme.darkSlate
+ static let blockerColor = Color.theme.jasperRed
+ static let opportunityColor = Color.theme.olympicBlue
+ static let stageForeground = Color.theme.lightNeutralGray
+ static let stageBackground = Color.white
+ }
+
+ struct ui {
+ static let foreground = Color("Foreground")
+ static let background = Color("Background")
+ }
+}
--- /dev/null
+import SwiftUI
+
+public extension Font {
+ struct theme {
+ static let axisLabel = Font.custom("Hiragino Mincho ProN", size: 14)
+ static let vertexLabel = Font.custom("Hiragino Mincho ProN", size: 12)
+ }
+}
--- /dev/null
+import AppKit
+
+extension NSColor {
+ struct syntax {
+ static let vertex = NSColor(named: "Vertex") ?? .textColor
+ static let number = NSColor(named: "Number") ?? .textColor
+ static let option = NSColor(named: "Option") ?? .textColor
+ static let symbol = NSColor(named: "Symbol") ?? .textColor
+ }
+}
let renderView = MapRenderView(
content: Binding.constant(map.content ?? ""),
- evolution: Binding.constant(Stage.stages(state.selectedEvolution)))
+ evolution: Binding.constant(state.selectedEvolution))
let view = NSHostingView(rootView: renderView)
window.contentView = view
dialog.showsResizeIndicator = false
dialog.canCreateDirectories = true
dialog.showsHiddenFiles = false
- dialog.allowedFileTypes = ["png"]
+ dialog.allowedContentTypes = [.png]
dialog.nameFieldStringValue = map.title ?? "Untitled Map"
if dialog.runModal() == NSApplication.ModalResponse.OK {
dialog.showsResizeIndicator = false
dialog.canCreateDirectories = true
dialog.showsHiddenFiles = false
- dialog.allowedFileTypes = ["txt"]
+ dialog.allowedContentTypes = [.text]
dialog.nameFieldStringValue = map.title ?? "Untitled Map"
if let content = map.content {
static func parse(content: String) -> ParsedMap {
let parsers = [
+ AnyMapParserStrategy(NoteParserStrategy()),
AnyMapParserStrategy(VertexParserStrategy()),
AnyMapParserStrategy(EdgeParserStrategy()),
AnyMapParserStrategy(BlockerParserStrategy()),
struct MapParsingPatterns {
static let vertex = try! NSRegularExpression(
pattern:
- "([^\\(]+?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?",
+ "([^\\(\\[\\]]*?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?",
options: .caseInsensitive)
static let edge = try! NSRegularExpression(
pattern: "(.+?)[\\s]*-([->])[\\s]*(.+)", options: .caseInsensitive)
static let blocker = try! NSRegularExpression(
pattern: "\\[(Blocker)\\][\\s]*(.+)", options: .caseInsensitive)
static let opportunity = try! NSRegularExpression(
- pattern: "\\[(Opportunity)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)",
+ pattern: "\\[(Evolution)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)",
+ options: .caseInsensitive)
+ static let note = try! NSRegularExpression(
+ pattern: "\\[(Note)\\][\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(.*)",
options: .caseInsensitive)
static let stage = try! NSRegularExpression(
pattern: "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)", options: .caseInsensitive)
let edges: [MapEdge]
let blockers: [Blocker]
let opportunities: [Opportunity]
+ let notes: [Note]
let stages: [CGFloat]
static let empty: ParsedMap = ParsedMap(
- vertices: [], edges: [], blockers: [], opportunities: [], stages: defaultDimensions)
+ vertices: [], edges: [], blockers: [], opportunities: [], notes: [], stages: defaultDimensions)
}
struct Vertex {
var shape: VertexShape = .circle
}
+struct Note {
+ let id: Int
+ let position: CGPoint
+ let text: String
+}
+
enum VertexShape: String {
case circle
case square
private var edges: [MapEdge] = []
private var blockers: [Blocker] = []
private var opportunities: [Opportunity] = []
+ private var notes: [Note] = []
private var stages: [CGFloat] = defaultDimensions
func addObjectToMap(type: Any.Type, object: Any) {
let opportunity = object as! Opportunity
opportunities.append(opportunity)
}
+
+ if type == Note.self {
+ let note = object as! Note
+ notes.append(note)
+ }
if type == StageDimensions.self {
let stageDimensions = object as! StageDimensions
stages[stageDimensions.index] = stageDimensions.dimensions
-
}
}
func build() -> ParsedMap {
let mappedVertices = vertices.map { label, vertex in return vertex }
return ParsedMap(
- vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities,
+ vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities, notes: notes,
stages: stages)
}
}
--- /dev/null
+import Foundation
+
+struct NoteParserStrategy: MapParserStrategy {
+ private let regex = MapParsingPatterns.note
+
+ func canHandle(line: String) -> Bool {
+ let range = NSRange(location: 0, length: line.utf16.count)
+ let matches = regex.matches(in: String(line), options: [], range: range)
+ return matches.count > 0 && matches[0].numberOfRanges == 5
+ }
+
+ func handle(index: Int, line: String, vertices: [String: Vertex]) -> (Any.Type, Any) {
+ let range = NSRange(location: 0, length: line.utf16.count)
+ let matches = regex.matches(in: String(line), options: [], range: range)
+
+ let match = matches[0]
+ let text = String(line[Range(match.range(at: 4), in: line)!])
+ let xString = String(line[Range(match.range(at: 2), in: line)!])
+ let yString = String(line[Range(match.range(at: 3), in: line)!])
+ let x = CGFloat(truncating: NumberFormatter().number(from: xString) ?? 0.0)
+ let y = CGFloat(truncating: NumberFormatter().number(from: yString) ?? 0.0)
+
+ let note = Note(
+ id: index,
+ position: CGPoint(x: x, y: y),
+ text: text
+ )
+
+ return (Note.self, note)
+ }
+}
var body: some Scene {
WindowGroup {
- ContentView()
+ MapEditorWindow()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(AppStore(initialState: AppState(), reducer: appStateReducer))
}.windowStyle(HiddenTitleBarWindowStyle()).commands {
+++ /dev/null
-import SwiftUI
-
-struct MapColor {
- let foreground: Color
- let background: Color
- let secondary: Color
- let blocker: Color
- let opportunity: Color
- let stages: StageColor
- let syntax: SyntaxColor
-
- static func colorForScheme(_ colorScheme: ColorScheme) -> MapColor {
- if colorScheme == .dark {
- return MapColor(
- foreground: Color.white,
- background: Color(.sRGB, red: 0.13, green: 0.13, blue: 0.13),
- secondary: Color(.sRGB, red: 0.81, green: 0.78, blue: 0.79),
- blocker: Color(.sRGB, red: 0.13, green: 0.13, blue: 0.13),
- opportunity: Color(.sRGB, red: 1.0, green: 0.37, blue: 0.34),
- stages: StageColor(
- i: Color(.sRGB, red: 0.37, green: 0.16, blue: 0.25),
- ii: Color(.sRGB, red: 0.30, green: 0.29, blue: 0.26),
- iii: Color(.sRGB, red: 0.15, green: 0.29, blue: 0.23),
- iv: Color(.sRGB, red: 0.14, green: 0.22, blue: 0.31)),
- syntax: SyntaxColor(
- vertex: NSColor(srgbRed: 0.41, green: 0.84, blue: 0.96, alpha: 1.0),
- number: NSColor(srgbRed: 0.85, green: 0.78, blue: 0.49, alpha: 1.0),
- option: NSColor(srgbRed: 1.0, green: 0.48, blue: 0.7, alpha: 1.0), // #FE7AB3
- symbol: NSColor(srgbRed: 0.85, green: 0.73, blue: 1.0, alpha: 1.0) // #DABBFF
- ))
- } else {
- return MapColor(
- foreground: Color(.sRGB, red: 0.13, green: 0.13, blue: 0.13),
- background: Color.white,
- secondary: Color.gray,
- blocker: Color(.sRGB, red: 0.60, green: 0.52, blue: 0.51),
- opportunity: Color(.sRGB, red: 1.0, green: 0.37, blue: 0.34),
- stages: StageColor(
- i: Color(.sRGB, red: 1.00, green: 0.93, blue: 0.97),
- ii: Color(.sRGB, red: 1.00, green: 0.98, blue: 0.92),
- iii: Color(.sRGB, red: 0.93, green: 1.00, blue: 0.97),
- iv: Color(.sRGB, red: 0.93, green: 0.96, blue: 1.00)),
- syntax: SyntaxColor(
- vertex: NSColor(srgbRed: 0.11, green: 0.42, blue: 0.57, alpha: 1.0),
- number: NSColor(srgbRed: 0.27, green: 0.31, blue: 0.87, alpha: 1.0),
- option: NSColor(srgbRed: 0.68, green: 0.24, blue: 0.64, alpha: 1.0),
- symbol: NSColor(srgbRed: 0.29, green: 0.13, blue: 0.69, alpha: 1.0)
- ))
- }
- }
-}
-
-struct StageColor {
- let i: Color
- let ii: Color
- let iii: Color
- let iv: Color
-}
-
-struct SyntaxColor {
- let vertex: NSColor
- let number: NSColor
- let option: NSColor
- let symbol: NSColor
-}
+++ /dev/null
-import SwiftUI
-
-struct MapStages: View {
-
- @Environment(\.colorScheme) var colorScheme
-
- let mapSize: CGSize
- let lineWidth: CGFloat
- let stages: [CGFloat]
- let opacity = 0.1
-
- var color: MapColor {
- MapColor.colorForScheme(colorScheme)
- }
-
- var body: some View {
-
- ZStack(alignment: .topLeading) {
- Path { path in
- path.addRect(CGRect(x: 0, y: 0, width: w(stages[0]), height: mapSize.height))
- }.fill(color.stages.i)
- Path { path in
- path.addRect(
- CGRect(x: w(stages[0]), y: 0, width: w(stages[1]) - w(stages[0]), height: mapSize.height))
- }.fill(color.stages.ii)
- Path { path in
- path.addRect(
- CGRect(x: w(stages[1]), y: 0, width: w(stages[2]) - w(stages[1]), height: mapSize.height))
- }.fill(color.stages.iii)
- Path { path in
- path.addRect(
- CGRect(x: w(stages[2]), y: 0, width: mapSize.width - w(stages[2]), height: mapSize.height)
- )
- }.fill(color.stages.iv)
-
- Path { path in
- path.move(to: CGPoint(x: w(stages[0]), y: 0))
- path.addLine(to: CGPoint(x: w(stages[0]), y: mapSize.height))
- path.move(to: CGPoint(x: w(stages[1]), y: 0))
- path.addLine(to: CGPoint(x: w(stages[1]), y: mapSize.height))
- path.move(to: CGPoint(x: w(stages[2]), y: 0))
- path.addLine(to: CGPoint(x: w(stages[2]), y: mapSize.height))
- path.move(to: CGPoint(x: w(stages[0]), y: 0))
- path.closeSubpath()
- }.strokedPath(StrokeStyle(lineWidth: lineWidth, dash: [10.0])).stroke(color.foreground)
- }
- }
-
- func w(_ dimension: CGFloat) -> CGFloat {
- max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
- }
-}
-
-struct MapStages_Previews: PreviewProvider {
- static var previews: some View {
- MapStages(
- mapSize: CGSize(width: 200.0, height: 200.0), lineWidth: CGFloat(1.0),
- stages: [25.0, 50.0, 75.0])
- }
-}
-//
-// EvolutionPicker.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/4/21.
-//
-
import SwiftUI
struct EvolutionPicker: View {
struct MapAxes: View {
- @Environment(\.colorScheme) var colorScheme
-
let mapSize: CGSize
let lineWidth: CGFloat
let evolution: Stage
let stageHeight = CGFloat(100.0)
let padding = CGFloat(5.0)
- var color: Color {
- MapColor.colorForScheme(colorScheme).foreground
- }
-
var body: some View {
ZStack(alignment: .topLeading) {
path.addLine(to: CGPoint(x: mapSize.width, y: mapSize.height))
path.move(to: CGPoint(x: mapSize.width, y: mapSize.height))
path.closeSubpath()
- }.stroke(color, lineWidth: lineWidth * 2)
+ }.stroke(Color.map.axisColor, lineWidth: lineWidth * 2)
// Y Labels
- Text("Visible").font(.title3).foregroundColor(color).rotationEffect(Angle(degrees: -90.0))
+ Text("Visible").font(.theme.axisLabel).foregroundColor(.map.labelColor).rotationEffect(Angle(degrees: -90.0))
.offset(CGSize(width: -35.0, height: 0.0))
- Text("Value Chain").font(.title).foregroundColor(color).rotationEffect(Angle(degrees: -90.0))
- .offset(CGSize(width: -72.0, height: mapSize.height / 2 - 20))
- Text("Invisible").font(.title3).foregroundColor(color).rotationEffect(Angle(degrees: -90.0))
+ Text("Invisible").font(.theme.axisLabel).foregroundColor(.map.labelColor).rotationEffect(Angle(degrees: -90.0))
.offset(CGSize(width: -40.0, height: mapSize.height - 20))
// X Labels
Text("Uncharted")
- .font(.title3)
- .foregroundColor(color)
+ .font(.theme.axisLabel)
+ .foregroundColor(.map.labelColor)
.frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading)
.offset(CGSize(width: 0.0, height: -stageHeight / 4.0))
Text("Industrialised")
- .font(.title3)
- .foregroundColor(color)
+ .font(.theme.axisLabel)
+ .foregroundColor(.map.labelColor)
.frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading)
.offset(CGSize(width: mapSize.width - 100.0, height: -stageHeight / 4.0))
Text(evolution.i)
- .font(.title3)
- .foregroundColor(color)
+ .font(.theme.axisLabel)
+ .foregroundColor(.map.labelColor)
.frame(width: w(stages[0]), height: stageHeight, alignment: .topLeading)
.offset(CGSize(width: 0.0, height: mapSize.height + padding))
Text(evolution.ii)
- .font(.title3)
- .foregroundColor(color)
+ .font(.theme.axisLabel)
+ .foregroundColor(.map.labelColor)
.frame(width: w(stages[1]) - w(stages[0]), height: stageHeight, alignment: .topLeading)
.offset(CGSize(width: w(stages[0]), height: mapSize.height + padding))
Text(evolution.iii)
- .font(.title3)
- .foregroundColor(color)
+ .font(.theme.axisLabel)
+ .foregroundColor(.map.labelColor)
.frame(width: w(stages[2]) - w(stages[1]), height: stageHeight, alignment: .topLeading)
.offset(CGSize(width: w(stages[1]), height: mapSize.height + padding))
Text(evolution.iv)
- .font(.title3)
- .foregroundColor(color)
+ .font(.theme.axisLabel)
+ .foregroundColor(.map.labelColor)
.frame(width: mapSize.width - w(stages[2]), height: stageHeight, alignment: .topLeading)
.offset(CGSize(width: w(stages[2]), height: mapSize.height + padding))
}
struct MapBlockers: View {
- @Environment(\.colorScheme) var colorScheme
-
let mapSize: CGSize
let vertexSize: CGSize
let blockers: [Blocker]
- var color: Color {
- MapColor.colorForScheme(colorScheme).blocker
- }
-
let cornerSize = CGSize(width: 2.0, height: 2.0)
var body: some View {
y: h(vertex.position.y) - vertexSize.height * 2 / 3),
size: CGSize(width: vertexSize.width / 2, height: vertexSize.height * 2)
), cornerSize: cornerSize)
- }.fill(color)
+ }.fill(Color.map.blockerColor)
}
}
-//
-// MapEdges.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/2/21.
-//
-
import SwiftUI
struct MapEdges: View {
- @Environment(\.colorScheme) var colorScheme
-
let mapSize: CGSize
let lineWidth: CGFloat
let vertexSize: CGSize
let arrowheadSize = CGFloat(10.0)
- var color: Color {
- MapColor.colorForScheme(colorScheme).foreground
- }
-
var body: some View {
ForEach(edges, id: \.id) { edge in
Path { path in
path.closeSubpath()
}.applying(
CGAffineTransform(translationX: vertexSize.width / 2.0, y: vertexSize.height / 2.0)
- ).stroke(color, lineWidth: lineWidth)
+ ).stroke(Color.map.vertexColor, lineWidth: lineWidth)
}
}
--- /dev/null
+import SwiftUI
+
+struct MapNotes: View {
+
+ let mapSize: CGSize
+ let lineWidth: CGFloat
+ let notes: [Note]
+
+ let maxWidth = 400.0
+
+
+ var body: some View {
+ ForEach(notes, id: \.id) { note in
+ Text(note.text.replacingOccurrences(of: "\\n", with: "\n")).font(.theme.axisLabel)
+ .padding(2.0)
+ .background(.white)
+ .foregroundColor(.map.labelColor)
+ .border(Color.map.vertexColor, width: lineWidth)
+ .frame(minWidth: 16.0, maxWidth: maxWidth, alignment: .topLeading)
+ .offset(
+ CGSize(
+ width: w(note.position.x),
+ height: h(note.position.y)
+ )
+ )
+ }
+ }
+
+ func h(_ dimension: CGFloat) -> CGFloat {
+ max(0.0, min(mapSize.height, dimension * mapSize.height / 100.0))
+ }
+
+ func w(_ dimension: CGFloat) -> CGFloat {
+ max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
+ }
+}
+
+struct MapNotes_Previews: PreviewProvider {
+ static var previews: some View {
+ MapNotes(
+ mapSize: CGSize(width: 400.0, height: 400.0), lineWidth: 1.0,
+ notes: [
+ Note(id: 0, position: CGPoint(x: 50.0, y: 50.0), text: "Notes can have a lot more text, so we need to make sure that they're resized correctly"),
+ ])
+ }
+}
-//
-// MapEdges.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/2/21.
-//
-
import SwiftUI
struct MapOpportunities: View {
- @Environment(\.colorScheme) var colorScheme
-
let mapSize: CGSize
let lineWidth: CGFloat
let vertexSize: CGSize
let arrowheadSize = CGFloat(10.0)
- var color: Color {
- MapColor.colorForScheme(colorScheme).opportunity
- }
-
var body: some View {
ForEach(opportunities, id: \.id) { edge in
Path { path in
path.closeSubpath()
}.applying(
CGAffineTransform(translationX: vertexSize.width / 2.0, y: vertexSize.height / 2.0)
- ).strokedPath(StrokeStyle(lineWidth: lineWidth * 2, dash: [10.0])).stroke(color)
+ ).strokedPath(StrokeStyle(lineWidth: lineWidth / 4, dash: [10.0])).stroke(Color.map.opportunityColor)
}
}
--- /dev/null
+import SwiftUI
+import Patterns
+
+struct MapStages: View {
+
+ let mapSize: CGSize
+ let lineWidth: CGFloat
+ let stages: [CGFloat]
+ let opacity = 0.1
+
+ var body: some View {
+ ZStack(alignment: .topLeading) {
+ PatternView(design: .constant(.stitch), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+ .frame(width: w(stages[0]), height: mapSize.height)
+ PatternView(design: .constant(.shingles), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+ .offset(CGSize(width: w(stages[0]), height: 0))
+ .frame(width: w(stages[1]) - w(stages[0]), height: mapSize.height)
+ PatternView(design: .constant(.shadowGrid), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+ .offset(CGSize(width: w(stages[1]), height: 0))
+ .frame(width: w(stages[2]) - w(stages[1]), height: mapSize.height)
+ PatternView(design: .constant(.wicker), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+ .offset(CGSize(width: w(stages[2]), height: 0))
+ .frame(width: mapSize.width - w(stages[2]), height: mapSize.height)
+
+ Path { path in
+ path.move(to: CGPoint(x: w(stages[0]), y: 0))
+ path.addLine(to: CGPoint(x: w(stages[0]), y: mapSize.height))
+ path.closeSubpath()
+ path.move(to: CGPoint(x: w(stages[1]), y: 0))
+ path.addLine(to: CGPoint(x: w(stages[1]), y: mapSize.height))
+ path.closeSubpath()
+ path.move(to: CGPoint(x: w(stages[2]), y: 0))
+ path.addLine(to: CGPoint(x: w(stages[2]), y: mapSize.height))
+ path.closeSubpath()
+ path.move(to: CGPoint(x: w(stages[0]), y: 0))
+ path.closeSubpath()
+ }.strokedPath(StrokeStyle(lineWidth: lineWidth / 4, dash: [10.0, 18.0])).stroke(Color.map.axisColor)
+ }
+ }
+
+ func w(_ dimension: CGFloat) -> CGFloat {
+ max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
+ }
+}
+
+struct MapStages_Previews: PreviewProvider {
+ static var previews: some View {
+ MapStages(
+ mapSize: CGSize(width: 200.0, height: 200.0), lineWidth: CGFloat(0.5),
+ stages: [25.0, 50.0, 75.0])
+ }
+}
struct MapVertices: View {
- @Environment(\.colorScheme) var colorScheme
-
let mapSize: CGSize
let vertexSize: CGSize
let vertices: [Vertex]
- let padding = CGFloat(4.0)
-
- var color: MapColor {
- MapColor.colorForScheme(colorScheme)
- }
+ let padding = CGFloat(5.0)
var body: some View {
- ForEach(vertices, id: \.id) { vertex in
- getVertexShape(vertex).fill(color.foreground)
- Text(vertex.label).foregroundColor(color.secondary).offset(
- CGSize(
- width: w(vertex.position.x) + vertexSize.width + padding,
- height: h(vertex.position.y)))
+ ZStack(alignment: .topLeading) {
+ ForEach(vertices, id: \.id) { vertex in
+ getVertexShape(vertex).fill(Color.map.vertexColor)
+ Text(vertex.label.replacingOccurrences(of: "\\n", with: "\n")).font(.theme.vertexLabel)
+ .foregroundColor(.map.labelColor)
+ .shadow(color: .white, radius: 0, x: -0.5, y: -0.5)
+ .shadow(color: .white, radius: 0, x: 0.5, y: 0.5)
+ .offset(
+ CGSize(
+ width: w(vertex.position.x) + vertexSize.width + padding,
+ height: h(vertex.position.y) + 7.0))
+ }
}
}
path.addLine(
to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y) + vertexSize.height))
path.closeSubpath()
- }.strokedPath(StrokeStyle(lineWidth: 5.0, lineCap: .butt))
+ }.strokedPath(StrokeStyle(lineWidth: 2.0, lineCap: .butt))
}
}
}
struct MapVertices_Previews: PreviewProvider {
static var previews: some View {
- MapVertices(
- mapSize: CGSize(width: 400.0, height: 400.0), vertexSize: CGSize(width: 25.0, height: 25.0),
- vertices: [
- Vertex(id: 0, label: "A", position: CGPoint(x: 50.0, y: 50.0)),
- Vertex(id: 0, label: "A", position: CGPoint(x: 10.0, y: 20.0)),
- ])
+ MapVertices(
+ mapSize: CGSize(width: 400.0, height: 400.0), vertexSize: CGSize(width: 25.0, height: 25.0),
+ vertices: [
+ Vertex(id: 0, label: "A Circle", position: CGPoint(x: 50.0, y: 50.0)),
+ Vertex(id: 1, label: "A Square", position: CGPoint(x: 10.0, y: 20.0), shape: .square),
+ Vertex(id: 2, label: "A triangle", position: CGPoint(x: 25, y: 32.0), shape: .triangle),
+ Vertex(id: 3, label: "An X", position: CGPoint(x: 70.0, y: 70.0), shape: .x),
+ ])
}
}
class MapTextEditorController: NSViewController {
@Binding var text: String
- var colorScheme: ColorScheme
+ let onChange: () -> Void
private let vertexRegex = MapParsingPatterns.vertex
private let edgeRegex = MapParsingPatterns.edge
private let blockerRegex = MapParsingPatterns.blocker
private let opportunityRegex = MapParsingPatterns.opportunity
+ private let noteRegex = MapParsingPatterns.note
private let stageRegex = MapParsingPatterns.stage
- private let debouncer: Debouncer = Debouncer(seconds: 0.2)
+ private let changeDebouncer: Debouncer = Debouncer(seconds: 1)
- init(text: Binding<String>, colorScheme: ColorScheme) {
+ init(text: Binding<String>, onChange: @escaping () -> Void) {
self._text = text
- self.colorScheme = colorScheme
+ self.onChange = onChange
super.init(nibName: nil, bundle: nil)
}
override func viewDidAppear() {
self.view.window?.makeFirstResponder(self.view)
}
-
- func updateColorScheme(_ colorScheme: ColorScheme) {
- self.colorScheme = colorScheme
- }
}
extension MapTextEditorController: NSTextViewDelegate {
func textDidChange(_ obj: Notification) {
if let textField = obj.object as? NSTextView {
self.text = textField.string
+
+
+ changeDebouncer.debounce {
+ DispatchQueue.main.async {
+ self.onChange()
+ }
+ }
}
}
}
extension MapTextEditorController: NSTextStorageDelegate {
+
override func textStorageDidProcessEditing(_ obj: Notification) {
if let textStorage = obj.object as? NSTextStorage {
- debouncer.debounce {
- DispatchQueue.main.async {
- self.colorizeText(textStorage: textStorage)
- }
- }
+ self.colorizeText(textStorage: textStorage)
}
}
private func colorizeText(textStorage: NSTextStorage) {
let range = NSMakeRange(0, textStorage.length)
var matches = vertexRegex.matches(in: textStorage.string, options: [], range: range)
- let colors = MapColor.colorForScheme(colorScheme)
for match in matches {
- textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 1))
- textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 2))
- textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 3))
- textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 4))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 3))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 4))
}
matches = edgeRegex.matches(in: textStorage.string, options: [], range: range)
for match in matches {
- textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 1))
let arrowRange = match.range(at: 2)
textStorage.addAttributes(
- [.foregroundColor: colors.syntax.symbol],
+ [.foregroundColor: NSColor.syntax.symbol],
range: NSMakeRange(arrowRange.lowerBound - 1, arrowRange.length + 1))
- textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 3))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 3))
}
matches = opportunityRegex.matches(in: textStorage.string, options: [], range: range)
for match in matches {
- textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1))
- textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 2))
- textStorage.addAttributes([.foregroundColor: colors.syntax.symbol], range: match.range(at: 3))
- textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 4))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.symbol], range: match.range(at: 3))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 4))
}
matches = blockerRegex.matches(in: textStorage.string, options: [], range: range)
for match in matches {
- textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1))
- textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 2))
+ }
+
+ matches = noteRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 3))
}
matches = stageRegex.matches(in: textStorage.string, options: [], range: range)
for match in matches {
- textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1))
- textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
}
}
}
struct MapTextEditor: NSViewControllerRepresentable {
@Binding var text: String
- let colorScheme: ColorScheme
+ var onChange: () -> Void = {}
func makeNSViewController(
context: NSViewControllerRepresentableContext<MapTextEditor>
) -> MapTextEditorController {
- return MapTextEditorController(text: $text, colorScheme: colorScheme)
+ return MapTextEditorController(text: $text, onChange: onChange)
}
func updateNSViewController(
_ nsViewController: MapTextEditorController,
context: NSViewControllerRepresentableContext<MapTextEditor>
- ) {
- nsViewController.updateColorScheme(context.environment.colorScheme)
- }
+ ) {}
}
-//
-// ContentView.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/1/21.
-//
-
import Combine
import CoreData
import CoreGraphics
struct MapRenderView: View {
- @Environment(\.colorScheme) var colorScheme
-
@Binding var content: String
- @Binding var evolution: Stage
+ @Binding var evolution: StageType
+
+ var stage: Stage {
+ Stage.stages(evolution)
+ }
@State var parsedMap: ParsedMap = ParsedMap.empty
let mapSize = CGSize(width: 1300.0, height: 1000.0)
- let lineWidth = CGFloat(1.0)
+ let lineWidth = CGFloat(0.5)
let vertexSize = CGSize(width: 25.0, height: 25.0)
let padding = CGFloat(30.0)
CGRect(
x: -padding, y: -padding, width: mapSize.width + padding * 2,
height: mapSize.height + padding * 4))
- }.fill(MapColor.colorForScheme(colorScheme).background)
+ }.fill(.white)
MapStages(mapSize: mapSize, lineWidth: lineWidth, stages: parsedMap.stages)
MapAxes(
- mapSize: mapSize, lineWidth: lineWidth, evolution: evolution, stages: parsedMap.stages)
+ mapSize: mapSize, lineWidth: lineWidth, evolution: stage, stages: parsedMap.stages)
+ MapEdges(
+ mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, edges: parsedMap.edges)
+ MapBlockers(mapSize: mapSize, vertexSize: vertexSize, blockers: parsedMap.blockers)
+ MapVertices(mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices)
MapOpportunities(
mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize,
opportunities: parsedMap.opportunities)
- MapBlockers(mapSize: mapSize, vertexSize: vertexSize, blockers: parsedMap.blockers)
- MapVertices(mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices)
- MapEdges(
- mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, edges: parsedMap.edges)
+ MapNotes(
+ mapSize: mapSize, lineWidth: lineWidth, notes: parsedMap.notes)
}.frame(
width: mapSize.width,
height: mapSize.height + 2 * padding, alignment: .topLeading
struct MapRenderView_Previews: PreviewProvider {
static var previews: some View {
MapRenderView(
- content: Binding.constant(""), evolution: Binding.constant(Stage.stages(.general))
+ content: Binding.constant(""), evolution: Binding.constant(StageType.general)
).environment(
\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
-//
-// ContentView.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/1/21.
-//
-
import CoreData
import SwiftUI
-struct DefaultMapView: View {
+struct EmptyMapDetailScreen: View {
var body: some View {
Text("Select a map from the left hand side, or click on + to create one.")
struct DefaultMapView_Previews: PreviewProvider {
static var previews: some View {
- DefaultMapView().environment(
+ EmptyMapDetailScreen().environment(
\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
-//
-// ContentView.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/1/21.
-//
-
import Combine
import CoreData
import SwiftUI
-class SaveTimer {
- let currentTimePublisher = Timer.TimerPublisher(interval: 1, runLoop: .main, mode: .default)
- let cancellable: AnyCancellable?
-
- init() {
- self.cancellable = currentTimePublisher.connect() as? AnyCancellable
- }
-
- deinit {
- self.cancellable?.cancel()
- }
-}
-
-let timer = SaveTimer()
-
-struct MapDetailView: View {
+struct MapDetailScreen: View {
@Environment(\.managedObjectContext) private var viewContext
- @Environment(\.colorScheme) var colorScheme
@EnvironmentObject var store: AppStore
@ObservedObject var map: Map
- private var mapColor: MapColor {
- MapColor.colorForScheme(colorScheme)
- }
-
@State var title: String
@State var content: String
VStack {
HStack {
TextField(
- "Title", text: $title
+ "Title", text: $title, onCommit: saveModel
).font(.title2).textFieldStyle(PlainTextFieldStyle()).padding(.vertical, 4.0).padding(
- .leading, 4.0)
+ .leading, 4.0)
Button(action: saveText) {
Image(systemName: "doc.text")
}.padding(.vertical, 4.0).padding(.leading, 4.0)
EvolutionPicker()
ZStack(alignment: .topLeading) {
- MapTextEditor(text: $content, colorScheme: colorScheme)
- .background(mapColor.background)
- .foregroundColor(mapColor.foreground)
+ MapTextEditor(text: $content, onChange: saveModel)
+ .background(Color.ui.background)
+ .foregroundColor(Color.ui.foreground)
.frame(minHeight: 96.0)
- }.padding(.top, 8.0).padding(.leading, 8.0).background(mapColor.background).cornerRadius(
+ }.padding(.top, 8.0).padding(.leading, 8.0).background(Color.ui.background).cornerRadius(
5.0)
}.padding(.horizontal, 8.0)
ScrollView([.horizontal, .vertical]) {
- SlowMapRender(
- content: content, evolution: Stage.stages(store.state.selectedEvolution),
- colorScheme: colorScheme)
- }.background(mapColor.background)
- }.onReceive(timer.currentTimePublisher) { _ in
- saveModel()
+ MapRenderView(content: $content, evolution: .constant(store.state.selectedEvolution))
+ }
}.onDisappear {
saveModel()
}
} else {
- DefaultMapView()
+ EmptyMapDetailScreen()
}
}
struct MapDetailView_Previews: PreviewProvider {
static var previews: some View {
- MapDetailView(map: Map(), title: "", content: "").environment(
+ MapDetailScreen(map: Map(), title: "", content: "").environment(
\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
-//
-// ContentView.swift
-// Map
-//
-// Created by Ruben Beltran del Rio on 2/1/21.
-//
-
import CoreData
import SwiftUI
-struct ContentView: View {
+struct MapEditorWindow: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var store: AppStore
NavigationView {
List {
if maps.count == 0 {
- DefaultMapView()
+ EmptyMapDetailScreen()
}
ForEach(maps) { map in
NavigationLink(
- destination: MapDetailView(map: map, title: map.title ?? "", content: map.content ?? "")
+ destination: MapDetailScreen(map: map, title: map.title ?? "", content: map.content ?? "")
) {
HStack {
Text(map.title ?? "Untitled Map")
}
}
}
- DefaultMapView()
+ EmptyMapDetailScreen()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
- ContentView().environment(
+ MapEditorWindow().environment(
\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
+++ /dev/null
-import Cocoa
-import SwiftUI
-
-class SlowMapRenderController: NSViewController {
-
- var content: String
- private var contentBinding: Binding<String> {
- Binding(
- get: { self.content },
- set: { content in
- self.content = content
- }
- )
- }
-
- var evolution: Stage
- private var evolutionBinding: Binding<Stage> {
- Binding(
- get: { self.evolution },
- set: { evolution in
- self.evolution = evolution
- }
- )
- }
- private var colorSchemeEnvironment: ObservableColorSchemeEnvironment
-
- private let debouncer: Debouncer = Debouncer(seconds: 0.1)
-
- init(content: String, evolution: Stage, colorScheme: ColorScheme) {
-
- self.content = content
- self.evolution = evolution
- self.colorSchemeEnvironment = ObservableColorSchemeEnvironment(colorScheme: colorScheme)
- super.init(nibName: nil, bundle: nil)
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func loadView() {
-
- let renderView = MapRenderView(content: self.contentBinding, evolution: self.evolutionBinding)
- .environmentObject(self.colorSchemeEnvironment)
- let hostingView = NSHostingView(rootView: renderView)
-
- self.view = hostingView
- }
-
- func update(content: String, evolution: Stage, colorScheme: ColorScheme) {
- self.debouncer.debounce {
- DispatchQueue.main.async {
- print("Updating: START")
- self.content = content
- self.evolution = evolution
- self.colorSchemeEnvironment.colorScheme = colorScheme
- print("Updating: END")
- }
- }
- }
-
- private class ObservableColorSchemeEnvironment: ObservableObject {
- @Published var colorScheme: ColorScheme
-
- init(colorScheme: ColorScheme) {
- self.colorScheme = colorScheme
- }
- }
-}
-
-struct SlowMapRender: NSViewControllerRepresentable {
-
- var content: String
- var evolution: Stage
- let colorScheme: ColorScheme
-
- func makeNSViewController(
- context: NSViewControllerRepresentableContext<SlowMapRender>
- ) -> SlowMapRenderController {
- return SlowMapRenderController(content: content, evolution: evolution, colorScheme: colorScheme)
- }
-
- func updateNSViewController(
- _ nsViewController: SlowMapRenderController,
- context: NSViewControllerRepresentableContext<SlowMapRender>
- ) {
- nsViewController.update(
- content: content, evolution: evolution, colorScheme: context.environment.colorScheme)
- }
-}