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

UI improvements #1036

Closed
wants to merge 9 commits into from
Closed
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
4 changes: 4 additions & 0 deletions Maccy.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
DA19691C2C3F3EAC00258481 /* Collection+Surrounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA19691B2C3F3EAC00258481 /* Collection+Surrounding.swift */; };
DA19691F2C3F5F0600258481 /* KeyHandlingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA19691E2C3F5F0600258481 /* KeyHandlingView.swift */; };
DA1969212C3F6C6800258481 /* HistoryItemAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1969202C3F6C6800258481 /* HistoryItemAction.swift */; };
DA1B106D2D42F044009BE364 /* HeaderItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1B106C2D42F041009BE364 /* HeaderItemView.swift */; };
DA1EDE432045B35300479723 /* HistoryDecoratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1EDE422045B35300479723 /* HistoryDecoratorTests.swift */; };
DA20FA722B082DD600056DD5 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA20FA712B082DD600056DD5 /* Notifier.swift */; };
DA20FA742B082E0100056DD5 /* Knock.caf in Resources */ = {isa = PBXBuildFile; fileRef = DA20FA732B082E0100056DD5 /* Knock.caf */; };
Expand Down Expand Up @@ -200,6 +201,7 @@
DA19691B2C3F3EAC00258481 /* Collection+Surrounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Surrounding.swift"; sourceTree = "<group>"; };
DA19691E2C3F5F0600258481 /* KeyHandlingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlingView.swift; sourceTree = "<group>"; };
DA1969202C3F6C6800258481 /* HistoryItemAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryItemAction.swift; sourceTree = "<group>"; };
DA1B106C2D42F041009BE364 /* HeaderItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderItemView.swift; sourceTree = "<group>"; };
DA1EDE422045B35300479723 /* HistoryDecoratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryDecoratorTests.swift; sourceTree = "<group>"; };
DA20FA712B082DD600056DD5 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
DA20FA732B082E0100056DD5 /* Knock.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Knock.caf; sourceTree = "<group>"; };
Expand Down Expand Up @@ -596,6 +598,7 @@
isa = PBXGroup;
children = (
DAA072DE2C41C63C006DDFD2 /* ConfirmationView.swift */,
DA1B106C2D42F041009BE364 /* HeaderItemView.swift */,
DA243D112C2F64820012A27F /* ContentView.swift */,
DAA072DC2C41C61F006DDFD2 /* FooterItemView.swift */,
DAA072E02C41C6E8006DDFD2 /* FooterView.swift */,
Expand Down Expand Up @@ -1044,6 +1047,7 @@
DA9C3C602C20E1890056795D /* IgnorePasteboardTypesSettingsView.swift in Sources */,
DA083A6E2C42E8E8004259A0 /* NSImage+Resized.swift in Sources */,
DAA072E12C41C6E8006DDFD2 /* FooterView.swift in Sources */,
DA1B106D2D42F044009BE364 /* HeaderItemView.swift in Sources */,
2F1A79C02C6DFB7800C98EBD /* SearchVisibility.swift in Sources */,
DAA072DD2C41C61F006DDFD2 /* FooterItemView.swift in Sources */,
DA3EAE0C2AE30F7500F39108 /* NSApplication+Windows.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions Maccy/Extensions/Defaults.Keys+Names.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extension Defaults.Keys {
static let pasteByDefault = Key<Bool>("pasteByDefault", default: false)
static let pinTo = Key<PinsPosition>("pinTo", default: .top)
static let popupPosition = Key<PopupPosition>("popupPosition", default: .cursor)
static let popupOrigin = Key<PopupPosition>("popupOrigin", default: .cursor)
lucianmocan marked this conversation as resolved.
Show resolved Hide resolved
static let popupScreen = Key<Int>("popupScreen", default: 0)
static let previewDelay = Key<Int>("previewDelay", default: 1500)
static let removeFormattingByDefault = Key<Bool>("removeFormattingByDefault", default: false)
Expand All @@ -55,6 +56,7 @@ extension Defaults.Keys {
static let size = Key<Int>("historySize", default: 200)
static let sortBy = Key<Sorter.By>("sortBy", default: .lastCopiedAt)
static let suppressClearAlert = Key<Bool>("suppressClearAlert", default: false)
static let historyListHeight = Key<CGFloat>("historyListHeight", default: 132)
static let windowSize = Key<NSSize>("windowSize", default: NSSize(width: 450, height: 800))
static let windowPosition = Key<NSPoint>("windowPosition", default: NSPoint(x: 0.5, y: 0.8))
static let showApplicationIcons = Key<Bool>("showApplicationIcons", default: false)
Expand Down
5 changes: 5 additions & 0 deletions Maccy/FloatingPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,14 @@ class FloatingPanel<Content: View>: NSPanel, NSWindowDelegate {

func toggle(height: CGFloat, at popupPosition: PopupPosition = Defaults[.popupPosition]) {
if isPresented {
Defaults[.popupOrigin] = Defaults[.popupPosition]
close()
} else {
Defaults[.popupOrigin] = popupPosition
open(height: height, at: popupPosition)
DispatchQueue.main.async {
self.verticallyResize(to: height)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Maccy/Observables/Popup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Observation

@Observable
class Popup {
let verticalPadding: CGFloat = 5
let verticalPadding: CGFloat = 10

var needsResize = false
var height: CGFloat = 0
Expand Down
18 changes: 18 additions & 0 deletions Maccy/Settings/AppearanceSettingsPane.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct AppearanceSettingsPane: View {
@Default(.showFooter) private var showFooter
@Default(.windowPosition) private var windowPosition
@Default(.showApplicationIcons) private var showApplicationIcons
@Default(.historyListHeight) private var historyListHeight

@State private var screens = NSScreen.screens

Expand Down Expand Up @@ -48,6 +49,13 @@ struct AppearanceSettingsPane: View {
return formatter
}()

private let historyListHeightFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.minimum = 100
formatter.maximum = 600
return formatter
}()

var body: some View {
Settings.Container(contentWidth: 650) {
Settings.Section(label: { Text("PopupAt", tableName: "AppearanceSettings") }) {
Expand Down Expand Up @@ -100,6 +108,16 @@ struct AppearanceSettingsPane: View {
}
}

Settings.Section(label: { Text("HistoryListHeight", tableName: "AppearanceSettings") }) {
HStack {
TextField("", value: $historyListHeight, formatter: historyListHeightFormatter)
.frame(width: 120)
.help(Text("HistoryListHeightTooltip", tableName: "AppearanceSettings"))
Stepper("", value: $historyListHeight, in: 100...600)
.labelsHidden()
}
}

Settings.Section(label: { Text("PreviewDelay", tableName: "AppearanceSettings") }) {
HStack {
TextField("", value: $previewDelay, formatter: previewDelayFormatter)
Expand Down
4 changes: 3 additions & 1 deletion Maccy/Settings/en.lproj/AppearanceSettings.strings
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"ImageHeightTooltip" = "Maximum image preview height.\nDefault: 40.\nHint: Set to 16 to look like text items.";
"PreviewDelay" = "Preview delay:";
"PreviewDelayTooltip" = "Delay in milliseconds until a preview popup is shown.\nDefault: 1500.";
"HistoryListHeight" = "History list height:";
"HistoryListHeightTooltip" = "Set height too see as many items from history as you would like.\nDefault: 132.";
"HighlightMatches" = "Highlight matches:";
"HighlightMatchColor" = "Color";
"HighlightMatchBold" = "Bold";
Expand All @@ -31,5 +33,5 @@
"ShowSearchField" = "Show search field";
"ShowTitleBeforeSearchField" = "Show title before search field";
"ShowApplicationIcons" = "Show application icons";
"ShowFooter" = "Show footer";
"ShowFooter" = "Show footer everywhere";
"OpenPreferencesWarning" = "⚠️ Press ⌘, to open preferences when footer is hidden.";
42 changes: 5 additions & 37 deletions Maccy/Views/FooterView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@ struct FooterView: View {
@Bindable var footer: Footer

@Environment(AppState.self) private var appState
@Environment(ModifierFlags.self) private var modifierFlags
@Default(.showFooter) private var showFooter
@State private var clearOpacity: Double = 1
@State private var clearAllOpacity: Double = 0
@Default(.popupOrigin) private var popupOrigin

var clearAllModifiersPressed: Bool {
let clearModifiers = footer.items[0].shortcuts.first?.modifierFlags ?? []
let clearAllModifiers = footer.items[1].shortcuts.first?.modifierFlags ?? []
return !modifierFlags.flags.isEmpty
&& !modifierFlags.flags.isSubset(of: clearModifiers)
&& modifierFlags.flags.isSubset(of: clearAllModifiers)
var isVisible: Bool {
showFooter || popupOrigin == .statusItem
}

var body: some View {
Expand All @@ -24,32 +18,6 @@ struct FooterView: View {
.padding(.horizontal, 10)
.padding(.vertical, 6)

ZStack {
FooterItemView(item: footer.items[0])
.opacity(clearOpacity)
FooterItemView(item: footer.items[1])
.opacity(clearAllOpacity)
}
.onChange(of: modifierFlags.flags) {
if clearAllModifiersPressed {
clearOpacity = 0
clearAllOpacity = 1
footer.items[0].isVisible = false
footer.items[1].isVisible = true
if appState.footer.selectedItem == footer.items[0] {
appState.selection = footer.items[1].id
}
} else {
clearOpacity = 1
clearAllOpacity = 0
footer.items[0].isVisible = true
footer.items[1].isVisible = false
if appState.footer.selectedItem == footer.items[1] {
appState.selection = footer.items[0].id
}
}
}

ForEach(footer.items.suffix(from: 2)) { item in
FooterItemView(item: item)
}
Expand All @@ -62,7 +30,7 @@ struct FooterView: View {
}
}
}
.opacity(showFooter ? 1 : 0)
.frame(maxHeight: showFooter ? nil : 0)
.opacity(isVisible ? 1 : 0)
.frame(maxHeight: isVisible ? nil : 0)
}
}
21 changes: 21 additions & 0 deletions Maccy/Views/HeaderItemView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// HeaderItemView.swift
// Maccy
//
// Created by Lucian Mocan on 23/01/2025.
// Copyright © 2025 p0deje. All rights reserved.
//

import SwiftUI

struct HeaderItemView: View {
@Bindable var item: FooterItem

var body: some View {
ConfirmationView(item: item) {
ListItemView(id: item.id, shortcuts: item.shortcuts, isSelected: item.isSelected) {
Text(LocalizedStringKey(item.title))
}
}
}
}
42 changes: 37 additions & 5 deletions Maccy/Views/HeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,48 @@ struct HeaderView: View {

@Environment(AppState.self) private var appState
@Environment(\.scenePhase) private var scenePhase
@Environment(ModifierFlags.self) private var modifierFlags
@State private var clearOpacity: Double = 1
@State private var clearAllOpacity: Double = 0

@Default(.showTitle) private var showTitle

var body: some View {
HStack {
HStack(spacing: 5) {
if showTitle {
Text("Maccy")
.foregroundStyle(.secondary)
.layoutPriority(1.5)
.frame(width: 40)
}

SearchFieldView(placeholder: "search_placeholder", query: $searchQuery)
.focused($searchFocused)
.frame(maxWidth: .infinity)
.onChange(of: scenePhase) {
if scenePhase == .background && !searchQuery.isEmpty {
searchQuery = ""
}
.layoutPriority(3)

// Add Clear buttons
ZStack {
HeaderItemView(item: appState.footer.items[0])
.opacity(clearOpacity)
HeaderItemView(item: appState.footer.items[1])
.opacity(clearAllOpacity)
}
.layoutPriority(1.5)
.frame(width: 150)
.onChange(of: modifierFlags.flags) {
if clearAllModifiersPressed {
clearOpacity = 0
clearAllOpacity = 1
appState.footer.items[0].isVisible = false
appState.footer.items[1].isVisible = true
} else {
clearOpacity = 1
clearAllOpacity = 0
appState.footer.items[0].isVisible = true
appState.footer.items[1].isVisible = false
}
}
}
.frame(height: appState.searchVisible ? 25 : 0)
.opacity(appState.searchVisible ? 1 : 0)
Expand All @@ -41,4 +65,12 @@ struct HeaderView: View {
}
}
}

var clearAllModifiersPressed: Bool {
let clearModifiers = appState.footer.items[0].shortcuts.first?.modifierFlags ?? []
let clearAllModifiers = appState.footer.items[1].shortcuts.first?.modifierFlags ?? []
return !modifierFlags.flags.isEmpty
&& !modifierFlags.flags.isSubset(of: clearModifiers)
&& modifierFlags.flags.isSubset(of: clearAllModifiers)
}
}
13 changes: 11 additions & 2 deletions Maccy/Views/HistoryListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct HistoryListView: View {

@Default(.pinTo) private var pinTo
@Default(.previewDelay) private var previewDelay
@Default(.historyListHeight) private var historyListHeight

private var pinnedItems: [HistoryItemDecorator] {
appState.history.pinnedItems.filter(\.isVisible)
Expand Down Expand Up @@ -48,8 +49,15 @@ struct HistoryListView: View {
ScrollView {
ScrollViewReader { proxy in
LazyVStack(spacing: 0) {
ForEach(unpinnedItems) { item in
HistoryItemView(item: item)
if pinnedItems.isEmpty && unpinnedItems.isEmpty {
Text("No clipboard items found.")
.foregroundColor(.secondary)
.padding(.top, 20)
.padding(.horizontal, 10)
} else {
ForEach(unpinnedItems) { item in
HistoryItemView(item: item)
}
}
}
.task(id: appState.scrollTarget) {
Expand Down Expand Up @@ -92,6 +100,7 @@ struct HistoryListView: View {
}
.contentMargins(.leading, 10, for: .scrollIndicators)
}
.frame(minHeight: historyListHeight)

if pinTo == .bottom {
LazyVStack(spacing: 0) {
Expand Down