diff --git a/app/NativeMacReceiverSelector/AppDelegate.swift b/app/NativeMacReceiverSelector/AppDelegate.swift index d704d9e..1458c88 100644 --- a/app/NativeMacReceiverSelector/AppDelegate.swift +++ b/app/NativeMacReceiverSelector/AppDelegate.swift @@ -1,14 +1,13 @@ import Cocoa -class AppDelegate: NSObject { +class AppDelegate : NSObject, NSApplicationDelegate { var mainWindow: NSWindow? var mainWindowController: NSWindowController? var mainWindowViewController: ViewController? -} -extension AppDelegate: NSApplicationDelegate { - func applicationDidFinishLaunching(_ aNotification: Notification) { + + func applicationDidFinishLaunching (_ aNotification: Notification) { let window = NSPanel( contentRect: NSZeroRect , styleMask: [ @@ -21,7 +20,7 @@ extension AppDelegate: NSApplicationDelegate { , backing: .buffered , defer: false) - window.title = "fx_cast" + window.titleVisibility = .hidden window.orderFrontRegardless() window.center() @@ -39,13 +38,12 @@ extension AppDelegate: NSApplicationDelegate { NSApp.activate(ignoringOtherApps: true) } - func applicationDidResignActive(_ aNotification: Notification) { + func applicationDidResignActive (_ aNotification: Notification) { self.mainWindow?.performClose(aNotification) } - func applicationWillTerminate(_ aNotification: Notification) {} - - func applicationShouldTerminateAfterLastWindowClosed(_ app: NSApplication) -> Bool { + func applicationShouldTerminateAfterLastWindowClosed ( + _ app: NSApplication) -> Bool { return true } } diff --git a/app/NativeMacReceiverSelector/ReceiverView.swift b/app/NativeMacReceiverSelector/ReceiverView.swift index 420e365..8a6b0ee 100644 --- a/app/NativeMacReceiverSelector/ReceiverView.swift +++ b/app/NativeMacReceiverSelector/ReceiverView.swift @@ -1,17 +1,29 @@ import Cocoa -protocol ReceiverViewDelegate: AnyObject { + +protocol ReceiverViewDelegate : AnyObject { func didCast (_ receiver: Receiver) } -class ReceiverView: NSStackView { +class ReceiverView : NSStackView { weak var receiverViewDelegate: ReceiverViewDelegate? var receiver: Receiver! var constraintsSet = false - var button: NSButton! - var spinner: NSProgressIndicator! + var castButton: NSButton! + var castingSpinner: NSProgressIndicator! + + + var isEnabled: Bool { + get { + return self.castButton.isEnabled + } + set { + self.castButton.isEnabled = newValue + } + } + override init (frame: CGRect) { super.init(frame: frame) @@ -20,8 +32,8 @@ class ReceiverView: NSStackView { super.init(coder: coder) } - init (receiver: Receiver) { - super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) + init (receiver: Receiver, initData: InitData) { + super.init(frame: NSZeroRect) self.receiver = receiver @@ -37,21 +49,23 @@ class ReceiverView: NSStackView { metaStackView.spacing = 4 - self.button = WideButton( - title: "Cast" + self.castButton = NSButton( + title: initData.i18n_castButtonTitle , target: self , action: #selector(ReceiverView.onCast)) - self.button.bezelStyle = .rounded + self.castButton.bezelStyle = .rounded + self.castButton.widthAnchor.constraint(equalToConstant: 100).isActive = true - self.spinner = NSProgressIndicator() - self.spinner.style = .spinning - self.spinner.controlSize = .small - self.spinner.isHidden = true + + self.castingSpinner = NSProgressIndicator() + self.castingSpinner.style = .spinning + self.castingSpinner.controlSize = .small + self.castingSpinner.isHidden = true self.addArrangedSubview(metaStackView) - self.addArrangedSubview(self.spinner) - self.addArrangedSubview(self.button) + self.addArrangedSubview(self.castingSpinner) + self.addArrangedSubview(self.castButton) self.distribution = .fill } @@ -61,34 +75,24 @@ class ReceiverView: NSStackView { if !constraintsSet { self.translatesAutoresizingMaskIntoConstraints = false - self.leadingAnchor.constraint(equalTo: superview!.leadingAnchor, constant: 8).isActive = true - self.trailingAnchor.constraint(equalTo: superview!.trailingAnchor, constant: -8).isActive = true + + self.leadingAnchor.constraint( + equalTo: superview!.leadingAnchor + , constant: 8).isActive = true + self.trailingAnchor.constraint( + equalTo: superview!.trailingAnchor + , constant: -8).isActive = true constraintsSet = true } } - func disable () { - self.button.isEnabled = false - } @objc func onCast () { self.receiverViewDelegate?.didCast(self.receiver) - self.spinner.isHidden = false - self.spinner.startAnimation(nil) - } -} - - -class WideButton: NSButton { - override var intrinsicContentSize: NSSize { - var size = super.intrinsicContentSize - if size.width < 100 { - size.width = 100 - } - - return size + self.castingSpinner.isHidden = false + self.castingSpinner.startAnimation(nil) } } diff --git a/app/NativeMacReceiverSelector/ViewController.swift b/app/NativeMacReceiverSelector/ViewController.swift index d9d79a5..2d2ae1a 100644 --- a/app/NativeMacReceiverSelector/ViewController.swift +++ b/app/NativeMacReceiverSelector/ViewController.swift @@ -1,60 +1,13 @@ import Cocoa -import Foundation -func makeLabel(_ text: String, - size: CGFloat = 0, - color: NSColor = NSColor.textColor) -> NSTextField { +class ViewController : NSViewController { + var initData: InitData! - let textField = NSTextField() - textField.stringValue = text - textField.backgroundColor = .clear - textField.isEditable = false - textField.isBezeled = false - textField.sizeToFit() - - // Text - textField.font = NSFont.systemFont(ofSize: size) - textField.textColor = color - - return textField -} - - -struct InitData: Codable { - let receivers: [Receiver] - let defaultMediaType: MediaType - - let i18n_mediaTypeApp: String - let i18n_mediaTypeTab: String - let i18n_mediaTypeScreen: String - let i18n_mediaSelectCastLabel: String - let i18n_mediaSelectToLabel: String -} - -struct ReceiverSelection: Codable { - let receiver: Receiver - let mediaType: MediaType -} - -enum MediaType: Int, Codable { - case app - case tab - case screen -} - -struct Receiver: Codable { - let friendlyName: String - let host: String - let id: String - let port: Int -} - - -class ViewController: NSViewController { var mediaTypePopUpButton: NSPopUpButton! var receiverViews = [ReceiverView]() + override func loadView () { let visualEffectView = NSVisualEffectView() visualEffectView.blendingMode = .behindWindow @@ -76,17 +29,35 @@ class ViewController: NSViewController { exit(1) } - - let initData: InitData! - do { - initData = try JSONDecoder().decode(InitData.self, from: data) + // Decode and store initialization JSON data + self.initData = try JSONDecoder().decode(InitData.self, from: data) } catch { fputs("Error: Failed to parse input data\n", stderr) exit(1) } + /** + * View Hierarchy + * -------------- + * + * stackView \ (NSStackView) + * ├ mediaTypeStackView \ (NSStackView) + * │ ├ mediaSelectCastLabel \ (NSTextField) + * │ ├ mediaTypePopUpButton \ (NSPopUpButton) + * │ └ mediaSelectToLabel \ (NSTextField) + * │ + * ├ receiverSeparator \ (NSBox) ┐ + * └ receiverView \ (ReceiverView:NSStackView) │ + * ├ metaStackView \ (NSStackView) │ + * │ ├ receiver name \ (NSTextField) ├ Repeats + * │ └ receiver host \ (NSTextField) │ + * ├ spinner \ (NSProgressIndicator) │ + * └ button \ (NSButton) ┘ + */ + + let stackView = NSStackView() stackView.orientation = .vertical stackView.alignment = .leading @@ -101,7 +72,9 @@ class ViewController: NSViewController { , initData.i18n_mediaTypeScreen ]) - self.mediaTypePopUpButton.selectItem(at: initData.defaultMediaType.rawValue) + self.mediaTypePopUpButton.selectItem( + at: initData.defaultMediaType.rawValue) + let mediaTypeStackView = NSStackView(views: [ makeLabel(initData.i18n_mediaSelectCastLabel), @@ -109,17 +82,29 @@ class ViewController: NSViewController { makeLabel(initData.i18n_mediaSelectToLabel) ]) - stackView.addArrangedSubview(mediaTypeStackView) + /** + * For each receiver in the initData list, create a new + * ReceiverView, set self as a ReceiverViewDelegate and + * appends to main stack view. + * + * Keeps a reference to the receiver view to call disable() + * later. + */ for receiver in initData.receivers { + // Create separator between last receiver / media type let receiverSeparator = NSBox() receiverSeparator.boxType = .separator - let receiverView = ReceiverView(receiver: receiver) + let receiverView = ReceiverView( + receiver: receiver + , initData: self.initData) + receiverView.receiverViewDelegate = self + self.receiverViews.append(receiverView) stackView.addArrangedSubview(receiverSeparator) @@ -127,17 +112,30 @@ class ViewController: NSViewController { } + // Add to main view and set width to resize window self.view.addSubview(stackView) self.view.autoresizesSubviews = true self.view.frame.size.width = 350 } + + override func viewDidAppear () { + // Set window title and update visibility + let window = self.view.window! + window.title = initData.i18n_extensionName + window.titleVisibility = .visible + } } -extension ViewController: ReceiverViewDelegate { +extension ViewController : ReceiverViewDelegate { func didCast (_ receiver: Receiver) { + + // Disable media type UI + self.mediaTypePopUpButton.isEnabled = false + + // Disable cast buttons for receiverView in self.receiverViews { - receiverView.disable() + receiverView.isEnabled = false } do { diff --git a/app/NativeMacReceiverSelector/main b/app/NativeMacReceiverSelector/main new file mode 100755 index 0000000..29698d6 Binary files /dev/null and b/app/NativeMacReceiverSelector/main differ diff --git a/app/NativeMacReceiverSelector/models/InitData.swift b/app/NativeMacReceiverSelector/models/InitData.swift new file mode 100644 index 0000000..41cca09 --- /dev/null +++ b/app/NativeMacReceiverSelector/models/InitData.swift @@ -0,0 +1,12 @@ +struct InitData : Codable { + let receivers: [Receiver] + let defaultMediaType: MediaType + + let i18n_extensionName: String + let i18n_castButtonTitle: String + let i18n_mediaTypeApp: String + let i18n_mediaTypeTab: String + let i18n_mediaTypeScreen: String + let i18n_mediaSelectCastLabel: String + let i18n_mediaSelectToLabel: String +} diff --git a/app/NativeMacReceiverSelector/models/MediaType.swift b/app/NativeMacReceiverSelector/models/MediaType.swift new file mode 100644 index 0000000..65b9dbc --- /dev/null +++ b/app/NativeMacReceiverSelector/models/MediaType.swift @@ -0,0 +1,3 @@ +enum MediaType : Int, Codable { + case app, tab, screen +} diff --git a/app/NativeMacReceiverSelector/models/Receiver.swift b/app/NativeMacReceiverSelector/models/Receiver.swift new file mode 100644 index 0000000..16f293c --- /dev/null +++ b/app/NativeMacReceiverSelector/models/Receiver.swift @@ -0,0 +1,6 @@ +struct Receiver : Codable { + let friendlyName: String + let host: String + let id: String + let port: Int +} diff --git a/app/NativeMacReceiverSelector/models/ReceiverSelection.swift b/app/NativeMacReceiverSelector/models/ReceiverSelection.swift new file mode 100644 index 0000000..3881fab --- /dev/null +++ b/app/NativeMacReceiverSelector/models/ReceiverSelection.swift @@ -0,0 +1,4 @@ +struct ReceiverSelection : Codable { + let receiver: Receiver + let mediaType: MediaType +} diff --git a/app/NativeMacReceiverSelector/util.swift b/app/NativeMacReceiverSelector/util.swift new file mode 100644 index 0000000..e2bdda8 --- /dev/null +++ b/app/NativeMacReceiverSelector/util.swift @@ -0,0 +1,19 @@ +import Cocoa + +func makeLabel(_ text: String, + size: CGFloat = 0, + color: NSColor = NSColor.textColor) -> NSTextField { + + let textField = NSTextField() + textField.stringValue = text + textField.backgroundColor = .clear + textField.isEditable = false + textField.isBezeled = false + textField.sizeToFit() + + // Text + textField.font = NSFont.systemFont(ofSize: size) + textField.textColor = color + + return textField +} diff --git a/ext/src/_locales/en/messages.json b/ext/src/_locales/en/messages.json index e08f895..da94803 100755 --- a/ext/src/_locales/en/messages.json +++ b/ext/src/_locales/en/messages.json @@ -21,10 +21,10 @@ , "popupMediaSelectToLabel": { "message": "to:" } - , "popupCastButtonLabel": { + , "popupCastButtonTitle": { "message": "Cast" } - , "popupCastingButtonLabel": { + , "popupCastingButtonTitle": { "message": "Casting" } diff --git a/ext/src/_locales/nl/messages.json b/ext/src/_locales/nl/messages.json index 26d773e..3dd3f8e 100755 --- a/ext/src/_locales/nl/messages.json +++ b/ext/src/_locales/nl/messages.json @@ -6,10 +6,10 @@ "message": "" } - , "popupCastButtonLabel": { + , "popupCastButtonTitle": { "message": "Casten" } - , "popupCastingButtonLabel": { + , "popupCastingButtonTitle": { "message": "Aan het casten" } diff --git a/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts b/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts index 80a6d5d..1cbdd04 100644 --- a/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts +++ b/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts @@ -60,6 +60,9 @@ export default class NativeMacReceiverSelectorManager , data: JSON.stringify({ receivers , defaultMediaType + + , i18n_extensionName: _("extensionName") + , i18n_castButtonTitle: _("popupCastButtonTitle") , i18n_mediaTypeApp: _("popupMediaTypeApp") , i18n_mediaTypeTab: _("popupMediaTypeTab") , i18n_mediaTypeScreen: _("popupMediaTypeScreen") diff --git a/ext/src/ui/popup/index.tsx b/ext/src/ui/popup/index.tsx index 8f9ca27..fd26f2e 100755 --- a/ext/src/ui/popup/index.tsx +++ b/ext/src/ui/popup/index.tsx @@ -187,11 +187,11 @@ class ReceiverEntry extends Component { onClick={ this.handleCast } disabled={this.props.isLoading}> { this.state.isLoading - ? _("popupCastingButtonLabel") + + ? _("popupCastingButtonTitle") + (this.state.isLoading ? this.state.ellipsis : "") - : _("popupCastButtonLabel") } + : _("popupCastButtonTitle") } );