Improve macOS app lifecycle and split handle

This commit is contained in:
Mikei386
2026-06-04 11:53:46 +02:00
parent 00d34f864b
commit cc2dd6fd1f
2 changed files with 115 additions and 4 deletions
+93 -4
View File
@@ -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
+22
View File
@@ -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
}
}