diff --git a/alt-tab-macos.xcodeproj/project.pbxproj b/alt-tab-macos.xcodeproj/project.pbxproj index 8f141b72..38618bac 100644 --- a/alt-tab-macos.xcodeproj/project.pbxproj +++ b/alt-tab-macos.xcodeproj/project.pbxproj @@ -53,7 +53,6 @@ 1C961BD37A4652E7A692ED8A /* thumbnails_show_running_applications_light@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1C961484E5164C1BFF3B78FF /* thumbnails_show_running_applications_light@2x.jpg */; }; 1C961C3245C17EC408B798DE /* app_icons_hide_preview_focused_window_light@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1C961A9B3F8ABA1ECFDF7C9F /* app_icons_hide_preview_focused_window_light@2x.jpg */; }; 1C961C7FC66CEEE4223C57A2 /* thumbnails_hide_space_number_labels_light@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1C9611334B667767A4514B57 /* thumbnails_hide_space_number_labels_light@2x.jpg */; }; - 1C961CB7E9B170E1CD99881F /* info_button@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C9616ABADF052D54E9375C2 /* info_button@2x.png */; }; 1C961D268F3911907129C96A /* titles_show_windowless_apps_light@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1C9618877FB96FD51E89A597 /* titles_show_windowless_apps_light@2x.jpg */; }; 1C961DD2AB893820F334BBA4 /* thumbnails_hide_preview_focused_window_light@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1C961802AB937EC23AADBBFA /* thumbnails_hide_preview_focused_window_light@2x.jpg */; }; 1C961DE40FF784E02A044D35 /* thumbnails_hide_status_icons_light@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1C9616C88A2EED5DFF4E83D8 /* thumbnails_hide_status_icons_light@2x.jpg */; }; @@ -291,7 +290,6 @@ 1C9616522F4E58860385E090 /* .swiftformat */ = {isa = PBXFileReference; lastKnownFileType = file.swiftformat; path = .swiftformat; sourceTree = ""; }; 1C9616610337AE2361694C63 /* titles_hide_preview_focused_window_light@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "titles_hide_preview_focused_window_light@2x.jpg"; sourceTree = ""; }; 1C96168DC0D8707F8EC6B442 /* titles_show_running_applications_windows_light@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "titles_show_running_applications_windows_light@2x.jpg"; sourceTree = ""; }; - 1C9616ABADF052D54E9375C2 /* info_button@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "info_button@2x.png"; sourceTree = ""; }; 1C9616C88A2EED5DFF4E83D8 /* thumbnails_hide_status_icons_light@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "thumbnails_hide_status_icons_light@2x.jpg"; sourceTree = ""; }; 1C9617A90991E963760F7475 /* thumbnails_show_app_badges_light@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "thumbnails_show_app_badges_light@2x.jpg"; sourceTree = ""; }; 1C9617C5839903D752070CF2 /* ImageTextButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageTextButtonView.swift; sourceTree = ""; }; @@ -716,14 +714,6 @@ path = illustrations; sourceTree = ""; }; - 1C961A5CDC3134CCB0811D78 /* preferences */ = { - isa = PBXGroup; - children = ( - 1C9616ABADF052D54E9375C2 /* info_button@2x.png */, - ); - path = preferences; - sourceTree = ""; - }; 1C961B4542D84D5B6E937B15 /* workflows */ = { isa = PBXGroup; children = ( @@ -1484,7 +1474,6 @@ BF0C86C288D4FD8F92D3C7DB /* app */, BF0C80536B40523C5DB23BE2 /* menubar */, BF0C8060939B50725E1C3392 /* permission-window */, - 1C961A5CDC3134CCB0811D78 /* preferences */, ); path = icons; sourceTree = ""; @@ -2004,7 +1993,6 @@ BF0C816911CAD3F1B5865BFA /* Localizable.strings in Resources */, BF0C863526C260F4155F9D2C /* Localizable.strings in Resources */, BF0C8C0EDECEA32BBCE5B27D /* Localizable.strings in Resources */, - 1C961CB7E9B170E1CD99881F /* info_button@2x.png in Resources */, 1C96150A9E75C3D69638C481 /* app_icons_light@2x.jpg in Resources */, 1C9617DCCF162B8F8CBF2C1D /* app_icons_align_thumbnails_center_light@2x.jpg in Resources */, 1C961F211FCEFE8A725563DD /* app_icons_align_thumbnails_leading_light@2x.jpg in Resources */, diff --git a/resources/SF-Pro-Text-Regular.otf b/resources/SF-Pro-Text-Regular.otf index c7049419..13608599 100644 Binary files a/resources/SF-Pro-Text-Regular.otf and b/resources/SF-Pro-Text-Regular.otf differ diff --git a/resources/icons/preferences/info_button@2x.png b/resources/icons/preferences/info_button@2x.png deleted file mode 100644 index 73b0f6f6..00000000 Binary files a/resources/icons/preferences/info_button@2x.png and /dev/null differ diff --git a/scripts/subset_font.sh b/scripts/subset_font.sh index 582c3aa9..de5df25e 100755 --- a/scripts/subset_font.sh +++ b/scripts/subset_font.sh @@ -4,4 +4,4 @@ set -exu "$(pipenv --venv)"/bin/pyftsubset resources/SF-Pro-Text-Regular-Full.otf \ --output-file=resources/SF-Pro-Text-Regular.otf \ - --text="􀥃􀀁􀁎􀁌􀕧􀀸􀀺􀀼􀀾􀁀􀁂􀁄􀑱􀁆􀁈􀁊􀑳􀓵􀓶􀓷􀓸􀓹􀓺􀓻􀓼􀓽􀓾􀓿􀔀􀔁􀔂􀔃􀔄􀔅􀔆􀔇􀔈􀔉􀕬􀀹􀀻􀀽􀀿􀁁􀘘􀁃􀁅􀑲􀁇􀁉􀁋􀑴􀔔􀔕􀔖􀔗􀔘􀔙􀔚􀔛􀔜􀔝􀔞􀔟􀔠􀔡􀔢􀔣􀔤􀔥􀔦􀔧􀔨􀕭" + --text="􀀁􀅴􀁎􀁌􀕧􀀸􀀺􀀼􀀾􀁀􀁂􀁄􀑱􀁆􀁈􀁊􀑳􀓵􀓶􀓷􀓸􀓹􀓺􀓻􀓼􀓽􀓾􀓿􀔀􀔁􀔂􀔃􀔄􀔅􀔆􀔇􀔈􀔉􀕬􀀹􀀻􀀽􀀿􀁁􀘘􀁃􀁅􀑲􀁇􀁉􀁋􀑴􀔔􀔕􀔖􀔗􀔘􀔙􀔚􀔛􀔜􀔝􀔞􀔟􀔠􀔡􀔢􀔣􀔤􀔥􀔦􀔧􀔨􀕭" diff --git a/src/ui/main-window/ThumbnailFontIconView.swift b/src/ui/main-window/ThumbnailFontIconView.swift index 5faa8392..4cfe233c 100644 --- a/src/ui/main-window/ThumbnailFontIconView.swift +++ b/src/ui/main-window/ThumbnailFontIconView.swift @@ -11,6 +11,7 @@ enum Symbols: String { case filledCircled = "􀀁" case filledCircledNumber0 = "􀀹" case filledCircledNumber10 = "􀔔" + case circledInfo = "􀅴" } // Font icon using SF Symbols from the SF Pro font from Apple diff --git a/src/ui/preferences-window/LabelAndControl.swift b/src/ui/preferences-window/LabelAndControl.swift index d7848995..b17ef17b 100644 --- a/src/ui/preferences-window/LabelAndControl.swift +++ b/src/ui/preferences-window/LabelAndControl.swift @@ -32,13 +32,13 @@ class MouseHoverView: NSView { } class ClickHoverImageView: MouseHoverView { - var imageView: NSImageView! + var infoCircle: NSView! var onClick: EventClosure? - init(imageView: NSImageView) { + init(infoCircle: NSView) { super.init(frame: .zero) - self.imageView = imageView - addSubview(imageView) + self.infoCircle = infoCircle + addSubview(infoCircle) let clickGesture = NSClickGestureRecognizer(target: self, action: #selector(handleClick(_:))) addGestureRecognizer(clickGesture) @@ -130,37 +130,21 @@ class LabelAndControl: NSObject { return checkbox } - static func makeInfoButton(width: CGFloat = 15, - height: CGFloat = 15, - padding: CGFloat = 2, + static func makeInfoButton(size: CGFloat = 18, onClick: EventClosure? = nil, onMouseEntered: EventClosure? = nil, onMouseExited: EventClosure? = nil) -> ClickHoverImageView { - let imageView = NSImageView(image: NSImage(named: "info_button")!) - imageView.translatesAutoresizingMaskIntoConstraints = false - imageView.imageScaling = .scaleProportionallyUpOrDown - - // Enable layer-backed view to improve rendering quality - imageView.wantsLayer = true - imageView.layer?.contentsGravity = .resizeAspect - imageView.layer?.shouldRasterize = true - imageView.layer?.rasterizationScale = NSScreen.main?.backingScaleFactor ?? 1.0 - - let view = ClickHoverImageView(imageView: imageView) + let imageView = ThumbnailFontIconView(symbol: .circledInfo, size: size, color: .labelColor, shadow: nil) + let view = ClickHoverImageView(infoCircle: imageView) view.onClick = onClick view.onMouseEntered = onMouseEntered view.onMouseExited = onMouseExited - - // Set constraints to add equal padding around the image NSLayoutConstraint.activate([ - imageView.widthAnchor.constraint(equalToConstant: width), - imageView.heightAnchor.constraint(equalToConstant: height), - imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: padding), - imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding), - imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding), - imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding), + imageView.topAnchor.constraint(equalTo: view.topAnchor), + imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor), ]) - return view } @@ -172,10 +156,9 @@ class LabelAndControl: NSObject { onClick: EventClosure? = nil, onMouseEntered: EventClosure? = nil, onMouseExited: EventClosure? = nil, - width: CGFloat = 15, - height: CGFloat = 15) -> [NSView] { + size: CGFloat = 15) -> [NSView] { let labelCheckboxViews = makeLabelWithCheckbox(labelText, rawName, extraAction: extraAction, labelPosition: labelPosition) - let infoButtonView = makeInfoButton(width: width, height: height, onClick: onClick, onMouseEntered: onMouseEntered, onMouseExited: onMouseExited) + let infoButtonView = makeInfoButton(size: size, onClick: onClick, onMouseEntered: onMouseEntered, onMouseExited: onMouseExited) var views: [NSView] = [] labelCheckboxViews.forEach { view in diff --git a/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift b/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift index 1178e3a5..15664c9b 100644 --- a/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift +++ b/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift @@ -30,7 +30,7 @@ class IllustratedImageThemeView: ClickHoverImageView { imageView.layer?.masksToBounds = true imageView.layer?.cornerRadius = TableGroupView.cornerRadius - super.init(imageView: imageView) + super.init(infoCircle: imageView) self.style = style self.theme = theme self.imageName = imageName @@ -77,12 +77,12 @@ class IllustratedImageThemeView: ClickHoverImageView { if highlighted { updateImage(imageName) } else { - self.imageView.image = NSImage(named: self.imageName) + (infoCircle as! NSImageView).image = NSImage(named: self.imageName) } } private func updateImage(_ imageName: String) { - imageView.image = NSImage(named: self.getStyleThemeImageName(imageName)) + (infoCircle as! NSImageView).image = NSImage(named: getStyleThemeImageName(imageName)) } static func getConcatenatedImageName(_ style: AppearanceStylePreference, @@ -214,7 +214,7 @@ class ShowHideIllustratedView { showTabsAsWindows.supportedStyles = [.thumbnails, .appIcons, .titles] showTabsAsWindows.leftViews = [TableGroupView.makeText(NSLocalizedString("Show standard tabs as windows", comment: ""))] showTabsAsWindows.subTitle = NSLocalizedString("Some apps like Finder or Preview use standard tabs which act like independent windows. Some other apps like web browsers use custom tabs which act in unique ways and are not actual windows. AltTab can't list those separately.", comment: "") - showTabsAsWindows.rightViews.append(LabelAndControl.makeInfoButton(width: 15, height: 15, onMouseEntered: { event, view in + showTabsAsWindows.rightViews.append(LabelAndControl.makeInfoButton(onMouseEntered: { event, view in if ShowHideIllustratedView.isDisabledOnApplications(showTabsAsWindows) { Popover.shared.show(event: event, positioningView: view, message: featureUnavailable) } else { @@ -237,7 +237,7 @@ class ShowHideIllustratedView { previewFocusedWindow.supportedStyles = [.thumbnails, .appIcons, .titles] previewFocusedWindow.leftViews = [TableGroupView.makeText(NSLocalizedString("Preview selected window", comment: ""))] previewFocusedWindow.subTitle = NSLocalizedString("Preview the selected window.", comment: "") - previewFocusedWindow.rightViews.append(LabelAndControl.makeInfoButton(width: 15, height: 15, onMouseEntered: { event, view in + previewFocusedWindow.rightViews.append(LabelAndControl.makeInfoButton(onMouseEntered: { event, view in if ShowHideIllustratedView.isDisabledOnApplications(previewFocusedWindow) { Popover.shared.show(event: event, positioningView: view, message: featureUnavailable) } else { @@ -329,11 +329,13 @@ class ShowHideIllustratedView { } } -class Popover: NSPopover { +class Popover: NSPopover, NSPopoverDelegate { static let shared = Popover() + private var hidingInitiated = true override init() { super.init() + delegate = self contentViewController = NSViewController() behavior = .semitransient } @@ -342,12 +344,18 @@ class Popover: NSPopover { fatalError("init(coder:) has not been implemented") } + func popoverWillClose(_ notification: Notification) { + hidingInitiated = true + } + func hide() { performClose(nil) } - func show(event: NSEvent, positioningView: NSView, message: String) { - hide() + func show(event: NSEvent, positioningView: NSView, message: String, extraView: NSView? = nil) { + if !hidingInitiated { return } + hidingInitiated = false + let view = NSView() let label = NSTextField(labelWithString: message) @@ -356,16 +364,16 @@ class Popover: NSPopover { label.maximumNumberOfLines = 0 label.isEditable = false label.isSelectable = true - label.textColor = NSColor.gray label.font = NSFont.systemFont(ofSize: 12) - view.addSubview(label) + var actualView: NSView = extraView == nil ? label : StackView([label, extraView!], .vertical) + view.addSubview(actualView) NSLayoutConstraint.activate([ view.widthAnchor.constraint(lessThanOrEqualToConstant: 400), - label.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), - label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10), - label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10), - label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10), + actualView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), + actualView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10), + actualView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10), + actualView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10), ]) contentViewController?.view = view