Skip to content

Commit

Permalink
feat: improve window preview shape (#93)
Browse files Browse the repository at this point in the history
* feat: improve window preview shape

Since not all apps have the same level of window corner rounding, we simply set a transparent background, and display the window as it is instead of interfering with its shape.
In addition, we define the gradient overlay with a mask so that it does not color the transparent corners as well.

* refactor gradient code, properly apply image mask

* image radius option

---------

Co-authored-by: Ethan Bills <ethanjacksonbills@gmail.com>
  • Loading branch information
ShlomoCode and ejbills authored Jul 8, 2024
1 parent 0262138 commit 0d5cb46
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 15 deletions.
3 changes: 2 additions & 1 deletion DockDoor/Utilities/WindowUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ final class WindowUtil {

// Configure the stream to capture only the window content
config.scalesToFit = false
config.backgroundColor = .clear
config.ignoreGlobalClipDisplay = true
config.ignoreShadowsDisplay = true
config.shouldBeOpaque = true
config.shouldBeOpaque = false
if #available(macOS 14.2, *) { config.includeChildWindows = false }
config.width = Int(window.frame.width)
config.height = Int(window.frame.height)
Expand Down
7 changes: 5 additions & 2 deletions DockDoor/Views/Hover Window/HoverWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ struct HoverView: View {
let dockPosition: DockPosition
let bestGuessMonitor: NSScreen

@Default(.uniformCardRadius) var uniformCardRadius

@State private var showWindows: Bool = false
@State private var hasAppeared: Bool = false
@State private var appIcon: NSImage? = nil
Expand Down Expand Up @@ -304,7 +306,7 @@ struct HoverView: View {
ForEach(activeWindows.indices, id: \.self) { index in
WindowPreview(windowInfo: activeWindows[index], onTap: onWindowTap, index: index,
dockPosition: dockPosition, maxWindowDimension: maxWindowDimension,
bestGuessMonitor: bestGuessMonitor)
bestGuessMonitor: bestGuessMonitor, uniformCardRadius: uniformCardRadius)
.id("\(appName)-\(index)")
}
}
Expand Down Expand Up @@ -401,7 +403,8 @@ struct HoverView: View {
index: index,
dockPosition: dockPosition,
maxWindowDimension: maxWindowDimension,
bestGuessMonitor: bestGuessMonitor
bestGuessMonitor: bestGuessMonitor,
uniformCardRadius: true // force it to be rounded, since these have no image previews
)
}
}
Expand Down
30 changes: 18 additions & 12 deletions DockDoor/Views/Hover Window/WindowPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct WindowPreview: View {
let dockPosition: DockPosition
let maxWindowDimension: CGPoint
let bestGuessMonitor: NSScreen
let uniformCardRadius: Bool

@State private var isHovering = false
@State private var isHoveringOverTabMenu = false
Expand Down Expand Up @@ -43,6 +44,16 @@ struct WindowPreview: View {
return CGSize(width: targetWidth, height: targetHeight)
}

private func fluidGradient() -> some View {
FluidGradient(
blobs: [.purple, .blue, .green, .yellow, .red, .purple].shuffled(),
highlights: [.red, .orange, .pink, .blue, .purple].shuffled(),
speed: 0.45,
blur: 0.75
)
.opacity(0.125)
}

private func windowContent(isMinimized: Bool, isHidden: Bool, isSelected: Bool) -> some View {
Group {
if isMinimized || isHidden {
Expand Down Expand Up @@ -72,17 +83,12 @@ struct WindowPreview: View {
.padding()
.frame(width: width)
.frame(height: 60)
.overlay { if isSelected { fluidGradient() }}
} else if let cgImage = windowInfo.image {
Image(decorative: cgImage, scale: 1.0)
.resizable()
.aspectRatio(contentMode: .fill)
let image = Image(decorative: cgImage, scale: 1.0).resizable().aspectRatio(contentMode: .fill)
image.overlay(!isSelected ? nil : fluidGradient().mask(image))
}
}
.overlay { if isSelected { FluidGradient(blobs: [.purple, .blue, .green, .yellow, .red, .purple].shuffled(),
highlights: [.red, .orange, .pink, .blue, .purple].shuffled(),
speed: 0.45,
blur: 0.75).opacity(0.125)
}}
.frame(width: isMinimized || isHidden ? nil : calculatedSize.width,
height: isMinimized || isHidden ? nil : calculatedSize.height,
alignment: .center)
Expand All @@ -97,12 +103,12 @@ struct WindowPreview: View {
VStack(spacing: 0) {
windowContent(isMinimized: windowInfo.isMinimized, isHidden: windowInfo.isHidden, isSelected: selected)
.overlay { Color.white.opacity(isHoveringOverTabMenu ? 0.1 : 0) }
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
.shadow(radius: selected || isHoveringOverTabMenu ? 0 : 3)
.background {
RoundedRectangle(cornerRadius: 6, style: .continuous)
.fill(Color.clear.shadow(.drop(color: .black.opacity(selected ? 0.35 : 0.25), radius: selected ? 12 : 8, y: selected ? 6 : 4)))
}
.clipShape(uniformCardRadius ? AnyShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) : AnyShape(Rectangle()))
}
.overlay(alignment: .bottomLeading) {
if selected, let windowTitle = windowInfo.window?.title, !windowTitle.isEmpty, windowTitle != windowInfo.appName {
Expand Down Expand Up @@ -195,17 +201,17 @@ struct WindowPreview: View {
.contentShape(Rectangle())
.onHover { over in
withAnimation(.snappy(duration: 0.175)) {
if !CurrentWindow.shared.showingTabMenu {
if (!CurrentWindow.shared.showingTabMenu) {
isHovering = over
} else {
isHoveringOverTabMenu = over
}
}
}
.onTapGesture {
if windowInfo.isMinimized {
if (windowInfo.isMinimized) {
WindowUtil.toggleMinimize(windowInfo: windowInfo)
} else if windowInfo.isHidden {
} else if (windowInfo.isHidden) {
WindowUtil.toggleHidden(windowInfo: windowInfo)
} else {
WindowUtil.bringWindowToFront(windowInfo: windowInfo)
Expand Down
5 changes: 5 additions & 0 deletions DockDoor/Views/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct SettingsView: View {
@Default(.screenCaptureCacheLifespan) var screenCaptureCacheLifespan
@Default(.showAnimations) var showAnimations
@Default(.showMenuBarIcon) var showMenuBarIcon
@Default(.uniformCardRadius) var uniformCardRadius

var body: some View {
VStack(alignment: .leading, spacing: 10) {
Expand All @@ -31,6 +32,10 @@ struct SettingsView: View {
Text("Enable Hover Window Sliding Animation")
})

Toggle(isOn: $uniformCardRadius, label: {
Text("Use Uniform Image Preview Radius")
})

Toggle(isOn: $showMenuBarIcon, label: {
Text("Show Menu Bar Icon")
})
Expand Down
3 changes: 3 additions & 0 deletions DockDoor/consts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ extension Defaults.Keys {
static let sizingMultiplier = Key<CGFloat>("sizingMultiplier") { 3 }
static let windowPadding = Key<CGFloat>("windowPadding") { 0 }
static let openDelay = Key<CGFloat>("openDelay") { 0 }

static let screenCaptureCacheLifespan = Key<CGFloat>("screenCaptureCacheLifespan") { 60 }
static let uniformCardRadius = Key<Bool>("uniformCardRadius") { true }

static let showAnimations = Key<Bool>("showAnimations") { true }
static let enableWindowSwitcher = Key<Bool>("enableWindowSwitcher"){ true }
static let showMenuBarIcon = Key<Bool>("showMenuBarIcon", default: true)
Expand Down

0 comments on commit 0d5cb46

Please sign in to comment.