Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various performance optimizations #310

Merged
merged 5 commits into from
Jun 22, 2023
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
26 changes: 17 additions & 9 deletions App/Sources/Core/Stores/ContentStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,26 @@ final class ContentStore: ObservableObject {
}

private func generatePerformanceData() {
for kind in Command.CodingKeys.allCases {
var group = WorkflowGroup(name: "Group:\(kind.rawValue)")
for x in 0..<100 {
var workflow = Workflow(name: "Workflow:\(kind.rawValue):\(x + 1)")
for y in 0..<100 {
var command = Command.empty(kind)
command.name = "Command:\(kind.rawValue):\(y+1)"
var updatedGroups = Set<WorkflowGroup>()
updatedGroups.reserveCapacity(groupStore.groups.count)

for group in groupStore.groups {
var copy = group
let appsCount = applicationStore.applications.count
(0..<5).forEach { x in
var workflow = Workflow(name: "Performance workflow \(x)")
(0..<50).forEach { y in
let randomApp = applicationStore.applications[Int.random(in: 0..<appsCount)]
let command: Command = .application(
ApplicationCommand(name: "Application command: \(y)", application: randomApp)
)
workflow.commands.append(command)
}
group.workflows.append(workflow)
copy.workflows.append(workflow)
}
groupStore.add(group)
updatedGroups.insert(copy)
}

groupStore.updateGroups(updatedGroups)
}
}
12 changes: 1 addition & 11 deletions App/Sources/UI/Views/CommandView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,7 @@ struct CommandView: View {
style: .focusRing)
)
.background(
LinearGradient(stops: [
.init(color: Color(nsColor: .windowBackgroundColor.blended(withFraction: 0.1, of: .white.withAlphaComponent(0.1))!), location: 0.0),
.init(color: Color(.windowBackgroundColor), location: 0.01),
.init(color: Color(.windowBackgroundColor), location: 0.99),
.init(color: Color(nsColor: .windowBackgroundColor.blended(withFraction: 0.2, of: .black.withAlphaComponent(0.1))!), location: 1.0),
], startPoint: .top, endPoint: .bottom)
.cornerRadius(8)
Color(.windowBackgroundColor).cornerRadius(8)
)
.compositingGroup()
.draggable($command.wrappedValue.draggablePayload(prefix: "WC|", selections: selectionManager.selections))
Expand Down Expand Up @@ -107,9 +101,6 @@ struct CommandView: View {
.animation(.none, value: command.meta.isEnabled)
.grayscale(command.meta.isEnabled ? controlActiveState == .key ? 0 : 0.25 : 0.5)
.opacity(command.meta.isEnabled ? 1 : 0.5)
.shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.1),
radius: command.meta.isEnabled ? 4 : 2,
y: command.meta.isEnabled ? 2 : 0)
.animation(.easeIn(duration: 0.2), value: command.meta.isEnabled)
}
}
Expand All @@ -128,7 +119,6 @@ struct CommandResolverView: View {
self.onAction = onAction
}

@ViewBuilder
var body: some View {
switch command.kind {
case .plain:
Expand Down
30 changes: 14 additions & 16 deletions App/Sources/UI/Views/Commands/ApplicationCommandView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ struct ApplicationCommandView: View {
@State private var metaData: CommandViewModel.MetaData
@State private var model: CommandViewModel.Kind.ApplicationModel

private let debounce: DebounceManager<String>

@EnvironmentObject var applicationStore: ApplicationStore

private let onAction: (Action) -> Void
Expand All @@ -31,6 +33,9 @@ struct ApplicationCommandView: View {
_metaData = .init(initialValue: metaData)
_model = .init(initialValue: model)
self.onAction = onAction
self.debounce = DebounceManager(for: .milliseconds(500)) { newName in
onAction(.updateName(newName: newName))
}
}

var body: some View {
Expand Down Expand Up @@ -66,24 +71,17 @@ struct ApplicationCommandView: View {

TextField("", text: $metaData.name)
.textFieldStyle(AppTextFieldStyle())
.onChange(of: metaData.name, perform: {
onAction(.updateName(newName: $0))
})
.onChange(of: metaData.name, perform: { debounce.send($0) })
}
}, subContent: { _ in
HStack {
Toggle("In background", isOn: $model.inBackground)
.onChange(of: model.inBackground) { newValue in
onAction(.changeApplicationModifier(modifier: .background, newValue: newValue))
}
Toggle("Hide when opening", isOn: $model.hideWhenRunning)
.onChange(of: model.hideWhenRunning) { newValue in
onAction(.changeApplicationModifier(modifier: .hidden, newValue: newValue))
}
Toggle("If not running", isOn: $model.ifNotRunning)
.onChange(of: model.ifNotRunning) { newValue in
onAction(.changeApplicationModifier(modifier: .onlyIfNotRunning, newValue: newValue))
}
AppCheckbox("In background", style: .small, isOn: $model.inBackground) { newValue in
onAction(.changeApplicationModifier(modifier: .background, newValue: newValue))
}
AppCheckbox("Hide when opening", style: .small, isOn: $model.hideWhenRunning) { newValue in
onAction(.changeApplicationModifier(modifier: .hidden, newValue: newValue))
}
AppCheckbox("If not running", style: .small, isOn: $model.ifNotRunning) { newValue in
onAction(.changeApplicationModifier(modifier: .onlyIfNotRunning, newValue: newValue))
}
},
onAction: { onAction(.commandAction($0)) })
Expand Down
21 changes: 9 additions & 12 deletions App/Sources/UI/Views/Commands/CommandContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,19 @@ struct CommandContainerView<IconContent, Content, SubContent>: View where IconCo
.frame(minHeight: 30)
}
.padding([.top, .leading], 8)
.padding(.bottom, 4)

HStack(spacing: 0) {
Toggle(isOn: $metaData.isEnabled) { }
.onChange(of: metaData.isEnabled, perform: {
onAction(.toggleIsEnabled($0))
})
.toggleStyle(.switch)
.tint(.green)
.compositingGroup()
.scaleEffect(0.65)
AppToggle("", onColor: Color(.systemGreen), style: .small, isOn: $metaData.isEnabled) {
onAction(.toggleIsEnabled($0))
}
.padding(.leading, 5)
.padding(.trailing, 5)

HStack {
Toggle("Notify", isOn: $metaData.notification)
.onChange(of: metaData.notification) { newValue in
onAction(.toggleNotify(newValue))
}
AppCheckbox("Notify", style: .small, isOn: $metaData.notification) {
onAction(.toggleNotify($0))
}

if detailPublisher.data.execution == .serial {
Button {
Expand Down
104 changes: 104 additions & 0 deletions App/Sources/UI/Views/Components/AppCheckbox.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import SwiftUI

struct AppCheckbox: View {
enum Style {
case regular
case small

var size: CGSize {
switch self {
case .regular:
return CGSize(width: 16, height: 16)
case .small:
return CGSize(width: 12, height: 12)
}
}

var fontSize: CGFloat {
switch self {
case .regular:
return 10
case .small:
return 8
}
}
}

@Binding private var isOn: Bool
private let style: Style
private let titleKey: String
private let onChange: (Bool) -> Void

init(_ titleKey: String,
style: Style = .regular,
isOn: Binding<Bool>,
onChange: @escaping (Bool) -> Void = { _ in }) {
_isOn = isOn
self.style = style
self.titleKey = titleKey
self.onChange = onChange
}

var body: some View {
HStack(spacing: 4) {
Button(action: {
isOn.toggle()
onChange(isOn)
}, label: {
RoundedRectangle(cornerRadius: 4, style: .continuous)
.fill(Color(nsColor: .controlColor))
.overlay(content: {
Image(systemName: "checkmark")
.font(Font.system(size: style.fontSize, weight: .heavy))
.opacity(isOn ? 1 : 0)
})
.frame(width: style.size.width, height: style.size.height)

})
.buttonStyle(.plain)
Text(titleKey)
}
}
}

struct AppCheckbox_Previews: PreviewProvider {
static var systemToggles: some View {
VStack {
Toggle(isOn: .constant(true), label: {
Text("Default on")
})
Toggle(isOn: .constant(false), label: {
Text("Default off")
})
}
}

static var previews: some View {
VStack(alignment: .leading) {
HStack(alignment: .top, spacing: 32) {
VStack(alignment: .leading) {
Text("System")
.font(.headline)
systemToggles
}

VStack(alignment: .leading) {
Text("Regular")
.font(.headline)
AppCheckbox("Default on", isOn: .constant(true))
AppCheckbox("Default off", isOn: .constant(false))
}

VStack(alignment: .leading) {
Text("Small")
.font(.headline)
AppCheckbox("Default on", style: .small, isOn: .constant(true))
.font(.caption)
AppCheckbox("Default off", style: .small, isOn: .constant(false))
.font(.caption)
}
}
}
.padding()
}
}
126 changes: 126 additions & 0 deletions App/Sources/UI/Views/Components/AppToggle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import SwiftUI

struct AppToggle: View {
enum Style {
case regular
case small

var size: CGSize {
switch self {
case .regular:
return CGSize(width: 38, height: 20)
case .small:
return CGSize(width: 22, height: 12)
}
}

var circle: CGSize {
switch self {
case .regular:
return CGSize(width: 19, height: 19)
case .small:
return CGSize(width: 11, height: 11)
}
}
}

@Environment(\.controlActiveState) var controlActiveState
@Binding private var isOn: Bool
private let onColor: Color
private let style: Style
private let titleKey: String
private let onChange: (Bool) -> Void

init(_ titleKey: String,
onColor: Color = Color(nsColor: .controlColor),
style: Style = .regular,
isOn: Binding<Bool>,
onChange: @escaping (Bool) -> Void = { _ in }) {
_isOn = isOn
self.onColor = onColor
self.style = style
self.titleKey = titleKey
self.onChange = onChange
}

var body: some View {
HStack(spacing: 4) {
Text(titleKey)
Button(action: {
isOn.toggle()
onChange(isOn)
}, label: {
Capsule()
.fill(
controlActiveState == .key
? isOn ? onColor : Color(nsColor: .controlColor)
: Color(nsColor: .controlColor)
)
.overlay(alignment: isOn ? .trailing : .leading, content: {
Circle()
.frame(width: style.circle.width, height: style.circle.height)
.overlay(
Circle()
.stroke(Color(nsColor: .gray), lineWidth: 0.5)
)
})
.animation(.default, value: isOn)
.background(
Capsule()
.strokeBorder(Color(nsColor: .windowBackgroundColor), lineWidth: 1)
)
.frame(width: style.size.width, height: style.size.height)
})
.buttonStyle(.plain)
}
}
}



struct AppToggle_Previews: PreviewProvider {
static var systemToggles: some View {
VStack {
Toggle(isOn: .constant(true), label: {
Text("Default on")
})
.tint(Color(.systemGreen))
Toggle(isOn: .constant(false), label: {
Text("Default off")
})
}
}

static var previews: some View {
VStack(alignment: .leading) {
HStack(alignment: .top, spacing: 32) {
VStack(alignment: .leading) {
Text("System")
.font(.headline)
systemToggles
.toggleStyle(.switch)
}

VStack(alignment: .leading) {
Text("Regular")
.font(.headline)
AppToggle("Default on",
onColor: Color(.systemGreen),
style: .small,
isOn: .constant(true)) { _ in }
AppToggle("Default off",
style: .small,
isOn: .constant(false)) { _ in }
}

VStack(alignment: .leading) {
Text("Small")
.font(.headline)
AppToggle("Default on", style: .small, isOn: .constant(true)) { _ in }
AppToggle("Default off", style: .small, isOn: .constant(false)) { _ in }
}
}
}
.padding()
}
}
Loading
Loading