Skip to content

Commit

Permalink
feat: Support changing the symbol horizontal and vertical position (#104
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jbmorley authored Jan 9, 2023
1 parent 0a41974 commit b7bf738
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 9 deletions.
6 changes: 5 additions & 1 deletion Symbolic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
D864A23C2965E239008A4261 /* MacIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D864A23B2965E239008A4261 /* MacIconView.swift */; };
D86B0ECF291BEF7400352367 /* IconDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86B0ECE291BEF7400352367 /* IconDocument.swift */; };
D86B0ED1291BEFC100352367 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86B0ED0291BEFC100352367 /* UTType.swift */; };
D8B0B1F1296CD4AB00F907BE /* OffsetGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B0B1F0296CD4AB00F907BE /* OffsetGuide.swift */; };
D8B61308293BB81100F29E0C /* AboutCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B61307293BB81100F29E0C /* AboutCommands.swift */; };
D8B6130A293BB98100F29E0C /* ApplicationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B61309293BB98100F29E0C /* ApplicationModel.swift */; };
D8CD4A7E28AC35C300D57F34 /* SymbolicApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CD4A7D28AC35C300D57F34 /* SymbolicApp.swift */; };
Expand Down Expand Up @@ -102,6 +103,7 @@
D864A23B2965E239008A4261 /* MacIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacIconView.swift; sourceTree = "<group>"; };
D86B0ECE291BEF7400352367 /* IconDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconDocument.swift; sourceTree = "<group>"; };
D86B0ED0291BEFC100352367 /* UTType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTType.swift; sourceTree = "<group>"; };
D8B0B1F0296CD4AB00F907BE /* OffsetGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetGuide.swift; sourceTree = "<group>"; };
D8B61307293BB81100F29E0C /* AboutCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutCommands.swift; sourceTree = "<group>"; };
D8B61309293BB98100F29E0C /* ApplicationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationModel.swift; sourceTree = "<group>"; };
D8CD4A7A28AC35C300D57F34 /* Symbolic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Symbolic.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -188,11 +190,12 @@
D84868E9296ACC8B009FDF32 /* IconSetView.swift */,
D821B9E328ACDBA800504AA4 /* IconView.swift */,
D864A23B2965E239008A4261 /* MacIconView.swift */,
D8B0B1F0296CD4AB00F907BE /* OffsetGuide.swift */,
D84B5D70296C52BE005E6C59 /* SettingsView.swift */,
D821B9ED28ACDC6F00504AA4 /* SymbolPicker.swift */,
D857297B296C176C0037E58F /* SymbolPickerCell.swift */,
D83970E628B132C700282EE8 /* UnityScaleWindow.swift */,
D826B5C3296B054000693D27 /* WatchGridView.swift */,
D84B5D70296C52BE005E6C59 /* SettingsView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -472,6 +475,7 @@
D84B5D6F296C5229005E6C59 /* SceneModel.swift in Sources */,
D826B5C4296B054000693D27 /* WatchGridView.swift in Sources */,
D821B9E828ACDBE000504AA4 /* Icon.swift in Sources */,
D8B0B1F1296CD4AB00F907BE /* OffsetGuide.swift in Sources */,
D821B9EC28ACDC2600504AA4 /* IconCorners.swift in Sources */,
D821B9EE28ACDC6F00504AA4 /* SymbolPicker.swift in Sources */,
D84B5D71296C52BE005E6C59 /* SettingsView.swift in Sources */,
Expand Down
30 changes: 30 additions & 0 deletions Symbolic/Models/Icon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ import SwiftUI

struct Icon: Identifiable, Codable {

enum CodingKeys: String, CodingKey {
case id = "id"
case topColor = "topColor"
case bottomColor = "bottomColor"
case systemImage = "systemImage"
case symbolColor = "symbolColor"
case iconScale = "iconScale"
case iconOffset = "iconOffset"
case shadowOpacity = "shadowOpacity"
case shadowHeight = "shadowHeight"
}

var id = UUID()

var topColor: Color = .pink
Expand All @@ -30,8 +42,26 @@ struct Icon: Identifiable, Codable {
var systemImage: String = "face.smiling"
var symbolColor: Color = .white
var iconScale: CGFloat = 0.8
var iconOffset: CGSize = .zero

var shadowOpacity: CGFloat = 0.4
var shadowHeight: CGFloat = 0.3

init() {

}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(UUID.self, forKey: .id)
self.topColor = try container.decode(Color.self, forKey: .topColor)
self.bottomColor = try container.decode(Color.self, forKey: .bottomColor)
self.systemImage = try container.decode(String.self, forKey: .systemImage)
self.symbolColor = try container.decode(Color.self, forKey: .symbolColor)
self.iconScale = try container.decode(CGFloat.self, forKey: .iconScale)
self.iconOffset = (try? container.decode(CGSize.self, forKey: .iconOffset)) ?? .zero
self.shadowOpacity = try container.decode(CGFloat.self, forKey: .shadowOpacity)
self.shadowHeight = try container.decode(CGFloat.self, forKey: .shadowHeight)
}

}
2 changes: 2 additions & 0 deletions Symbolic/Models/SceneModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ import SwiftUI
class SceneModel: ObservableObject {

@Published var showGrid = false
@Published var showOffsetX = false
@Published var showOffsetY = false

}
4 changes: 2 additions & 2 deletions Symbolic/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct ContentView: View {
Header(section.name)
CenteredFlowLayout {
ForEach(section.sets) { iconSet in
IconSetView(icon: document.icon, iconSet: iconSet, showGrid: sceneModel.showGrid)
IconSetView(sceneModel: sceneModel, icon: document.icon, iconSet: iconSet)
.padding()
}
}
Expand All @@ -49,7 +49,7 @@ struct ContentView: View {
.background(Color(nsColor: .textBackgroundColor))
.frame(maxWidth: .infinity, minHeight: 400)
Divider()
SettingsView(document: document)
SettingsView(sceneModel: sceneModel, document: document)
.frame(width: 300)
}
.focusedSceneObject(document)
Expand Down
11 changes: 7 additions & 4 deletions Symbolic/Views/IconPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import SwiftUI

struct IconPreview: View {

@ObservedObject var sceneModel: SceneModel

let icon: Icon
let definition: IconDefinition
let showGrid: Bool

var body: some View {
ZStack {
Expand All @@ -35,26 +36,28 @@ struct IconPreview: View {
switch definition.style {
case .macOS:
MacIconView(icon: icon, size: width, isShadowFlipped: false)
if showGrid {
if sceneModel.showGrid {
Image("Grid_macOS")
.resizable()
.frame(width: width, height: height)
}
case .iOS:
IconView(icon: icon, size: width, renderShadow: false)
.modifier(IconCorners(size: width, style: .iOS))
if showGrid {
if sceneModel.showGrid {
Image("Grid_iOS")
.resizable()
.frame(width: width, height: height)
}
case .watchOS:
IconView(icon: icon, size: width, renderShadow: false, isWatchOS: true)
.modifier(IconCorners(size: width, style: .watchOS))
if showGrid {
if sceneModel.showGrid {
WatchGridView(size: width)
}
}

OffsetGuide(size: width, showX: sceneModel.showOffsetX, showY: sceneModel.showOffsetY)
}
}

Expand Down
5 changes: 3 additions & 2 deletions Symbolic/Views/IconSetView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@ import SwiftUI

struct IconSetView: View {

@ObservedObject var sceneModel: SceneModel

let icon: Icon
let iconSet: IconSet
let showGrid: Bool

var body: some View {
VStack {
HStack(alignment: .bottom, spacing: 16) {
ForEach(iconSet.definitions) { definition in
VStack {
IconPreview(icon: icon, definition: definition, showGrid: showGrid)
IconPreview(sceneModel: sceneModel, icon: icon, definition: definition)
Text("\(Int(definition.scale))x")
.fixedSize()
if let description = definition.description {
Expand Down
7 changes: 7 additions & 0 deletions Symbolic/Views/IconView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ struct IconView: View {
}
}

var iconOffset: CGSize {
return CGSize(width: icon.iconOffset.width * size,
height: icon.iconOffset.height * size * -1.0)
}

var body: some View {
ZStack {
let x = size / 2
Expand All @@ -63,8 +68,10 @@ struct IconView: View {
radius: shadowRadius,
x: 0,
y: shadowOffset)
.offset(iconOffset)
} else {
image
.offset(iconOffset)
}
}
.frame(width: iconSize, height: iconSize)
Expand Down
49 changes: 49 additions & 0 deletions Symbolic/Views/OffsetGuide.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2022-2023 InSeven Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import SwiftUI

struct OffsetGuide: View {

var size: CGFloat
var showX: Bool
var showY: Bool

var body: some View {

ZStack {
Path() { path in
if showY {
path.move(to: CGPoint(x: 0, y: size / 2))
path.addLine(to: CGPoint(x: size, y: size / 2))
}
if showX {
path.move(to: CGPoint(x: size / 2, y: 0))
path.addLine(to: CGPoint(x: size / 2, y: size))
}
}
.stroke(lineWidth: 2)
.opacity(0.4)
}
.foregroundColor(.accentColor)
.frame(width: size, height: size)
}

}
27 changes: 27 additions & 0 deletions Symbolic/Views/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct SettingsView: View {

@Environment(\.undoManager) var undoManager

@ObservedObject var sceneModel: SceneModel
@ObservedObject var document: IconDocument

@State var undoContext = UndoContext()
Expand All @@ -40,6 +41,32 @@ struct SettingsView: View {
ColorPicker("Color",
selection: $document.icon.symbolColor.undoable(undoManager, context: undoContext),
supportsOpacity: false)
Slider(value: $document.icon.iconOffset.width.undoable(undoManager, context: undoContext), in: -0.5...0.5) {
Text("X Offset")
.onTapGesture(count: 2) {
let width = document.icon.iconOffset.width
undoManager?.registerUndo(undoContext) {
document.icon.iconOffset.width = width
}
document.icon.iconOffset.width = 0
}
}
.onHover { isHovering in
sceneModel.showOffsetX = isHovering
}
Slider(value: $document.icon.iconOffset.height.undoable(undoManager, context: undoContext), in: -0.5...0.5) {
Text("Y Offset")
.onTapGesture(count: 2) {
let height = document.icon.iconOffset.height
undoManager?.registerUndo(undoContext) {
document.icon.iconOffset.height = height
}
document.icon.iconOffset.height = 0
}
}
.onHover { isHovering in
sceneModel.showOffsetY = isHovering
}
}
Section("Shadow") {
Slider(value: $document.icon.shadowOpacity.undoable(undoManager, context: undoContext)) {
Expand Down

0 comments on commit b7bf738

Please sign in to comment.