mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-12 10:39:57 +00:00
Add file media type to receiver selectors
This commit is contained in:
@@ -8,15 +8,9 @@ class AppDelegate : NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
|
|
||||||
func applicationDidFinishLaunching (_ aNotification: Notification) {
|
func applicationDidFinishLaunching (_ aNotification: Notification) {
|
||||||
let window = NSPanel(
|
let window = NSWindow(
|
||||||
contentRect: NSZeroRect
|
contentRect: NSZeroRect
|
||||||
, styleMask: [
|
, styleMask: [ .titled, .closable ]
|
||||||
.titled
|
|
||||||
, .closable
|
|
||||||
, .hudWindow
|
|
||||||
, .utilityWindow
|
|
||||||
, .nonactivatingPanel
|
|
||||||
]
|
|
||||||
, backing: .buffered
|
, backing: .buffered
|
||||||
, defer: false)
|
, defer: false)
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ class ViewController : NSViewController {
|
|||||||
var mediaTypePopUpButton: NSPopUpButton!
|
var mediaTypePopUpButton: NSPopUpButton!
|
||||||
var receiverViews = [ReceiverView]()
|
var receiverViews = [ReceiverView]()
|
||||||
|
|
||||||
|
var filePath: String?
|
||||||
|
|
||||||
|
|
||||||
override func loadView () {
|
override func loadView () {
|
||||||
let visualEffectView = NSVisualEffectView()
|
/*let visualEffectView = NSVisualEffectView()
|
||||||
visualEffectView.blendingMode = .behindWindow
|
visualEffectView.blendingMode = .behindWindow
|
||||||
visualEffectView.state = .active
|
visualEffectView.state = .active*/
|
||||||
|
|
||||||
self.view = visualEffectView
|
self.view = NSView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad () {
|
override func viewDidLoad () {
|
||||||
@@ -71,34 +73,35 @@ class ViewController : NSViewController {
|
|||||||
initData.i18n_mediaTypeApp
|
initData.i18n_mediaTypeApp
|
||||||
, initData.i18n_mediaTypeTab
|
, initData.i18n_mediaTypeTab
|
||||||
, initData.i18n_mediaTypeScreen
|
, initData.i18n_mediaTypeScreen
|
||||||
|
, initData.i18n_mediaTypeFile
|
||||||
])
|
])
|
||||||
|
|
||||||
|
let mediaTypePopUpButtonMenu = self.mediaTypePopUpButton.menu!
|
||||||
|
mediaTypePopUpButtonMenu.delegate = self
|
||||||
|
|
||||||
let appItem = self.mediaTypePopUpButton
|
let appItem = self.mediaTypePopUpButton
|
||||||
.item(withTitle: initData.i18n_mediaTypeApp)!
|
.item(withTitle: initData.i18n_mediaTypeApp)!
|
||||||
let tabItem = self.mediaTypePopUpButton
|
let tabItem = self.mediaTypePopUpButton
|
||||||
.item(withTitle: initData.i18n_mediaTypeTab)!
|
.item(withTitle: initData.i18n_mediaTypeTab)!
|
||||||
let screenItem = self.mediaTypePopUpButton
|
let screenItem = self.mediaTypePopUpButton
|
||||||
.item(withTitle: initData.i18n_mediaTypeScreen)!
|
.item(withTitle: initData.i18n_mediaTypeScreen)!
|
||||||
|
let fileItem = self.mediaTypePopUpButton
|
||||||
|
.item(withTitle: initData.i18n_mediaTypeFile)!
|
||||||
|
|
||||||
|
mediaTypePopUpButtonMenu
|
||||||
|
.insertItem(NSMenuItem.separator()
|
||||||
|
, at: mediaTypePopUpButtonMenu.index(of: fileItem))
|
||||||
|
|
||||||
// Set tags to enum value
|
// Set tags to enum value
|
||||||
appItem.tag = MediaType.app.rawValue
|
appItem.tag = MediaType.app.rawValue
|
||||||
tabItem.tag = MediaType.tab.rawValue
|
tabItem.tag = MediaType.tab.rawValue
|
||||||
screenItem.tag = MediaType.screen.rawValue
|
screenItem.tag = MediaType.screen.rawValue
|
||||||
|
fileItem.tag = MediaType.file.rawValue
|
||||||
|
|
||||||
if (initData.availableMediaTypes & appItem.tag) == 0 {
|
for item in self.mediaTypePopUpButton.itemArray {
|
||||||
self.mediaTypePopUpButton
|
if (initData.availableMediaTypes & item.tag) == 0 {
|
||||||
.item(withTitle: initData.i18n_mediaTypeApp)?
|
item.isEnabled = false
|
||||||
.isEnabled = false
|
}
|
||||||
}
|
|
||||||
if (initData.availableMediaTypes & tabItem.tag) == 0 {
|
|
||||||
self.mediaTypePopUpButton
|
|
||||||
.item(withTitle: initData.i18n_mediaTypeTab)?
|
|
||||||
.isEnabled = false
|
|
||||||
}
|
|
||||||
if (initData.availableMediaTypes & screenItem.tag) == 0 {
|
|
||||||
self.mediaTypePopUpButton
|
|
||||||
.item(withTitle: initData.i18n_mediaTypeScreen)?
|
|
||||||
.isEnabled = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mediaTypePopUpButton.selectItem(
|
self.mediaTypePopUpButton.selectItem(
|
||||||
@@ -155,6 +158,50 @@ class ViewController : NSViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ViewController : NSMenuDelegate {
|
||||||
|
func menuDidClose (_ menu: NSMenu) {
|
||||||
|
let mediaType = MediaType(
|
||||||
|
rawValue: self.mediaTypePopUpButton.selectedItem!.tag)!
|
||||||
|
|
||||||
|
let fileItem = self.mediaTypePopUpButton
|
||||||
|
.item(at: self.mediaTypePopUpButton.indexOfItem(
|
||||||
|
withTag: MediaType.file.rawValue))!
|
||||||
|
|
||||||
|
if (mediaType == .file) {
|
||||||
|
let panel = NSOpenPanel()
|
||||||
|
panel.allowsMultipleSelection = false
|
||||||
|
panel.allowedFileTypes = [ "aac", "mp3", "mp4", "wav", "webm" ]
|
||||||
|
panel.canChooseDirectories = false
|
||||||
|
panel.canCreateDirectories = false
|
||||||
|
panel.canChooseFiles = true
|
||||||
|
|
||||||
|
panel.beginSheetModal(for: self.view.window!) { (result) in
|
||||||
|
if (result == .OK) {
|
||||||
|
let url = panel.urls[0]
|
||||||
|
let fileName = url.lastPathComponent
|
||||||
|
|
||||||
|
// Truncate file name and set as title
|
||||||
|
fileItem.title = fileName.count > 12
|
||||||
|
? "\(fileName.prefix(12))..."
|
||||||
|
: fileName
|
||||||
|
|
||||||
|
self.filePath = url.path
|
||||||
|
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// Re-select the default media type item
|
||||||
|
self.mediaTypePopUpButton.selectItem(
|
||||||
|
withTag: self.initData.defaultMediaType.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Reset file item
|
||||||
|
fileItem.title = self.initData.i18n_mediaTypeFile
|
||||||
|
self.filePath = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension ViewController : ReceiverViewDelegate {
|
extension ViewController : ReceiverViewDelegate {
|
||||||
func didCast (_ receiver: Receiver) {
|
func didCast (_ receiver: Receiver) {
|
||||||
@@ -173,7 +220,8 @@ extension ViewController : ReceiverViewDelegate {
|
|||||||
|
|
||||||
let selection = ReceiverSelection(
|
let selection = ReceiverSelection(
|
||||||
receiver: receiver
|
receiver: receiver
|
||||||
, mediaType: mediaType)
|
, mediaType: mediaType
|
||||||
|
, filePath: self.filePath ?? nil)
|
||||||
|
|
||||||
let jsonData = try JSONEncoder().encode(selection)
|
let jsonData = try JSONEncoder().encode(selection)
|
||||||
let jsonString = String(data: jsonData, encoding: .utf8)
|
let jsonString = String(data: jsonData, encoding: .utf8)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ struct InitData : Codable {
|
|||||||
let i18n_mediaTypeApp: String
|
let i18n_mediaTypeApp: String
|
||||||
let i18n_mediaTypeTab: String
|
let i18n_mediaTypeTab: String
|
||||||
let i18n_mediaTypeScreen: String
|
let i18n_mediaTypeScreen: String
|
||||||
|
let i18n_mediaTypeFile: String
|
||||||
let i18n_mediaSelectCastLabel: String
|
let i18n_mediaSelectCastLabel: String
|
||||||
let i18n_mediaSelectToLabel: String
|
let i18n_mediaSelectToLabel: String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ enum MediaType: Int, Codable {
|
|||||||
case app = 1
|
case app = 1
|
||||||
case tab = 2
|
case tab = 2
|
||||||
case screen = 4
|
case screen = 4
|
||||||
|
case file = 8
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
struct ReceiverSelection : Codable {
|
struct ReceiverSelection : Codable {
|
||||||
let receiver: Receiver
|
let receiver: Receiver
|
||||||
let mediaType: MediaType
|
let mediaType: MediaType
|
||||||
|
let filePath: String?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
"message": "Screen"
|
"message": "Screen"
|
||||||
, "description": "Receiver selector media type <option> text for screen."
|
, "description": "Receiver selector media type <option> text for screen."
|
||||||
}
|
}
|
||||||
|
, "popupMediaTypeFile": {
|
||||||
|
"message": "Browse..."
|
||||||
|
, "description": "Receiver selector media type <option> text for opening a file selector dialog."
|
||||||
|
}
|
||||||
|
|
||||||
, "popupMediaSelectCastLabel": {
|
, "popupMediaSelectCastLabel": {
|
||||||
"message": "Cast"
|
"message": "Cast"
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export default class NativeMacReceiverSelector
|
|||||||
, i18n_mediaTypeApp: _("popupMediaTypeApp")
|
, i18n_mediaTypeApp: _("popupMediaTypeApp")
|
||||||
, i18n_mediaTypeTab: _("popupMediaTypeTab")
|
, i18n_mediaTypeTab: _("popupMediaTypeTab")
|
||||||
, i18n_mediaTypeScreen: _("popupMediaTypeScreen")
|
, i18n_mediaTypeScreen: _("popupMediaTypeScreen")
|
||||||
|
, i18n_mediaTypeFile: _("popupMediaTypeFile")
|
||||||
, i18n_mediaSelectCastLabel: _("popupMediaSelectCastLabel")
|
, i18n_mediaSelectCastLabel: _("popupMediaSelectCastLabel")
|
||||||
, i18n_mediaSelectToLabel: _("popupMediaSelectToLabel")
|
, i18n_mediaSelectToLabel: _("popupMediaSelectToLabel")
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ export enum ReceiverSelectorMediaType {
|
|||||||
App = 1
|
App = 1
|
||||||
, Tab = 2
|
, Tab = 2
|
||||||
, Screen = 4
|
, Screen = 4
|
||||||
|
, File = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReceiverSelection {
|
export interface ReceiverSelection {
|
||||||
receiver: Receiver;
|
receiver: Receiver;
|
||||||
mediaType: ReceiverSelectorMediaType;
|
mediaType: ReceiverSelectorMediaType;
|
||||||
|
filePath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReceiverSelectorSelectedEvent = CustomEvent<ReceiverSelection>;
|
export type ReceiverSelectorSelectedEvent = CustomEvent<ReceiverSelection>;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export default class ApiConfig {
|
|||||||
, public _availableMediaTypes: ReceiverSelectorMediaType
|
, public _availableMediaTypes: ReceiverSelectorMediaType
|
||||||
= ReceiverSelectorMediaType.App
|
= ReceiverSelectorMediaType.App
|
||||||
| ReceiverSelectorMediaType.Tab
|
| ReceiverSelectorMediaType.Tab
|
||||||
| ReceiverSelectorMediaType.Screen) {
|
| ReceiverSelectorMediaType.Screen
|
||||||
|
| ReceiverSelectorMediaType.File) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,13 @@ interface PopupAppState {
|
|||||||
mediaType: ReceiverSelectorMediaType;
|
mediaType: ReceiverSelectorMediaType;
|
||||||
availableMediaTypes: ReceiverSelectorMediaType;
|
availableMediaTypes: ReceiverSelectorMediaType;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
filePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PopupApp extends Component<{}, PopupAppState> {
|
class PopupApp extends Component<{}, PopupAppState> {
|
||||||
private port: browser.runtime.Port;
|
private port: browser.runtime.Port;
|
||||||
private win: browser.windows.Window;
|
private win: browser.windows.Window;
|
||||||
|
private defaultMediaType: ReceiverSelectorMediaType;
|
||||||
|
|
||||||
constructor (props: {}) {
|
constructor (props: {}) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -44,6 +46,7 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
, mediaType: ReceiverSelectorMediaType.App
|
, mediaType: ReceiverSelectorMediaType.App
|
||||||
, availableMediaTypes: ReceiverSelectorMediaType.App
|
, availableMediaTypes: ReceiverSelectorMediaType.App
|
||||||
, isLoading: false
|
, isLoading: false
|
||||||
|
, filePath: null
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store window ref
|
// Store window ref
|
||||||
@@ -63,9 +66,11 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
this.port.onMessage.addListener((message: Message) => {
|
this.port.onMessage.addListener((message: Message) => {
|
||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "popup:/populateReceiverList": {
|
case "popup:/populateReceiverList": {
|
||||||
|
this.defaultMediaType = message.data.defaultMediaType;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
receivers: message.data.receivers
|
receivers: message.data.receivers
|
||||||
, mediaType: message.data.defaultMediaType
|
, mediaType: this.defaultMediaType
|
||||||
, availableMediaTypes: message.data.availableMediaTypes
|
, availableMediaTypes: message.data.availableMediaTypes
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,6 +98,18 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render () {
|
public render () {
|
||||||
|
let truncatedFileName: string;
|
||||||
|
|
||||||
|
if (this.state.filePath) {
|
||||||
|
const filePath = this.state.filePath;
|
||||||
|
const fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
|
||||||
|
|
||||||
|
truncatedFileName = fileName.length > 12
|
||||||
|
? `${fileName.substring(0, 12)}...`
|
||||||
|
: fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="media-select">
|
<div className="media-select">
|
||||||
@@ -115,6 +132,16 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
& ReceiverSelectorMediaType.Screen) }>
|
& ReceiverSelectorMediaType.Screen) }>
|
||||||
{ _("popupMediaTypeScreen") }
|
{ _("popupMediaTypeScreen") }
|
||||||
</option>
|
</option>
|
||||||
|
<option disabled>
|
||||||
|
─────
|
||||||
|
</option>
|
||||||
|
<option value={ ReceiverSelectorMediaType.File }
|
||||||
|
disabled={ !(this.state.availableMediaTypes
|
||||||
|
& ReceiverSelectorMediaType.File) }>
|
||||||
|
{ this.state.filePath
|
||||||
|
? truncatedFileName
|
||||||
|
: _("popupMediaTypeFile") }
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
{ _("popupMediaSelectToLabel") }
|
{ _("popupMediaSelectToLabel") }
|
||||||
</div>
|
</div>
|
||||||
@@ -140,13 +167,45 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
, data: {
|
, data: {
|
||||||
receiver
|
receiver
|
||||||
, mediaType: this.state.mediaType
|
, mediaType: this.state.mediaType
|
||||||
|
, filePath: this.state.filePath
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSelectChange (ev: React.ChangeEvent<HTMLSelectElement>) {
|
private onSelectChange (ev: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
const mediaType = parseInt(ev.target.value);
|
||||||
|
|
||||||
|
if (mediaType === ReceiverSelectorMediaType.File) {
|
||||||
|
try {
|
||||||
|
const filePath = window.prompt();
|
||||||
|
|
||||||
|
// Validate URL
|
||||||
|
const fileUrl = new URL(filePath.startsWith("file://")
|
||||||
|
? filePath
|
||||||
|
: `file://${filePath}`);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
mediaType
|
||||||
|
, filePath
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
// Don't need to handle any errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set media type to default if failed to set filePath
|
||||||
|
this.setState({
|
||||||
|
mediaType: this.defaultMediaType
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
mediaType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
mediaType: parseInt(ev.target.value)
|
filePath: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user