Improve macOS app lifecycle and split handle
This commit is contained in:
@@ -7,6 +7,7 @@ struct ContentView: View {
|
||||
@State private var settings = SettingsStore.load()
|
||||
@State private var selectedClipID: UUID?
|
||||
@State private var variantPickerClip: GrowthLapseProjectClip?
|
||||
@State private var reviewPanelHeight: CGFloat = 340
|
||||
|
||||
var body: some View {
|
||||
HSplitView {
|
||||
@@ -231,12 +232,14 @@ struct ContentView: View {
|
||||
.progressViewStyle(.linear)
|
||||
|
||||
if let project = processor.project {
|
||||
VSplitView {
|
||||
ResizableVerticalStack(
|
||||
topHeight: $reviewPanelHeight,
|
||||
minTopHeight: 260,
|
||||
minBottomHeight: 160
|
||||
) {
|
||||
projectReviewPanel(project)
|
||||
.frame(minHeight: 260)
|
||||
|
||||
} bottom: {
|
||||
logView
|
||||
.frame(minHeight: 160)
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -635,6 +638,92 @@ private struct ClipPreviewView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct ResizableVerticalStack<Top: View, Bottom: View>: View {
|
||||
@Binding var topHeight: CGFloat
|
||||
let minTopHeight: CGFloat
|
||||
let minBottomHeight: CGFloat
|
||||
@ViewBuilder let top: () -> Top
|
||||
@ViewBuilder let bottom: () -> Bottom
|
||||
|
||||
@State private var dragStartHeight: CGFloat?
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
let dividerHeight: CGFloat = 14
|
||||
let availableHeight = max(0, proxy.size.height - dividerHeight)
|
||||
let maxTopHeight = max(minTopHeight, availableHeight - minBottomHeight)
|
||||
let clampedTopHeight = min(max(topHeight, minTopHeight), maxTopHeight)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
top()
|
||||
.frame(height: clampedTopHeight)
|
||||
.clipped()
|
||||
|
||||
SplitHandle()
|
||||
.frame(height: dividerHeight)
|
||||
.contentShape(Rectangle())
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0)
|
||||
.onChanged { value in
|
||||
if dragStartHeight == nil {
|
||||
dragStartHeight = clampedTopHeight
|
||||
}
|
||||
let proposed = (dragStartHeight ?? clampedTopHeight) + value.translation.height
|
||||
topHeight = min(max(proposed, minTopHeight), maxTopHeight)
|
||||
}
|
||||
.onEnded { _ in
|
||||
dragStartHeight = nil
|
||||
}
|
||||
)
|
||||
.help("Ziehen, um Clip-Nachbearbeitung und Log größer oder kleiner zu machen.")
|
||||
|
||||
bottom()
|
||||
.frame(height: max(minBottomHeight, availableHeight - clampedTopHeight))
|
||||
.clipped()
|
||||
}
|
||||
.onAppear {
|
||||
topHeight = clampedTopHeight
|
||||
}
|
||||
.onChange(of: proxy.size.height) { _ in
|
||||
topHeight = clampedTopHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct SplitHandle: View {
|
||||
@State private var isHovering = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(Color(nsColor: .windowBackgroundColor))
|
||||
|
||||
RoundedRectangle(cornerRadius: 3)
|
||||
.fill(isHovering ? Color.accentColor.opacity(0.75) : Color(nsColor: .separatorColor))
|
||||
.frame(width: 84, height: 5)
|
||||
}
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle()
|
||||
.fill(Color(nsColor: .separatorColor).opacity(0.45))
|
||||
.frame(height: 1)
|
||||
}
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle()
|
||||
.fill(Color(nsColor: .separatorColor).opacity(0.45))
|
||||
.frame(height: 1)
|
||||
}
|
||||
.onHover { hovering in
|
||||
isHovering = hovering
|
||||
if hovering {
|
||||
NSCursor.resizeUpDown.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct SegmentTimelineView: View {
|
||||
let clip: GrowthLapseProjectClip
|
||||
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct GrowthLapseApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.frame(minWidth: 980, minHeight: 680)
|
||||
}
|
||||
.windowStyle(.titleBar)
|
||||
.commands {
|
||||
CommandGroup(replacing: .appTermination) {
|
||||
Button("GrowthLapse beenden") {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
.keyboardShortcut("q", modifiers: .command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user