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

Show Menu and Dock Icon whilst interacting #121

Merged
merged 19 commits into from
Jul 9, 2020
Merged
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
2 changes: 2 additions & 0 deletions Dozer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
if DozerIcons.shared.hideStatusBarIconsAtLaunch {
DozerIcons.shared.hide()
}

_ = DozerIcons.toggleDockIcon(showIcon: false)
}

// Show all Dozer icons when opening Dozer from Finder etc.
Expand Down
3 changes: 2 additions & 1 deletion Dozer/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extension Defaults.Keys {
static let hideAfterDelay: Defaults.Key<TimeInterval> = Key<TimeInterval>("hideAfterDelay", default: 10)
static let noIconMode: Defaults.Key<Bool> = Key<Bool>("noIconMode", default: false)
static let removeDozerIconEnabled: Defaults.Key<Bool> = Key<Bool>("removeStatusIconEnabled", default: false)
static let showIconAndMenuEnabled: Defaults.Key<Bool> = Key<Bool>("showIconAndMenuEnabled", default: false)
static let iconSize: Defaults.Key<Int> = Key<Int>("fontSize", default: 10)
static let buttonPadding: Defaults.Key<CGFloat> = Key<CGFloat>("buttonPadding", default: 25)
static let animationEnabled: Defaults.Key<Bool> = Key<Bool>("animationEnabeld", default: false)
Expand All @@ -34,7 +35,7 @@ extension PreferencePaneIdentifier {
}

struct AppInfo {
static let bundleIdentifier: String = "com.mortennn.Dozer"
static let bundleIdentifier: String = Bundle.main.bundleIdentifier!
static var releaseVersionNumber: String? {
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
}
Expand Down
61 changes: 53 additions & 8 deletions Dozer/DozerIcons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public final class DozerIcons {
private var dozerIcons: [HelperstatusIcon] = []
private var timerToCheckUserInteraction = Timer()
private var timerToHideDozerIcons = Timer()
private var previousApp = NSRunningApplication()

private init() {
dozerIcons.append(NormalStatusIcon())
Expand Down Expand Up @@ -86,6 +87,16 @@ public final class DozerIcons {
}
}

public var enableIconAndMenu: Bool = Defaults[.showIconAndMenuEnabled] {
didSet {
Defaults[.showIconAndMenuEnabled] = self.enableIconAndMenu
gingerbeardman marked this conversation as resolved.
Show resolved Hide resolved
if self.enableIconAndMenu == false {
_ = DozerIcons.toggleDockIcon(showIcon: false)
appDelegate.preferencesWindowController.show(preferencePane: .general)
}
}
}

public var iconFontSize: Int = Defaults[.iconSize] {
didSet {
Defaults[.iconSize] = self.iconFontSize
Expand All @@ -112,15 +123,18 @@ public final class DozerIcons {
perform(action: .hide, statusIcon: .normalRight)
}
didHideStatusBarIcons()
hideIconAndMenu()
}

public func show() {
resetTimer()
perform(action: .hide, statusIcon: .remove)
perform(action: .show, statusIcon: .normalLeft)
if Defaults[.noIconMode] {
perform(action: .show, statusIcon: .normalRight)
}
didShowStatusBarIcons()
showIconAndMenu()
}

public func toggle() {
Expand All @@ -139,7 +153,26 @@ public final class DozerIcons {
}
}

/// Force show all Dozer status bar icons
public func showIconAndMenu() {
if NSWorkspace.shared.frontmostApplication?.bundleIdentifier != AppInfo.bundleIdentifier {
previousApp = NSWorkspace.shared.frontmostApplication!
}
if Defaults[.showIconAndMenuEnabled] {
_ = DozerIcons.toggleDockIcon(showIcon: true)
NSApp.activate(ignoringOtherApps: true)
}
}

public func hideIconAndMenu() {
if Defaults[.showIconAndMenuEnabled] {
_ = DozerIcons.toggleDockIcon(showIcon: false)
if NSWorkspace.shared.frontmostApplication?.bundleIdentifier == AppInfo.bundleIdentifier {
previousApp.activate()
}
}
}

/// Force show all Dozer icons
public func showAll() {
perform(action: .show, statusIcon: .remove)
perform(action: .show, statusIcon: .normalLeft)
Expand All @@ -148,12 +181,12 @@ public final class DozerIcons {
}

public func handleOptionClick() {
showIconAndMenu()
if get(dozerIcon: .normalLeft).isShown {
DozerIcons.shared.perform(
action: .toggle,
statusIcon: .remove
)
resetTimer()
} else {
DozerIcons.shared.perform(
action: .show,
Expand All @@ -164,11 +197,12 @@ public final class DozerIcons {
statusIcon: .remove
)
}
resetTimer()
}

// MARK: Show/hide lifecycle
private func didShowStatusBarIcons() {
startTimer()
//startTimer()
startUserInteractionTimer()
}

Expand All @@ -182,7 +216,7 @@ public final class DozerIcons {
return
}

// don't hide on user interaction with status bar
// don't hide on user interaction with menu bar
guard !isUserInteractingWithStatusBar() else {
resetTimer()
return
Expand Down Expand Up @@ -230,7 +264,7 @@ public final class DozerIcons {
}
}

/// Will crash if trying to get ´DozerIcon´ which does not exist in the status bar
/// Will crash if trying to get ´DozerIcon´ which does not exist in the menu bar
private func get(dozerIcon: DozerIcon) -> HelperstatusIcon {
var normalStatusIconsXPosition: [CGFloat] = []
for statusIcon in dozerIcons where statusIcon.type == .normal {
Expand All @@ -255,9 +289,20 @@ public final class DozerIcons {
}
}

/// Determines if the user is interacting with the status bar based on level, owner and y-coordinate
/// hide and show dock icon and thus its menu bar: to free up space to show more menu bar icons
public class func toggleDockIcon(showIcon state: Bool) -> Bool {
var result: Bool
if state {
result = NSApp.setActivationPolicy(NSApplication.ActivationPolicy.regular)
} else {
result = NSApp.setActivationPolicy(NSApplication.ActivationPolicy.accessory)
}
return result
Copy link

@decadent decadent Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep shuddering a little knowing that some code running on my machine is so devoted to not having early returns. (And apparently the ternary operator too.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to suggest a change

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, in this case I'd write it in one line:

return NSApp.setActivationPolicy(state ? NSApplication.ActivationPolicy.regular : NSApplication.ActivationPolicy.accessory)

Dunno if Swift's [.thing] syntax allows to remove the duplication of NSApplication.ActivationPolicy too.

Generally, I'm a big fan of early returns, especially for exceptional inputs that cause functions to not do usual stuff. They're pretty much saying “input wrong? no can do” right at the start, and as a bonus avoid extra nesting. Also they work smoothly with more-functional style, which is very nice, in my book.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift warning

Implicit Return Violation: Prefer implicit returns in closures, functions and getters. (implicit_return)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, apparently it's Swift saying to drop return altogether in one-liner functions. Didn't know about that. Seems they're bent even further in the functional direction than older mainstream languages.

}

/// Determines if the user is interacting with the menu bar based on level, owner and y-coordinate
///
/// - Returns: Returns whether the user is interacting with the status bar or not
/// - Returns: Returns whether the user is interacting with the menu bar or not
private func isUserInteractingWithStatusBar() -> Bool {
let windowListType = CGWindowListOption.optionOnScreenOnly
guard let windowInfoList = CGWindowListCopyWindowInfo(windowListType, kCGNullWindowID) as NSArray? as? [[String: AnyObject]] else {
Expand All @@ -267,7 +312,7 @@ public final class DozerIcons {

for windowInfo in windowInfoList {
guard let window = Window(windowInfo),
// If the preferences window are close to the status bar it won't auto hide
// If the preferences window are close to the menu bar it won't auto hide
window.owner != "Dozer" else {
continue
}
Expand Down
6 changes: 6 additions & 0 deletions Dozer/ViewControllers/GeneralVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ final class General: NSViewController, PreferencePane {
@IBOutlet private var HideStatusBarIconsSecondsPopUpButton: NSPopUpButton!
@IBOutlet private var HideBothDozerIconsCheckbox: NSButton!
@IBOutlet private var EnableRemoveDozerIconCheckbox: NSButton!
@IBOutlet private var ShowIconAndMenuCheckbox: NSButton!
@IBOutlet private var FontSizePopUpButton: NSPopUpButton!
@IBOutlet private var ButtonPaddingPopUpButton: NSPopUpButton!
@IBOutlet private var ToggleMenuItemsView: MASShortcutView!
Expand All @@ -45,6 +46,7 @@ final class General: NSViewController, PreferencePane {
HideStatusBarIconsAfterDelayCheckbox.isChecked = Defaults[.hideAfterDelayEnabled]
HideBothDozerIconsCheckbox.isChecked = Defaults[.noIconMode]
EnableRemoveDozerIconCheckbox.isChecked = Defaults[.removeDozerIconEnabled]
ShowIconAndMenuCheckbox.isChecked = Defaults[.showIconAndMenuEnabled]
HideStatusBarIconsSecondsPopUpButton.selectItem(withTitle: "\(Int(Defaults[.hideAfterDelay])) seconds")
FontSizePopUpButton.selectItem(withTitle: "\(Int(Defaults[.iconSize])) px")
ButtonPaddingPopUpButton.selectItem(withTitle: "\(Int(Defaults[.buttonPadding])) px")
Expand Down Expand Up @@ -86,6 +88,10 @@ final class General: NSViewController, PreferencePane {
DozerIcons.shared.hideBothDozerIcons = HideBothDozerIconsCheckbox.isChecked
}

@IBAction private func showIconAndMenuClicked(_ sender: NSButton) {
DozerIcons.shared.enableIconAndMenu = ShowIconAndMenuCheckbox.isChecked
}

@IBAction private func fontSizeChanged(_ sender: NSPopUpButton) {
DozerIcons.shared.iconFontSize = FontSizePopUpButton.selectedTag()
}
Expand Down
9 changes: 7 additions & 2 deletions Dozer/XIB/Dozer.xib
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
Expand All @@ -24,6 +25,8 @@
<buttonCell key="cell" type="push" title="Check for updates" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="rlC-nV-9gL">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent">u</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SpT-W4-9Jy">
Expand All @@ -50,6 +53,8 @@
<buttonCell key="cell" type="push" title="Quit" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WWq-a5-SXU">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent">q</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
</button>
</subviews>
Expand Down
Loading