-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #643 from bitmovin/feature/complete-settings-menu
Completed new Settings menu
- Loading branch information
Showing
12 changed files
with
415 additions
and
52 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { UIInstanceManager } from '../uimanager'; | ||
import { ModernSettingsPanelPage } from './modernsettingspanelpage'; | ||
import { PlayerAPI } from 'bitmovin-player'; | ||
import { SettingsPanel, SettingsPanelConfig, NavigationDirection } from './settingspanel'; | ||
|
||
|
||
export class ModernSettingsPanel extends SettingsPanel { | ||
constructor(config: SettingsPanelConfig) { | ||
super(config); | ||
|
||
this.config = this.mergeConfig(config, { | ||
cssClass: 'ui-settings-panel', | ||
hideDelay: 3000, | ||
pageTransitionAnimation: true, | ||
} as SettingsPanelConfig, this.config); | ||
|
||
(<ModernSettingsPanelPage>this.getActivePage()).onRequestsDisplaySubMenu.subscribe(this.handleShowSubPage); | ||
} | ||
|
||
configure(player: PlayerAPI, uimanager: UIInstanceManager): void { | ||
super.configure(player, uimanager); | ||
|
||
uimanager.onPreviewControlsHide.subscribe(() => { | ||
this.hide(); | ||
}); | ||
} | ||
|
||
private handleShowSubPage = (sender: ModernSettingsPanelPage, subPage: ModernSettingsPanelPage) => { | ||
this.show(); | ||
this.addComponent(subPage); | ||
this.updateComponents(); | ||
this.setActivePage(subPage); | ||
} | ||
|
||
private handleNavigateBack = (page: ModernSettingsPanelPage) => { | ||
this.popSettingsPanelPage(); | ||
this.removeComponent(page); | ||
this.updateComponents(); | ||
} | ||
|
||
protected navigateToPage( | ||
targetPage: ModernSettingsPanelPage, | ||
sourcePage: ModernSettingsPanelPage, | ||
direction: NavigationDirection, | ||
skipAnimation: boolean, | ||
): void { | ||
super.navigateToPage(targetPage, sourcePage, direction, skipAnimation); | ||
|
||
if (direction === NavigationDirection.Forwards) { | ||
targetPage.onRequestsDisplaySubMenu.subscribe(this.handleShowSubPage); | ||
targetPage.onRequestsNavigateBack.subscribe(() => this.handleNavigateBack(targetPage)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
import { ContainerConfig} from './container'; | ||
import {Component, ComponentConfig} from './component'; | ||
import {Event as EDEvent, EventDispatcher, NoArgs} from '../eventdispatcher'; | ||
import { Label, LabelConfig } from './label'; | ||
import {UIInstanceManager} from '../uimanager'; | ||
import {SelectBox} from './selectbox'; | ||
import {ListBox} from './listbox'; | ||
import {VideoQualitySelectBox} from './videoqualityselectbox'; | ||
import {AudioQualitySelectBox} from './audioqualityselectbox'; | ||
import {PlaybackSpeedSelectBox} from './playbackspeedselectbox'; | ||
import { PlayerAPI } from 'bitmovin-player'; | ||
import { i18n, LocalizableText } from '../localization/i18n'; | ||
import { ModernSettingsPanelPage } from './modernsettingspanelpage'; | ||
import { ListSelector, ListSelectorConfig } from './listselector'; | ||
import { SubtitleSelectBox } from './subtitleselectbox'; | ||
import { SettingsPanelItem } from './settingspanelitem'; | ||
|
||
/** | ||
* An item for a {@link ModernSettingsPanelPage}, | ||
* Containing an optional {@link Label} and a component that configures a setting. | ||
* If the components is a {@link SelectBox} it will handle the logic of displaying it or not | ||
*/ | ||
export class ModernSettingsPanelItem extends SettingsPanelItem { | ||
|
||
/** | ||
* If setting is null, that means that the item is not an option and does not | ||
* have a submenu. So if setting is null we can assume that the item should be | ||
* used as a back button | ||
*/ | ||
private selectedOptionLabel: Label<LabelConfig>; | ||
private isOption: Boolean; | ||
private key: string; | ||
|
||
private player: PlayerAPI; | ||
private uimanager: UIInstanceManager; | ||
|
||
private modernSettingsPanelItemEvents = { | ||
onRequestSubPage: new EventDispatcher<ModernSettingsPanelItem, ModernSettingsPanelPage>(), | ||
onRequestNavigateBack: new EventDispatcher<ModernSettingsPanelItem, NoArgs>(), | ||
onItemSelect: new EventDispatcher<ModernSettingsPanelItem, string>(), | ||
}; | ||
|
||
constructor(label: LocalizableText | Component<ComponentConfig>, setting: Component<ComponentConfig>, key: string = null, config: ContainerConfig = {}) { | ||
super(label, setting, config, false); | ||
|
||
this.isOption = Boolean(key); | ||
this.key = key; | ||
|
||
this.config = this.mergeConfig(config, { | ||
cssClass: 'ui-settings-panel-item', | ||
role: 'menuitem', | ||
}, this.config); | ||
} | ||
|
||
configure(player: PlayerAPI, uimanager: UIInstanceManager): void { | ||
this.player = player; | ||
this.uimanager = uimanager; | ||
|
||
if (this.setting !== null) { | ||
this.setting.configure(this.player, this.uimanager); | ||
} | ||
|
||
if (!this.isOption && (this.setting instanceof SelectBox || this.setting instanceof ListBox)) { | ||
this.setting.onItemSelected.subscribe(() => { | ||
this.removeComponent(this.selectedOptionLabel); | ||
const setting = this.setting as ListSelector<ListSelectorConfig>; | ||
let selectedOptionLabel: LocalizableText = setting.getItemForKey(setting.getSelectedItem()).label; | ||
|
||
if (this.setting instanceof SubtitleSelectBox) { | ||
let availableSettings = setting.getItems().length; | ||
selectedOptionLabel = selectedOptionLabel + ' (' + (availableSettings - 1) + ')'; | ||
} | ||
this.selectedOptionLabel = new Label({ text: selectedOptionLabel, for: this.getConfig().id } as LabelConfig); | ||
this.selectedOptionLabel.getDomElement().addClass(this.prefixCss('ui-label-setting-selected-option')); | ||
this.addComponent(this.selectedOptionLabel); | ||
this.updateComponents(); | ||
}); | ||
|
||
let handleConfigItemChanged = () => { | ||
if (!(this.setting instanceof SelectBox) && !(this.setting instanceof ListBox)) { | ||
return; | ||
} | ||
// The minimum number of items that must be available for the setting to be displayed | ||
// By default, at least two items must be available, else a selection is not possible | ||
let minItemsToDisplay = 2; | ||
// Audio/video quality select boxes contain an additional 'auto' mode, which in combination with a single | ||
// available quality also does not make sense | ||
if ((this.setting instanceof VideoQualitySelectBox && this.setting.hasAutoItem()) | ||
|| this.setting instanceof AudioQualitySelectBox) { | ||
minItemsToDisplay = 3; | ||
} | ||
if (this.setting.itemCount() < minItemsToDisplay) { | ||
// Hide the setting if no meaningful choice is available | ||
this.hide(); | ||
} else if (this.setting instanceof PlaybackSpeedSelectBox | ||
&& !uimanager.getConfig().playbackSpeedSelectionEnabled) { | ||
// Hide the PlaybackSpeedSelectBox if disabled in config | ||
this.hide(); | ||
} else { | ||
this.show(); | ||
} | ||
|
||
// Visibility might have changed and therefore the active state might have changed so we fire the event | ||
// TODO fire only when state has really changed (e.g. check if visibility has really changed) | ||
this.onActiveChangedEvent(); | ||
|
||
this.getDomElement().attr('aria-haspopup', 'true'); | ||
}; | ||
|
||
this.setting.onItemAdded.subscribe(handleConfigItemChanged); | ||
this.setting.onItemRemoved.subscribe(handleConfigItemChanged); | ||
|
||
// Initialize hidden state | ||
handleConfigItemChanged(); | ||
} | ||
else if (this.isOption) { | ||
this.show(); | ||
this.onActiveChangedEvent(); | ||
this.getLabel.getDomElement().addClass(this.prefixCss('option')); | ||
} | ||
|
||
const handleItemClick = (e: Event) => { | ||
if (this.setting !== null) { | ||
if (!this.isOption) { | ||
this.displayItemsSubPage(); | ||
} | ||
|
||
else { | ||
if (this.setting instanceof SelectBox || this.setting instanceof ListBox) { | ||
this.setting.selectItem(this.key); | ||
this.modernSettingsPanelItemEvents.onItemSelect.dispatch(this, this.key); | ||
this.getLabel.getDomElement().addClass(this.prefixCss('selected')); | ||
} | ||
} | ||
} else { | ||
this.modernSettingsPanelItemEvents.onRequestNavigateBack.dispatch(this); | ||
} | ||
}; | ||
const domElement = this.getDomElement(); | ||
domElement.on('click', (e) => handleItemClick(e)); | ||
} | ||
|
||
private getSubPage(): ModernSettingsPanelPage { | ||
if (this.setting instanceof SelectBox || this.setting instanceof ListBox) { | ||
let menuOptions = this.setting.getItems(); | ||
let selectedItem = this.setting.getSelectedItem(); | ||
let page = new ModernSettingsPanelPage({}); | ||
let label = this.getLabel instanceof Label ? new Label({ text: this.getLabel.getConfig().text, for: page.getConfig().id } as LabelConfig) : new Label({ text: i18n.getLocalizer('back'), for: page.getConfig().id } as LabelConfig); | ||
label.getDomElement().addClass(this.prefixCss('heading')); | ||
let itemToAdd = new ModernSettingsPanelItem(label, null); | ||
itemToAdd.configure(this.player, this.uimanager); | ||
page.addComponent(itemToAdd); | ||
|
||
for (let option of menuOptions) { | ||
let itemToAdd = new ModernSettingsPanelItem(option.label, this.setting, option.key); | ||
itemToAdd.configure(this.player, this.uimanager); | ||
|
||
if (option.key === selectedItem) { | ||
itemToAdd.getLabel.getDomElement().addClass(this.prefixCss('selected')); | ||
} | ||
page.addComponent(itemToAdd); | ||
} | ||
page.configure(this.player, this.uimanager); | ||
return page; | ||
} | ||
} | ||
|
||
public getSetting(): Component<ComponentConfig> { | ||
return this.setting; | ||
} | ||
|
||
public displayItemsSubPage(): void { | ||
let page = this.getSubPage(); | ||
this.modernSettingsPanelItemEvents.onRequestSubPage.dispatch(this, page); | ||
} | ||
|
||
/** | ||
* Gets the event that is fired, when the SettingsPanelItem has been clicked | ||
* and wants to display its sub menu on the {@link ModernSettingsPanel} as a seperate {@link ModernSettingsPanelPage} | ||
*/ | ||
get getOnDisplaySubPage(): EDEvent<ModernSettingsPanelItem, NoArgs> { | ||
return this.modernSettingsPanelItemEvents.onRequestSubPage.getEvent(); | ||
} | ||
|
||
get getOnRequestNavigateBack(): EDEvent<ModernSettingsPanelItem, NoArgs> { | ||
return this.modernSettingsPanelItemEvents.onRequestNavigateBack.getEvent(); | ||
} | ||
|
||
get onItemSelect(): EDEvent<ModernSettingsPanelItem, string> { | ||
return this.modernSettingsPanelItemEvents.onItemSelect.getEvent(); | ||
} | ||
} |
Oops, something went wrong.