mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Fix: Window things
This commit is contained in:
parent
6a8477758b
commit
f84c0aafa4
2 changed files with 152 additions and 70 deletions
|
|
@ -157,44 +157,66 @@ struct ClipboardPanelView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
ZStack {
|
||||||
// Header with tabs
|
VStack(spacing: 0) {
|
||||||
ClipboardPanelHeader(
|
// Header with tabs
|
||||||
selectedTab: $selectedTab,
|
ClipboardPanelHeader(
|
||||||
searchText: $searchText,
|
selectedTab: $selectedTab,
|
||||||
onClose: onClose
|
searchText: $searchText,
|
||||||
)
|
onClose: onClose
|
||||||
|
|
||||||
Divider()
|
|
||||||
.background(Color.gray.opacity(0.3))
|
|
||||||
|
|
||||||
// Content
|
|
||||||
if filteredItems.isEmpty {
|
|
||||||
ClipboardPanelEmptyState(
|
|
||||||
hasSearch: !searchText.isEmpty,
|
|
||||||
isHistoryTab: selectedTab == .history
|
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
ScrollView {
|
Divider()
|
||||||
LazyVStack(spacing: 1) {
|
.background(Color.gray.opacity(0.3))
|
||||||
ForEach(filteredItems) { item in
|
|
||||||
ClipboardPanelItemRow(
|
// Content
|
||||||
item: item,
|
if filteredItems.isEmpty {
|
||||||
isHovered: hoveredItemId == item.id,
|
ClipboardPanelEmptyState(
|
||||||
isPinned: clipboardManager.pinnedItems.contains(where: { $0.id == item.id })
|
hasSearch: !searchText.isEmpty,
|
||||||
) { hoverId in
|
isHistoryTab: selectedTab == .history
|
||||||
hoveredItemId = hoverId
|
)
|
||||||
|
} else {
|
||||||
|
ScrollView {
|
||||||
|
LazyVStack(spacing: 1) {
|
||||||
|
ForEach(filteredItems) { item in
|
||||||
|
ClipboardPanelItemRow(
|
||||||
|
item: item,
|
||||||
|
isHovered: hoveredItemId == item.id,
|
||||||
|
isPinned: clipboardManager.pinnedItems.contains(where: { $0.id == item.id })
|
||||||
|
) { hoverId in
|
||||||
|
hoveredItemId = hoverId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 8)
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(width: 320, height: 400)
|
||||||
|
.background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
||||||
|
.cornerRadius(12)
|
||||||
|
.shadow(color: .black.opacity(0.4), radius: 20, x: 0, y: 10)
|
||||||
|
|
||||||
|
// Close button positioned in top-left corner
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Button(action: onClose) {
|
||||||
|
Image(systemName: "xmark")
|
||||||
|
.font(.system(size: 12, weight: .medium))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(width: 24, height: 24)
|
||||||
|
.background(Color.red)
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
.padding(.leading, 12)
|
||||||
|
.padding(.top, 12)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(width: 320, height: 400)
|
|
||||||
.background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
|
||||||
.cornerRadius(12)
|
|
||||||
.shadow(color: .black.opacity(0.4), radius: 20, x: 0, y: 10)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,13 +256,6 @@ struct ClipboardPanelHeader: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
.disabled(selectedTab == .history ? clipboardManager.clipboardHistory.isEmpty : clipboardManager.pinnedItems.isEmpty)
|
.disabled(selectedTab == .history ? clipboardManager.clipboardHistory.isEmpty : clipboardManager.pinnedItems.isEmpty)
|
||||||
|
|
||||||
Button(action: onClose) {
|
|
||||||
Image(systemName: "xmark")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.font(.system(size: 12, weight: .medium))
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
.padding(.top, 12)
|
.padding(.top, 12)
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,12 @@ class ColorPickerPanelManager: ObservableObject {
|
||||||
hideColorPickerPanel() // Close any existing panel
|
hideColorPickerPanel() // Close any existing panel
|
||||||
|
|
||||||
let newPanel = ColorPickerPanel()
|
let newPanel = ColorPickerPanel()
|
||||||
newPanel.positionPanel()
|
|
||||||
|
|
||||||
self.panel = newPanel
|
self.panel = newPanel
|
||||||
|
|
||||||
// Make the panel key and order front to ensure it can receive focus
|
// Position the panel
|
||||||
|
newPanel.positionPanel()
|
||||||
|
|
||||||
|
// Make the panel visible and focused
|
||||||
newPanel.makeKeyAndOrderFront(nil)
|
newPanel.makeKeyAndOrderFront(nil)
|
||||||
newPanel.orderFrontRegardless()
|
newPanel.orderFrontRegardless()
|
||||||
|
|
||||||
|
|
@ -60,35 +61,52 @@ class ColorPickerPanelManager: ObservableObject {
|
||||||
|
|
||||||
class ColorPickerPanel: NSPanel {
|
class ColorPickerPanel: NSPanel {
|
||||||
override var canBecomeKey: Bool { true }
|
override var canBecomeKey: Bool { true }
|
||||||
override var canBecomeMain: Bool { false }
|
override var canBecomeMain: Bool { true }
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(
|
super.init(
|
||||||
contentRect: NSRect(x: 0, y: 0, width: 450, height: 600),
|
contentRect: .zero,
|
||||||
styleMask: [.titled, .closable, .fullSizeContentView],
|
styleMask: [.borderless, .nonactivatingPanel],
|
||||||
backing: .buffered,
|
backing: .buffered,
|
||||||
defer: false
|
defer: true
|
||||||
)
|
)
|
||||||
|
|
||||||
setupPanel()
|
setupPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupPanel() {
|
private func setupPanel() {
|
||||||
title = "Color Picker"
|
backgroundColor = .clear
|
||||||
level = .floating
|
|
||||||
isMovableByWindowBackground = true
|
|
||||||
backgroundColor = NSColor.clear
|
|
||||||
isOpaque = false
|
isOpaque = false
|
||||||
hasShadow = true
|
hasShadow = true
|
||||||
|
level = .floating
|
||||||
|
isMovableByWindowBackground = true
|
||||||
titlebarAppearsTransparent = true
|
titlebarAppearsTransparent = true
|
||||||
titleVisibility = .hidden
|
titleVisibility = .hidden
|
||||||
|
isFloatingPanel = true
|
||||||
|
|
||||||
|
// Allow dragging from any part of the window
|
||||||
|
styleMask.insert(.fullSizeContentView)
|
||||||
|
|
||||||
|
collectionBehavior = [
|
||||||
|
.canJoinAllSpaces,
|
||||||
|
.stationary,
|
||||||
|
.fullScreenAuxiliary // Float above full-screen apps
|
||||||
|
]
|
||||||
|
|
||||||
|
// Accept mouse moved events for proper hover behavior
|
||||||
|
acceptsMouseMovedEvents = true
|
||||||
|
|
||||||
let panelView = ColorPickerPanelView {
|
let panelView = ColorPickerPanelView {
|
||||||
ColorPickerPanelManager.shared.hideColorPickerPanel()
|
ColorPickerPanelManager.shared.hideColorPickerPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView = NSHostingView(rootView: panelView)
|
let hostingView = NSHostingView(rootView: panelView)
|
||||||
|
contentView = hostingView
|
||||||
|
|
||||||
|
// Set initial size
|
||||||
|
let preferredSize = CGSize(width: 450, height: 600)
|
||||||
|
hostingView.setFrameSize(preferredSize)
|
||||||
|
setContentSize(preferredSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func positionPanel() {
|
func positionPanel() {
|
||||||
|
|
@ -97,11 +115,46 @@ class ColorPickerPanel: NSPanel {
|
||||||
let screenFrame = screen.visibleFrame
|
let screenFrame = screen.visibleFrame
|
||||||
let panelFrame = frame
|
let panelFrame = frame
|
||||||
|
|
||||||
let xPosition = screenFrame.midX - panelFrame.width / 2
|
// Check if we have a saved position
|
||||||
let yPosition = screenFrame.midY - panelFrame.height / 2
|
if let savedPosition = getSavedPosition() {
|
||||||
|
// Validate saved position is still on screen
|
||||||
|
let savedFrame = NSRect(origin: savedPosition, size: panelFrame.size)
|
||||||
|
if screenFrame.intersects(savedFrame) {
|
||||||
|
setFrameOrigin(savedPosition)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to center of screen (not top center)
|
||||||
|
let xPosition = (screenFrame.width - panelFrame.width) / 2 + screenFrame.minX
|
||||||
|
let yPosition = (screenFrame.height - panelFrame.height) / 2 + screenFrame.minY
|
||||||
|
|
||||||
setFrameOrigin(NSPoint(x: xPosition, y: yPosition))
|
setFrameOrigin(NSPoint(x: xPosition, y: yPosition))
|
||||||
print("ColorPicker: Panel positioned at \(xPosition), \(yPosition)")
|
}
|
||||||
|
|
||||||
|
private func getSavedPosition() -> NSPoint? {
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let x = defaults.double(forKey: "colorPickerPanelPositionX")
|
||||||
|
let y = defaults.double(forKey: "colorPickerPanelPositionY")
|
||||||
|
|
||||||
|
// Check if we have valid saved coordinates (not default 0.0)
|
||||||
|
if x != 0.0 || y != 0.0 {
|
||||||
|
return NSPoint(x: x, y: y)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveCurrentPosition() {
|
||||||
|
let currentOrigin = frame.origin
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
defaults.set(currentOrigin.x, forKey: "colorPickerPanelPositionX")
|
||||||
|
defaults.set(currentOrigin.y, forKey: "colorPickerPanelPositionY")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setFrameOrigin(_ point: NSPoint) {
|
||||||
|
super.setFrameOrigin(point)
|
||||||
|
// Save position whenever it changes (user dragging)
|
||||||
|
saveCurrentPosition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,16 +179,38 @@ struct ColorPickerPanelView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
ZStack {
|
||||||
headerSection
|
VStack(spacing: 0) {
|
||||||
|
headerSection
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.background(Color.gray.opacity(0.3))
|
||||||
|
|
||||||
|
contentSection
|
||||||
|
}
|
||||||
|
.background(ColorPickerVisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
||||||
|
.cornerRadius(12)
|
||||||
|
|
||||||
Divider()
|
// Close button positioned in top-left corner
|
||||||
.background(Color.gray.opacity(0.3))
|
VStack {
|
||||||
|
HStack {
|
||||||
contentSection
|
Button(action: onClose) {
|
||||||
|
Image(systemName: "xmark")
|
||||||
|
.font(.system(size: 12, weight: .medium))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(width: 24, height: 24)
|
||||||
|
.background(Color.red)
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
.padding(.leading, 12)
|
||||||
|
.padding(.top, 12)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.background(ColorPickerVisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
|
||||||
.cornerRadius(12)
|
|
||||||
.alert("Delete Color", isPresented: $showingDeleteAlert) {
|
.alert("Delete Color", isPresented: $showingDeleteAlert) {
|
||||||
Button("Cancel", role: .cancel) { }
|
Button("Cancel", role: .cancel) { }
|
||||||
Button("Delete", role: .destructive) {
|
Button("Delete", role: .destructive) {
|
||||||
|
|
@ -195,14 +270,6 @@ struct ColorPickerPanelView: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
.disabled(colorPickerManager.colorHistory.isEmpty)
|
.disabled(colorPickerManager.colorHistory.isEmpty)
|
||||||
|
|
||||||
// Close Button
|
|
||||||
Button(action: onClose) {
|
|
||||||
Image(systemName: "xmark")
|
|
||||||
.font(.system(size: 14, weight: .medium))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue