From 67e531c8545e705b8b7ce1f91f91e7e0b18d168a Mon Sep 17 00:00:00 2001 From: philbrisco Date: Mon, 4 Jan 2021 11:16:41 -0700 Subject: [PATCH] Add files via upload --- MTPopupMac.swift | 146 +++++++++++++++++++++++++++++++----------- MTPopupMac_readme.txt | 34 +++++----- 2 files changed, 125 insertions(+), 55 deletions(-) diff --git a/MTPopupMac.swift b/MTPopupMac.swift index 887b2be..274e4d5 100644 --- a/MTPopupMac.swift +++ b/MTPopupMac.swift @@ -4,15 +4,34 @@ // // Created by Phillip Brisco on 12/29/20. // -// Ported MTPopup from IOS to OSX and improved it +// Ported MTPopup from IOS to OSX. +// +// The MTPopupMac view is an NSView version of MTPopup for UIView and IOS. It +// popups an overlay view that contains a rendered HTML file or URL. This is very +// good for putting help files on each screen in an easily read format. +// +// This very usefule (IMHO) class was originally created by Marin Todorov in +// Objective-C several years ago. I ported it to swift in IOS (and let him see +// what I had done). I've kept up with it (more or less) ever since. +// +// This version took care of the following issues: +// +// 2.1 +// multiple help screens were allowed to be displayed in each window. This has been +// fixed. Only one help screen per window is allowed. +// +// 2.1 +// Issue with dismissing of help screens (when multiple windows were displaying help +// screens) in a random or unexpected order has been fixed. Now only the key +// window help is dismissed. +// +// 2.1 +// Shut down access to non-interfacing functions, classes and variables by making +// them private or fileprivate. -import Foundation import WebKit import Cocoa import AVFoundation -import QuartzCore -import AppKit -import CoreGraphics @objc protocol MTPopupWindowDelegate { @objc optional func willShowMTPopupWindow(sender : MTPopupWindow); @@ -26,7 +45,7 @@ let kDefaultMargin : CGFloat = 0 var kWindowMarginSize = CGSize() // Set up event handler to take care of local keydown events. -public class MTEventMonitor { +fileprivate class MTEventMonitor { private var monitor: Any? private let mask: NSEvent.EventTypeMask @@ -66,7 +85,8 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe private var dimView : NSView? private var bgView : NSView? private var loader = NSProgressIndicator() - private var audioClick : AVAudioPlayer? = nil; + private var audioClick : AVAudioPlayer? = nil + private var audioSwoosh : AVAudioPlayer? = nil private var btnClose = MTPopupWindowCloseButton() private var theText = NSTextField(frame: NSMakeRect(20, 20, 200, 40)) var insetW: CGFloat = 0 @@ -81,54 +101,61 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe override init(frame: CGRect) { super.init(frame: frame) + theText.delegate = self loader.removeFromSuperview() setupUI() locEventHandler = MTEventMonitor(mask: [.keyDown], handler: { (NSEvent) -> NSEvent? in - self.closePopupWindow() - self.locEventHandler?.stop() - // Command-q (quit). While an NSEvent returning the keycode of 12 - // ("q") won't hurt anything, it is better to return nil instead. - // This is why the modifier is tested. We want to pass the modified - // key. - if NSEvent.keyCode == 12 && NSEvent.modifierFlags.rawValue > 256 { + // Allow any non-modified key (command, shift, option, control, functio,) + // etc.) key to ONLY disappear the help screen. + if NSEvent.modifierFlags.rawValue > 256 { return NSEvent } else { - return nil + + // Only close the help if this is the key window, otherwise just + // pass the event to the window controller to do it's magic. + if (self.window?.isKeyWindow)! { + self.closePopupWindow() + self.locEventHandler?.stop() + return nil + } else { + return NSEvent + } + } }) locEventHandler?.start() } - func initialize () { + private func initialize () { _ = CGSize (width: kDefaultMargin, height: kDefaultMargin) } - func setWindowMargin (margin : CGSize) { + private func setWindowMargin (margin : CGSize) { kWindowMarginSize = margin; } - func showWindowWithHTMLFile (fileName : String) -> MTPopupWindow { + private func showWindowWithHTMLFile (fileName : String) -> MTPopupWindow { let view : NSView = NSApplication.shared.windows.filter {$0.isKeyWindow}.first!.contentView! view.wantsLayer = true return self.showWindowWithHtmlFile(fileName: fileName, insideView: view) } - func showWindowWithHtmlFile (fileName: String, insideView: NSView) -> MTPopupWindow { + private func showWindowWithHtmlFile (fileName: String, insideView: NSView) -> MTPopupWindow { let popup : MTPopupWindow = initWithFile (fName: fileName as NSString) as! MTPopupWindow return popup } - func initWithFile (fName : NSString) -> AnyObject { + private func initWithFile (fName : NSString) -> AnyObject { self.fileName = fName as String; return MTPopupWindow(); } - func setupUI () { + private func setupUI () { webView.wantsLayer = true webView.layer?.borderWidth = 5.0 webView.layer?.borderColor = NSColor.black.cgColor; @@ -138,7 +165,7 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe webView.alphaValue = 1.0 } - func showAlert (_ message: String, details: String) { + private func showAlert (_ message: String, details: String) { guard let mySelf = NSApplication.shared.mainWindow else { return } if NSApp.keyWindow != nil { let alert = NSAlert() @@ -148,13 +175,10 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe alert.icon = NSImage(named: "wickedWitch") alert.window.minSize = .init(width: 480, height: 200) errorPlayer.play() + closePopupWindow() alert.beginSheetModal(for: mySelf, completionHandler: { response in - - if self.audioClick != nil { - self.playClickSound() - } - + self.playClickSound() }) } } @@ -163,12 +187,12 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe showAlert("Webview Error", details: "The requested document cannot be loaded, try again later.") } - func showInView (v:NSView) { + private func showInView (v:NSView) { dimView = NSView(); + dimView?.identifier = NSUserInterfaceItemIdentifier(rawValue: "MTPopup") v.addSubview(dimView!) dimView!.layoutMaximizeInView(v: v, inset: 0) - bgView = NSView(); v.addSubview(bgView!); bgView?.layoutMaximizeInView(v: v, insetH: insetH, insetW: insetW) @@ -258,11 +282,26 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe func show () { let view: NSView = (NSApplication.shared.keyWindow?.contentView)! - self.showInView(v: view) + + for subView in view.subviews { + + if subView.identifier! + .rawValue == "MTPopup" { + audioClick = nil + print ("gothereno") + closePopupWindow() + return + } + + } + + print ("gothere maybe") + self.playClickSwoosh() + self.showInView(v: view) } - func animatePopup (v : NSView) { - let fauxView = NSView(); + private func animatePopup (v : NSView) { + let fauxView = NSView(); fauxView.wantsLayer = true fauxView.layer?.backgroundColor = NSColor.red.cgColor @@ -308,9 +347,7 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe delegate!.willCloseMTPopupWindow!(sender: self) } - if (audioClick != nil) { - playClickSound() - } + playClickSound() NSAnimationContext.runAnimationGroup({ context in let rotation = CABasicAnimation(keyPath: "transform") @@ -338,7 +375,7 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe self.locEventHandler?.stop() if (self.responds(to: #selector(MTPopupWindowDelegate.didCloseMTPopupWindow(sender:)))) { - self.delegate!.didCloseMTPopupWindow!(sender: self) + self.delegate!.didCloseMTPopupWindow!(sender: self) } }) @@ -404,17 +441,48 @@ class MTPopupWindow: NSView, WKUIDelegate, WKNavigationDelegate, NSApplicationDe } } - func playClickSound () { + private func playClickSound () { if (audioClick != nil && !audioClick!.isPlaying) { audioClick!.play(); } } + + // Set up swoosh sound + func clickBtnSwoosh (fileName: String?, fileType: String?) { + + if (fileName != nil && fileType != nil) { + guard let filePath : String = Bundle.main.path(forResource: fileName!, ofType: fileType!) else {return} + + guard let fileUrl: NSURL = NSURL.fileURL(withPath: filePath) as NSURL? else {return} + + if (audioSwoosh == nil) { + do { + try audioSwoosh = AVAudioPlayer(contentsOf: (fileUrl) as URL) + audioSwoosh!.numberOfLoops = 0; + audioSwoosh!.prepareToPlay(); + } catch let error { + showAlert("Sound Error", details: error.localizedDescription) + } + + } + + } + } + + private func playClickSwoosh () { + + if (audioSwoosh != nil && !audioSwoosh!.isPlaying) { + audioSwoosh!.play(); + } + + } + } // Layout constraints for close button. -class MTPopupWindowCloseButton : NSButton, MTPopupWindowDelegate { +fileprivate class MTPopupWindowCloseButton : NSButton, MTPopupWindowDelegate { var closeBtnTopInset: CGFloat = 0 var closeBtnRightInset: CGFloat = 0 @@ -478,7 +546,7 @@ class MTPopupWindowCloseButton : NSButton, MTPopupWindowDelegate { } -extension NSView { +fileprivate extension NSView { func layoutMaximizeInView (v : NSView, inset : CGFloat) { self.layoutMaximizeInView (v: v, insetSize: CGSize(width: inset, height: inset)) diff --git a/MTPopupMac_readme.txt b/MTPopupMac_readme.txt index bdad798..5e68982 100644 --- a/MTPopupMac_readme.txt +++ b/MTPopupMac_readme.txt @@ -52,15 +52,16 @@ func mainHelp () { let winPop = MTPopupWindow() let view: NSView = (NSApplication.shared.keyWindow?.contentView)! + view.wantsLayer = true - winPop.closeBtnTopInset = 15 // Push button down + winPop.closeBtnTopInset = 15 // Push down button to avoid status bar winPop.closeBtnRightInset = 15 // Push button to left winPop.clickBtnSound(fileName: "click", fileType: "wav") + winPop.clickBtnSwoosh(fileName: "swoosh", fileType: "wav") winPop.fileName = String(htmlFile) winPop.insetH = 0 // Offset for navigation bar winPop.insetW = 0 // Offset for width of window. - winPop.showInView(v: view) - swooshPlayer.play() + winPop.show() } The function checks to see what the title of the current window is, then uses @@ -80,18 +81,19 @@ being 5. This port is better than the IOS ports and is technically more sophisticated, but it is still version 1 for OSX. -------------------------------------------------- -Known issues: +Fixes: + +2.1 Phillip Brisco +Fixed bug that dismissed the help screens (with multiple windows on screen) in +an unexpected random order. Now the help screen of the key window is closed +while all other help screens are left untouched. -When having multiple windows on the screen at any one time with the help -view running on each window, the dismissal of said help won't be in the expected -order (with the key window help disappearing first). This is related to an -issue with addLocalMonitorForEvents. I initially thought that it was handling -events per window, but it turns out that it is handling them per application. -It still dismisses all the windows it should but not just in the expected way. -Oh well... +2.1 Phillip Brisco +Fixed issue with multiple help screens being loaded into a window. Now there +is only one help screen per window. -Multiple help views can be loaded on a window. This results in the necessity -of dismissing each one before you can see the original window. Unless you just -dismiss the window, then redisplay it. This is not a bad bug and, in fact, -I'm not totally sure it is one. At any rate, it would probably be rare that -this one happens and is easily dealt with. +2.1 Phillip Brisco +Made most of the non-interfacing functions private, variables private and +lasses fileprivate to increase the security of MTPopup Probably not all that +necessary except in the case of the monitor. We don't want that one +tromped on by or tromping other monitors.