2018-07-08 05:15:02 +00:00
import QtQuick 2.9
2018-07-08 17:19:08 +00:00
import QtQuick . Controls 2.2
2020-11-24 06:13:39 +00:00
import QtQuick . Layouts 1.2
2023-09-11 05:08:20 +00:00
import QtQuick . Window 2.2
2018-07-08 17:19:08 +00:00
2018-07-09 05:56:38 +00:00
import StreamingPreferences 1.0
2018-09-09 19:33:19 +00:00
import ComputerManager 1.0
2018-09-30 20:41:32 +00:00
import SdlGamepadKeyNavigation 1.0
2019-03-23 19:05:08 +00:00
import SystemProperties 1.0
2018-07-09 05:56:38 +00:00
2018-11-14 02:03:53 +00:00
Flickable {
2018-07-08 05:15:02 +00:00
id: settingsPage
2020-11-21 17:42:16 +00:00
objectName: qsTr ( "Settings" )
2018-11-14 02:03:53 +00:00
2022-04-10 18:17:42 +00:00
signal languageChanged ( )
2020-02-20 21:38:01 +00:00
boundsBehavior: Flickable . OvershootBounds
2018-11-14 02:03:53 +00:00
contentWidth: settingsColumn1 . width > settingsColumn2 . width ? settingsColumn1.width : settingsColumn2 . width
contentHeight: settingsColumn1 . height > settingsColumn2 . height ? settingsColumn1.height : settingsColumn2 . height
ScrollBar.vertical: ScrollBar {
anchors {
2020-05-10 19:53:27 +00:00
left: parent . right
2018-11-14 02:03:53 +00:00
leftMargin: - 10
}
}
2018-07-08 05:15:02 +00:00
2023-09-11 05:08:20 +00:00
function isChildOfFlickable ( item ) {
while ( item ) {
if ( item . parent === contentItem ) {
return true
}
item = item . parent
}
return false
}
NumberAnimation on contentY {
id: autoScrollAnimation
duration: 100
}
Window.onActiveFocusItemChanged: {
var item = Window . activeFocusItem
if ( item ) {
// Ignore non-child elements like the toolbar buttons
if ( ! isChildOfFlickable ( item ) ) {
return
}
// Map the focus item's position into our content item's coordinate space
var pos = item . mapToItem ( contentItem , 0 , 0 )
// Ensure some extra space is visible around the element we're scrolling to
var scrollMargin = height > 100 ? 50 : 0
if ( pos . y - scrollMargin < contentY ) {
autoScrollAnimation . from = contentY
autoScrollAnimation . to = Math . max ( pos . y - scrollMargin , 0 )
autoScrollAnimation . start ( )
}
else if ( pos . y + item . height + scrollMargin > contentY + height ) {
autoScrollAnimation . from = contentY
autoScrollAnimation . to = Math . min ( pos . y + item . height + scrollMargin - height , contentHeight - height )
autoScrollAnimation . start ( )
}
}
}
2019-02-23 06:14:06 +00:00
StackView.onActivated: {
2019-05-19 17:16:54 +00:00
// This enables Tab and BackTab based navigation rather than arrow keys.
// It is required to shift focus between controls on the settings page.
SdlGamepadKeyNavigation . setUiNavMode ( true )
2019-05-19 18:08:23 +00:00
// Highlight the first item if a gamepad is connected
if ( SdlGamepadKeyNavigation . getConnectedGamepads ( ) > 0 ) {
resolutionComboBox . forceActiveFocus ( Qt . TabFocus )
}
2018-09-30 20:41:32 +00:00
}
2019-02-23 06:14:06 +00:00
StackView.onDeactivating: {
2019-05-19 17:16:54 +00:00
SdlGamepadKeyNavigation . setUiNavMode ( false )
2019-03-28 01:13:20 +00:00
// Save the prefs so the Session can observe the changes
StreamingPreferences . save ( )
2018-07-09 05:56:38 +00:00
}
2021-01-14 01:43:52 +00:00
Component.onDestruction: {
// Also save preferences on destruction, since we won't get a
// deactivating callback if the user just closes Moonlight
StreamingPreferences . save ( )
}
2018-07-08 17:19:08 +00:00
Column {
2018-07-20 22:47:50 +00:00
padding: 10
id: settingsColumn1
2020-05-10 19:30:15 +00:00
width: settingsPage . width / 2
spacing: 15
2018-07-08 17:19:08 +00:00
GroupBox {
2018-07-08 18:12:22 +00:00
id: basicSettingsGroupBox
2020-05-10 20:02:10 +00:00
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
2018-07-08 17:19:08 +00:00
padding: 12
2020-11-21 19:15:54 +00:00
title: "<font color=\"skyblue\">" + qsTr ( "Basic Settings" ) + "</font>"
2018-07-08 18:12:22 +00:00
font.pointSize: 12
2018-07-08 17:19:08 +00:00
Column {
anchors.fill: parent
spacing: 5
Label {
width: parent . width
id: resFPStitle
2018-07-28 08:22:31 +00:00
text: qsTr ( "Resolution and FPS" )
2018-07-08 17:19:08 +00:00
font.pointSize: 12
2018-11-22 10:35:25 +00:00
wrapMode: Text . Wrap
2018-07-08 17:19:08 +00:00
}
Label {
width: parent . width
id: resFPSdesc
2018-11-04 22:36:12 +00:00
text: qsTr ( "Setting values too high for your PC or network connection may cause lag, stuttering, or errors." )
2018-07-08 17:19:08 +00:00
font.pointSize: 9
wrapMode: Text . Wrap
}
2018-07-28 08:22:31 +00:00
Row {
spacing: 5
2020-05-10 18:12:37 +00:00
width: parent . width
2018-07-28 08:22:31 +00:00
2018-10-06 19:12:05 +00:00
AutoResizingComboBox {
2020-11-24 06:13:39 +00:00
property int lastIndexValue
2024-05-11 23:24:40 +00:00
function addDetectedResolution ( friendlyNamePrefix , rect ) {
var indexToAdd = 0
for ( var j = 0 ; j < resolutionComboBox . count ; j ++ ) {
var existing_width = parseInt ( resolutionListModel . get ( j ) . video_width ) ;
var existing_height = parseInt ( resolutionListModel . get ( j ) . video_height ) ;
if ( rect . width === existing_width && rect . height === existing_height ) {
// Duplicate entry, skip
indexToAdd = - 1
break
}
else if ( rect . width * rect . height > existing_width * existing_height ) {
// Candidate entrypoint after this entry
indexToAdd = j + 1
}
}
// Insert this display's resolution if it's not a duplicate
if ( indexToAdd >= 0 ) {
resolutionListModel . insert ( indexToAdd ,
{
"text" : friendlyNamePrefix + " (" + rect . width + "x" + rect . height + ")" ,
"video_width" : "" + rect . width ,
"video_height" : "" + rect . height ,
"is_custom" : false
} )
}
}
2018-07-28 08:22:31 +00:00
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
2020-12-08 01:58:42 +00:00
// Refresh display data before using it to build the list
SystemProperties . refreshDisplays ( )
2024-05-11 23:24:40 +00:00
// Add native and safe area resolutions for all attached displays
2018-09-04 04:21:37 +00:00
var done = false
for ( var displayIndex = 0 ; ! done ; displayIndex ++ ) {
2022-08-16 06:22:26 +00:00
var screenRect = SystemProperties . getNativeResolution ( displayIndex ) ;
2024-05-11 23:24:40 +00:00
var safeAreaRect = SystemProperties . getSafeAreaResolution ( displayIndex ) ;
2018-08-05 21:55:26 +00:00
2022-08-16 06:22:26 +00:00
if ( screenRect . width === 0 ) {
// Exceeded max count of displays
done = true
break
}
2024-05-11 23:24:40 +00:00
addDetectedResolution ( qsTr ( "Native" ) , screenRect )
addDetectedResolution ( qsTr ( "Native (Excluding Notch)" ) , safeAreaRect )
2018-08-05 21:55:26 +00:00
}
2020-02-23 08:43:43 +00:00
// Prune resolutions that are over the decoder's maximum
var max_pixels = SystemProperties . maximumResolution . width * SystemProperties . maximumResolution . height ;
if ( max_pixels > 0 ) {
for ( var j = 0 ; j < resolutionComboBox . count ; j ++ ) {
var existing_width = parseInt ( resolutionListModel . get ( j ) . video_width ) ;
var existing_height = parseInt ( resolutionListModel . get ( j ) . video_height ) ;
if ( existing_width * existing_height > max_pixels ) {
resolutionListModel . remove ( j )
j --
}
}
}
2018-07-28 08:22:31 +00:00
// load the saved width/height, and iterate through the ComboBox until a match is found
// and set it to that index.
2019-03-28 01:13:20 +00:00
var saved_width = StreamingPreferences . width
var saved_height = StreamingPreferences . height
2020-11-24 06:13:39 +00:00
var index_set = false
2018-09-08 21:33:34 +00:00
for ( var i = 0 ; i < resolutionListModel . count ; i ++ ) {
2018-07-28 08:22:31 +00:00
var el_width = parseInt ( resolutionListModel . get ( i ) . video_width ) ;
var el_height = parseInt ( resolutionListModel . get ( i ) . video_height ) ;
2018-08-05 21:55:26 +00:00
2020-11-24 06:13:39 +00:00
if ( saved_width === el_width && saved_height === el_height ) {
2018-07-28 08:22:31 +00:00
currentIndex = i
2020-11-24 06:13:39 +00:00
index_set = true
break
2018-07-28 08:22:31 +00:00
}
}
2018-09-08 06:16:13 +00:00
2020-11-24 06:13:39 +00:00
if ( ! index_set ) {
// We did not find a match. This must be a custom resolution.
resolutionListModel . append ( {
2024-05-11 23:24:40 +00:00
"text" : qsTr ( "Custom" ) + " (" + StreamingPreferences . width + "x" + StreamingPreferences . height + ")" ,
2020-11-24 06:13:39 +00:00
"video_width" : "" + StreamingPreferences . width ,
"video_height" : "" + StreamingPreferences . height ,
"is_custom" : true
} )
currentIndex = resolutionListModel . count - 1
}
else {
resolutionListModel . append ( {
2024-05-11 23:24:40 +00:00
"text" : qsTr ( "Custom" ) ,
2020-11-24 06:13:39 +00:00
"video_width" : "" ,
"video_height" : "" ,
"is_custom" : true
} )
}
// Since we don't call activate() here, we need to trigger
// width calculation manually
recalculateWidth ( )
lastIndexValue = currentIndex
2018-07-16 01:27:41 +00:00
}
2018-07-28 08:22:31 +00:00
id: resolutionComboBox
2020-05-10 18:12:37 +00:00
maximumWidth: parent . width / 2
2018-07-28 08:22:31 +00:00
textRole: "text"
model: ListModel {
id: resolutionListModel
2018-08-05 21:55:26 +00:00
// Other elements may be added at runtime
// based on attached display resolution
2018-07-28 08:22:31 +00:00
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "720p" )
2018-07-28 08:22:31 +00:00
video_width: "1280"
video_height: "720"
2020-11-24 06:13:39 +00:00
is_custom: false
2018-07-28 08:22:31 +00:00
}
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "1080p" )
2018-07-28 08:22:31 +00:00
video_width: "1920"
video_height: "1080"
2020-11-24 06:13:39 +00:00
is_custom: false
2018-07-28 08:22:31 +00:00
}
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "1440p" )
2018-07-28 08:22:31 +00:00
video_width: "2560"
video_height: "1440"
2020-11-24 06:13:39 +00:00
is_custom: false
2018-07-28 08:22:31 +00:00
}
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "4K" )
2018-07-28 08:22:31 +00:00
video_width: "3840"
video_height: "2160"
2020-11-24 06:13:39 +00:00
is_custom: false
2018-07-28 08:22:31 +00:00
}
2018-07-16 01:27:41 +00:00
}
2020-11-24 06:13:39 +00:00
function updateBitrateForSelection ( ) {
2018-09-08 06:16:13 +00:00
var selectedWidth = parseInt ( resolutionListModel . get ( currentIndex ) . video_width )
var selectedHeight = parseInt ( resolutionListModel . get ( currentIndex ) . video_height )
// Only modify the bitrate if the values actually changed
2019-03-28 01:13:20 +00:00
if ( StreamingPreferences . width !== selectedWidth || StreamingPreferences . height !== selectedHeight ) {
StreamingPreferences . width = selectedWidth
StreamingPreferences . height = selectedHeight
StreamingPreferences . bitrateKbps = StreamingPreferences . getDefaultBitrate ( StreamingPreferences . width ,
StreamingPreferences . height ,
2024-05-15 13:22:42 +00:00
StreamingPreferences . fps ,
StreamingPreferences . enableYUV444 ) ;
2019-03-28 01:13:20 +00:00
slider . value = StreamingPreferences . bitrateKbps
2018-09-08 06:16:13 +00:00
}
2020-11-24 06:13:39 +00:00
lastIndexValue = currentIndex
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
if ( resolutionListModel . get ( currentIndex ) . is_custom ) {
customResolutionDialog . open ( )
}
else {
updateBitrateForSelection ( )
}
}
NavigableDialog {
id: customResolutionDialog
standardButtons: Dialog . Ok | Dialog . Cancel
onOpened: {
// Force keyboard focus on the textbox so keyboard navigation works
widthField . forceActiveFocus ( )
// standardButton() was added in Qt 5.10, so we must check for it first
if ( customResolutionDialog . standardButton ) {
customResolutionDialog . standardButton ( Dialog . Ok ) . enabled = customResolutionDialog . isInputValid ( )
}
}
onClosed: {
widthField . clear ( )
heightField . clear ( )
}
onRejected: {
resolutionComboBox . currentIndex = resolutionComboBox . lastIndexValue
}
function isInputValid ( ) {
// If we have text in either textbox that isn't valid,
// reject the input.
if ( ( ! widthField . acceptableInput && widthField . text ) ||
( ! heightField . acceptableInput && heightField . text ) ) {
return false
}
// The textboxes need to have text or placeholder text
if ( ( ! widthField . text && ! widthField . placeholderText ) ||
( ! heightField . text && ! heightField . placeholderText ) ) {
return false
}
return true
}
onAccepted: {
// Reject if there's invalid input
if ( ! isInputValid ( ) ) {
reject ( )
return
}
var width = widthField . text ? widthField.text : widthField . placeholderText
var height = heightField . text ? heightField.text : heightField . placeholderText
// Find and update the custom entry
for ( var i = 0 ; i < resolutionListModel . count ; i ++ ) {
if ( resolutionListModel . get ( i ) . is_custom ) {
resolutionListModel . setProperty ( i , "video_width" , width )
resolutionListModel . setProperty ( i , "video_height" , height )
resolutionListModel . setProperty ( i , "text" , "Custom (" + width + "x" + height + ")" )
// Now update the bitrate using the custom resolution
resolutionComboBox . currentIndex = i
resolutionComboBox . updateBitrateForSelection ( )
// Update the combobox width too
resolutionComboBox . recalculateWidth ( )
break
}
}
}
ColumnLayout {
Label {
text: qsTr ( "Custom resolutions are not officially supported by GeForce Experience, so it will not set your host display resolution. You will need to set it manually while in game." ) + "\n\n" +
qsTr ( "Resolutions that are not supported by your client or host PC may cause streaming errors." ) + "\n"
wrapMode: Label . WordWrap
Layout.maximumWidth: 300
}
Label {
text: qsTr ( "Enter a custom resolution:" )
font.bold: true
}
RowLayout {
TextField {
id: widthField
maximumLength: 5
inputMethodHints: Qt . ImhDigitsOnly
placeholderText: resolutionListModel . get ( resolutionComboBox . currentIndex ) . video_width
2022-02-19 16:47:25 +00:00
validator: IntValidator { bottom: 256 ; top: 8192 }
2020-11-24 06:13:39 +00:00
focus: true
onTextChanged: {
// standardButton() was added in Qt 5.10, so we must check for it first
if ( customResolutionDialog . standardButton ) {
customResolutionDialog . standardButton ( Dialog . Ok ) . enabled = customResolutionDialog . isInputValid ( )
}
}
Keys.onReturnPressed: {
customResolutionDialog . accept ( )
}
2021-02-27 23:01:22 +00:00
Keys.onEnterPressed: {
customResolutionDialog . accept ( )
}
2020-11-24 06:13:39 +00:00
}
Label {
text: "x"
font.bold: true
}
TextField {
id: heightField
maximumLength: 5
inputMethodHints: Qt . ImhDigitsOnly
placeholderText: resolutionListModel . get ( resolutionComboBox . currentIndex ) . video_height
2022-02-19 16:47:25 +00:00
validator: IntValidator { bottom: 256 ; top: 8192 }
2020-11-24 06:13:39 +00:00
onTextChanged: {
// standardButton() was added in Qt 5.10, so we must check for it first
if ( customResolutionDialog . standardButton ) {
customResolutionDialog . standardButton ( Dialog . Ok ) . enabled = customResolutionDialog . isInputValid ( )
}
}
Keys.onReturnPressed: {
customResolutionDialog . accept ( )
}
2021-02-27 23:01:22 +00:00
Keys.onEnterPressed: {
customResolutionDialog . accept ( )
}
2020-11-24 06:13:39 +00:00
}
}
}
2018-07-16 01:27:41 +00:00
}
2018-07-28 08:22:31 +00:00
}
2018-10-06 19:12:05 +00:00
AutoResizingComboBox {
2023-09-02 20:53:30 +00:00
property int lastIndexValue
function updateBitrateForSelection ( ) {
// Only modify the bitrate if the values actually changed
var selectedFps = parseInt ( model . get ( fpsComboBox . currentIndex ) . video_fps )
if ( StreamingPreferences . fps !== selectedFps ) {
StreamingPreferences . fps = selectedFps
StreamingPreferences . bitrateKbps = StreamingPreferences . getDefaultBitrate ( StreamingPreferences . width ,
StreamingPreferences . height ,
2024-05-15 13:22:42 +00:00
StreamingPreferences . fps ,
StreamingPreferences . enableYUV444 ) ;
2023-09-02 20:53:30 +00:00
slider . value = StreamingPreferences . bitrateKbps
}
lastIndexValue = currentIndex
}
NavigableDialog {
function isInputValid ( ) {
// If we have text that isn't valid, reject the input.
if ( ! fpsField . acceptableInput && fpsField . text ) {
return false
}
// The textbox needs to have text or placeholder text
if ( ! fpsField . text && ! fpsField . placeholderText ) {
return false
}
return true
}
id: customFpsDialog
standardButtons: Dialog . Ok | Dialog . Cancel
onOpened: {
// Force keyboard focus on the textbox so keyboard navigation works
fpsField . forceActiveFocus ( )
// standardButton() was added in Qt 5.10, so we must check for it first
if ( customFpsDialog . standardButton ) {
customFpsDialog . standardButton ( Dialog . Ok ) . enabled = customFpsDialog . isInputValid ( )
}
}
onClosed: {
fpsField . clear ( )
}
onRejected: {
fpsComboBox . currentIndex = fpsComboBox . lastIndexValue
}
onAccepted: {
// Reject if there's invalid input
if ( ! isInputValid ( ) ) {
reject ( )
return
}
var fps = fpsField . text ? fpsField.text : fpsField . placeholderText
// Find and update the custom entry
for ( var i = 0 ; i < fpsListModel . count ; i ++ ) {
if ( fpsListModel . get ( i ) . is_custom ) {
fpsListModel . setProperty ( i , "video_fps" , fps )
fpsListModel . setProperty ( i , "text" , qsTr ( "Custom (%1 FPS)" ) . arg ( fps ) )
// Now update the bitrate using the custom resolution
fpsComboBox . currentIndex = i
fpsComboBox . updateBitrateForSelection ( )
// Update the combobox width too
fpsComboBox . recalculateWidth ( )
break
}
}
}
ColumnLayout {
Label {
text: qsTr ( "Enter a custom frame rate:" )
font.bold: true
}
RowLayout {
TextField {
id: fpsField
maximumLength: 4
inputMethodHints: Qt . ImhDigitsOnly
placeholderText: fpsListModel . get ( fpsComboBox . currentIndex ) . video_fps
2023-09-08 05:59:56 +00:00
validator: IntValidator { bottom: 10 ; top: 9999 }
2023-09-02 20:53:30 +00:00
focus: true
onTextChanged: {
// standardButton() was added in Qt 5.10, so we must check for it first
if ( customFpsDialog . standardButton ) {
customFpsDialog . standardButton ( Dialog . Ok ) . enabled = customFpsDialog . isInputValid ( )
}
}
Keys.onReturnPressed: {
customFpsDialog . accept ( )
}
Keys.onEnterPressed: {
customFpsDialog . accept ( )
}
}
}
}
}
function addRefreshRateOrdered ( fpsListModel , refreshRate , description , custom ) {
2023-02-03 06:45:27 +00:00
var indexToAdd = 0
for ( var j = 0 ; j < fpsListModel . count ; j ++ ) {
var existing_fps = parseInt ( fpsListModel . get ( j ) . video_fps ) ;
2023-09-02 20:53:30 +00:00
if ( refreshRate === existing_fps || ( custom && fpsListModel . get ( j ) . is_custom ) ) {
2023-02-03 06:45:27 +00:00
// Duplicate entry, skip
indexToAdd = - 1
break
}
else if ( refreshRate > existing_fps ) {
// Candidate entrypoint after this entry
indexToAdd = j + 1
}
}
2023-09-02 20:53:30 +00:00
// Insert this frame rate if it's not a duplicate
2023-02-03 06:45:27 +00:00
if ( indexToAdd >= 0 ) {
2023-09-02 20:53:30 +00:00
// Custom values always go at the end of the list
if ( custom ) {
indexToAdd = fpsListModel . count
}
2023-02-03 06:45:27 +00:00
fpsListModel . insert ( indexToAdd ,
{
2023-09-02 20:53:30 +00:00
"text" : description ,
"video_fps" : "" + refreshRate ,
"is_custom" : custom
2023-02-03 06:45:27 +00:00
} )
}
return indexToAdd
}
2023-09-02 20:53:30 +00:00
function reinitialize ( ) {
2023-02-03 06:45:27 +00:00
// Add native refresh rate for all attached displays
var done = false
for ( var displayIndex = 0 ; ! done ; displayIndex ++ ) {
var refreshRate = SystemProperties . getRefreshRate ( displayIndex ) ;
if ( refreshRate === 0 ) {
// Exceeded max count of displays
done = true
break
2018-09-08 22:09:46 +00:00
}
2023-09-02 20:53:30 +00:00
addRefreshRateOrdered ( fpsListModel , refreshRate , qsTr ( "%1 FPS" ) . arg ( refreshRate ) , false )
2018-09-08 21:33:34 +00:00
}
2019-03-28 01:13:20 +00:00
var saved_fps = StreamingPreferences . fps
2023-09-13 00:28:33 +00:00
var found = false
2018-09-08 21:33:34 +00:00
for ( var i = 0 ; i < model . count ; i ++ ) {
var el_fps = parseInt ( model . get ( i ) . video_fps ) ;
2018-08-05 21:55:26 +00:00
2023-02-03 06:45:27 +00:00
// Look for a matching frame rate
if ( saved_fps === el_fps ) {
2018-07-28 08:22:31 +00:00
currentIndex = i
2023-09-13 00:28:33 +00:00
found = true
2023-02-03 06:45:27 +00:00
break
2018-07-28 08:22:31 +00:00
}
}
2018-09-08 06:16:13 +00:00
2023-02-03 06:45:27 +00:00
// If we didn't find one, add a custom frame rate for the current value
2023-09-13 00:28:33 +00:00
if ( ! found ) {
2023-09-02 20:53:30 +00:00
currentIndex = addRefreshRateOrdered ( model , saved_fps , qsTr ( "Custom (%1 FPS)" ) . arg ( saved_fps ) , true )
}
else {
addRefreshRateOrdered ( model , "" , qsTr ( "Custom" ) , true )
2023-02-03 06:45:27 +00:00
}
2023-09-02 20:53:30 +00:00
recalculateWidth ( )
lastIndexValue = currentIndex
2018-07-16 01:27:41 +00:00
}
2018-07-28 08:22:31 +00:00
2018-09-08 21:33:34 +00:00
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
reinitialize ( )
2022-04-10 18:17:42 +00:00
languageChanged . connect ( reinitialize )
2018-09-08 21:33:34 +00:00
}
2023-09-02 20:53:30 +00:00
model: ListModel {
id: fpsListModel
// Other elements may be added at runtime
ListElement {
text: qsTr ( "30 FPS" )
video_fps: "30"
is_custom: false
}
ListElement {
text: qsTr ( "60 FPS" )
video_fps: "60"
is_custom: false
}
}
2018-07-28 08:22:31 +00:00
id: fpsComboBox
2020-05-10 18:12:37 +00:00
maximumWidth: parent . width / 2
2018-07-28 08:22:31 +00:00
textRole: "text"
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
2023-09-02 20:53:30 +00:00
if ( model . get ( currentIndex ) . is_custom ) {
customFpsDialog . open ( )
}
else {
updateBitrateForSelection ( )
2018-09-08 06:16:13 +00:00
}
2018-07-16 01:27:41 +00:00
}
}
2018-07-08 17:19:08 +00:00
}
Label {
width: parent . width
id: bitrateTitle
2020-11-21 19:15:54 +00:00
text: qsTr ( "Video bitrate:" )
2018-07-08 17:19:08 +00:00
font.pointSize: 12
wrapMode: Text . Wrap
}
Label {
width: parent . width
id: bitrateDesc
2018-12-25 19:48:34 +00:00
text: qsTr ( "Lower the bitrate on slower connections. Raise the bitrate to increase image quality." )
2018-07-08 17:19:08 +00:00
font.pointSize: 9
wrapMode: Text . Wrap
}
Slider {
id: slider
2019-03-28 01:13:20 +00:00
value: StreamingPreferences . bitrateKbps
2018-07-18 01:52:06 +00:00
2018-07-08 17:19:08 +00:00
stepSize: 500
from : 500
2018-07-28 08:22:31 +00:00
to: 150000
2018-07-18 01:52:06 +00:00
2018-07-08 17:19:08 +00:00
snapMode: "SnapOnRelease"
width: Math . min ( bitrateDesc . implicitWidth , parent . width )
2018-07-09 05:56:38 +00:00
onValueChanged: {
2020-11-21 19:15:54 +00:00
bitrateTitle . text = qsTr ( "Video bitrate: %1 Mbps" ) . arg ( value / 1000.0 )
2019-03-28 01:13:20 +00:00
StreamingPreferences . bitrateKbps = value
2018-07-08 17:19:08 +00:00
}
2023-09-02 23:47:42 +00:00
Component.onCompleted: {
// Refresh the text after translations change
2024-04-13 05:05:11 +00:00
languageChanged . connect ( valueChanged )
2023-09-02 23:47:42 +00:00
}
2018-07-08 17:19:08 +00:00
}
2018-07-08 18:12:22 +00:00
2018-09-04 02:17:34 +00:00
Label {
width: parent . width
id: windowModeTitle
text: qsTr ( "Display mode" )
font.pointSize: 12
wrapMode: Text . Wrap
2021-03-13 21:20:58 +00:00
visible: SystemProperties . hasDesktopEnvironment
2018-09-04 02:17:34 +00:00
}
2018-10-06 19:12:05 +00:00
AutoResizingComboBox {
2022-04-10 18:17:42 +00:00
function createModel ( ) {
var model = Qt . createQmlObject ( 'import QtQuick 2.0; ListModel {}' , parent , '' )
model . append ( {
text: qsTr ( "Fullscreen" ) ,
val: StreamingPreferences . WM_FULLSCREEN
} )
model . append ( {
text: qsTr ( "Borderless windowed" ) ,
val: StreamingPreferences . WM_FULLSCREEN_DESKTOP
} )
model . append ( {
text: qsTr ( "Windowed" ) ,
val: StreamingPreferences . WM_WINDOWED
} )
// Set the recommended option based on the OS
for ( var i = 0 ; i < model . count ; i ++ ) {
var thisWm = model . get ( i ) . val ;
if ( thisWm === StreamingPreferences . recommendedFullScreenMode ) {
2023-09-02 23:41:27 +00:00
model . get ( i ) . text += " " + qsTr ( "(Recommended)" )
2022-04-10 18:17:42 +00:00
model . move ( i , 0 , 1 )
break
}
}
return model
}
// This is used on initialization and upon retranslation
function reinitialize ( ) {
2021-02-07 16:49:29 +00:00
if ( ! visible ) {
// Do nothing if the control won't even be visible
return
}
2022-04-10 18:17:42 +00:00
model = createModel ( )
2018-09-04 02:17:34 +00:00
currentIndex = 0
2020-02-09 05:31:04 +00:00
2022-04-10 18:17:42 +00:00
// Set the current value based on the saved preferences
2021-02-07 16:49:29 +00:00
var savedWm = StreamingPreferences . windowMode
2022-04-10 18:17:42 +00:00
for ( var i = 0 ; i < model . count ; i ++ ) {
var thisWm = model . get ( i ) . val ;
2021-02-07 16:49:29 +00:00
if ( savedWm === thisWm ) {
currentIndex = i
break
}
2018-08-21 05:25:19 +00:00
}
2020-02-09 05:31:04 +00:00
2018-09-08 06:16:13 +00:00
activated ( currentIndex )
2018-08-21 05:25:19 +00:00
}
2022-04-10 18:17:42 +00:00
Component.onCompleted: {
reinitialize ( )
languageChanged . connect ( reinitialize )
}
2018-09-04 02:17:34 +00:00
id: windowModeComboBox
2021-03-13 21:20:58 +00:00
visible: SystemProperties . hasDesktopEnvironment
2021-01-16 01:46:27 +00:00
enabled: ! SystemProperties . rendererAlwaysFullScreen
2018-11-04 22:36:12 +00:00
hoverEnabled: true
2018-09-04 02:17:34 +00:00
textRole: "text"
onActivated: {
2022-04-10 18:17:42 +00:00
StreamingPreferences . windowMode = model . get ( currentIndex ) . val
2018-09-04 02:17:34 +00:00
}
2018-11-04 22:36:12 +00:00
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2021-01-10 15:50:12 +00:00
ToolTip.text: qsTr ( "Fullscreen generally provides the best performance, but borderless windowed may work better with features like macOS Spaces, Alt+Tab, screenshot tools, on-screen overlays, etc." )
2018-09-04 02:17:34 +00:00
}
CheckBox {
id: vsyncCheck
2020-05-10 18:12:37 +00:00
width: parent . width
2018-11-04 22:36:12 +00:00
hoverEnabled: true
2020-11-21 19:15:54 +00:00
text: qsTr ( "V-Sync" )
2018-09-04 02:17:34 +00:00
font.pointSize: 12
2019-03-28 01:13:20 +00:00
checked: StreamingPreferences . enableVsync
2018-09-04 02:17:34 +00:00
onCheckedChanged: {
2019-03-28 01:13:20 +00:00
StreamingPreferences . enableVsync = checked
2018-09-04 02:17:34 +00:00
}
2018-11-04 22:36:12 +00:00
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2020-11-21 17:42:16 +00:00
ToolTip.text: qsTr ( "Disabling V-Sync allows sub-frame rendering latency, but it can display visible tearing" )
2018-07-08 18:12:22 +00:00
}
2018-12-25 20:57:00 +00:00
CheckBox {
id: framePacingCheck
2020-05-10 18:12:37 +00:00
width: parent . width
2018-12-25 20:57:00 +00:00
hoverEnabled: true
2020-11-21 17:42:16 +00:00
text: qsTr ( "Frame pacing" )
2018-12-25 20:57:00 +00:00
font.pointSize: 12
2019-03-28 01:13:20 +00:00
enabled: StreamingPreferences . enableVsync
checked: StreamingPreferences . enableVsync && StreamingPreferences . framePacing
2018-12-25 20:57:00 +00:00
onCheckedChanged: {
2019-03-28 01:13:20 +00:00
StreamingPreferences . framePacing = checked
2018-12-25 20:57:00 +00:00
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2020-11-21 17:42:16 +00:00
ToolTip.text: qsTr ( "Frame pacing reduces micro-stutter by delaying frames that come in too early" )
2018-12-25 20:57:00 +00:00
}
2018-07-08 18:12:22 +00:00
}
}
GroupBox {
2018-07-18 02:39:16 +00:00
2018-07-08 18:12:22 +00:00
id: audioSettingsGroupBox
2020-05-10 20:02:10 +00:00
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
2018-07-08 18:12:22 +00:00
padding: 12
2020-11-21 19:15:54 +00:00
title: "<font color=\"skyblue\">" + qsTr ( "Audio Settings" ) + "</font>"
2018-07-08 18:12:22 +00:00
font.pointSize: 12
Column {
anchors.fill: parent
spacing: 5
2018-07-20 21:35:47 +00:00
Label {
width: parent . width
id: resAudioTitle
text: qsTr ( "Audio configuration" )
font.pointSize: 12
wrapMode: Text . Wrap
}
2018-07-18 02:39:16 +00:00
2018-10-06 19:12:05 +00:00
AutoResizingComboBox {
2018-07-20 21:35:47 +00:00
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
2019-03-28 01:13:20 +00:00
var saved_audio = StreamingPreferences . audioConfig
2018-07-20 21:35:47 +00:00
currentIndex = 0
2018-09-08 06:16:13 +00:00
for ( var i = 0 ; i < audioListModel . count ; i ++ ) {
var el_audio = audioListModel . get ( i ) . val ;
if ( saved_audio === el_audio ) {
currentIndex = i
break
}
2018-07-20 21:35:47 +00:00
}
2018-09-08 06:16:13 +00:00
activated ( currentIndex )
2018-07-20 21:35:47 +00:00
}
2018-07-18 02:39:16 +00:00
2018-07-20 21:35:47 +00:00
id: audioComboBox
textRole: "text"
model: ListModel {
id: audioListModel
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "Stereo" )
2018-10-02 22:30:22 +00:00
val: StreamingPreferences . AC_STEREO
2018-07-20 21:35:47 +00:00
}
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "5.1 surround sound" )
2018-10-02 22:30:22 +00:00
val: StreamingPreferences . AC_51_SURROUND
2018-07-20 21:35:47 +00:00
}
2020-04-03 07:12:52 +00:00
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "7.1 surround sound" )
2020-04-03 07:12:52 +00:00
val: StreamingPreferences . AC_71_SURROUND
}
2018-07-20 21:35:47 +00:00
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
2019-03-28 01:13:20 +00:00
StreamingPreferences . audioConfig = audioListModel . get ( currentIndex ) . val
2018-07-18 02:39:16 +00:00
}
2018-07-08 18:12:22 +00:00
}
2018-07-20 21:35:47 +00:00
2020-12-25 18:19:22 +00:00
CheckBox {
id: audioPcCheck
width: parent . width
text: qsTr ( "Mute host PC speakers while streaming" )
font.pointSize: 12
checked: ! StreamingPreferences . playAudioOnHost
onCheckedChanged: {
StreamingPreferences . playAudioOnHost = ! checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr ( "You must restart any game currently in progress for this setting to take effect" )
}
2020-12-25 21:32:11 +00:00
CheckBox {
2021-01-09 23:56:23 +00:00
id: muteOnFocusLossCheck
2020-12-25 21:32:11 +00:00
width: parent . width
2021-01-09 23:56:23 +00:00
text: qsTr ( "Mute audio stream when Moonlight is not the active window" )
2020-12-25 21:32:11 +00:00
font.pointSize: 12
2021-03-13 21:20:58 +00:00
visible: SystemProperties . hasDesktopEnvironment
2021-01-09 23:56:23 +00:00
checked: StreamingPreferences . muteOnFocusLoss
2020-12-25 21:32:11 +00:00
onCheckedChanged: {
2021-01-09 23:56:23 +00:00
StreamingPreferences . muteOnFocusLoss = checked
2020-12-25 21:32:11 +00:00
}
2021-01-09 23:56:23 +00:00
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr ( "Mutes Moonlight's audio when you Alt+Tab out of the stream or click on a different window." )
2020-12-25 21:32:11 +00:00
}
2018-07-08 18:12:22 +00:00
}
}
2018-11-16 00:41:02 +00:00
GroupBox {
id: uiSettingsGroupBox
2020-05-10 20:02:10 +00:00
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
2018-11-16 00:41:02 +00:00
padding: 12
2020-11-21 19:15:54 +00:00
title: "<font color=\"skyblue\">" + qsTr ( "UI Settings" ) + "</font>"
2018-11-16 00:41:02 +00:00
font.pointSize: 12
Column {
anchors.fill: parent
spacing: 5
2021-01-30 18:14:31 +00:00
Label {
width: parent . width
id: languageTitle
text: qsTr ( "Language" )
font.pointSize: 12
wrapMode: Text . Wrap
}
AutoResizingComboBox {
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
var saved_language = StreamingPreferences . language
currentIndex = 0
for ( var i = 0 ; i < languageListModel . count ; i ++ ) {
var el_language = languageListModel . get ( i ) . val ;
if ( saved_language === el_language ) {
currentIndex = i
break
}
}
activated ( currentIndex )
}
id: languageComboBox
textRole: "text"
model: ListModel {
id: languageListModel
ListElement {
text: qsTr ( "Automatic" )
val: StreamingPreferences . LANG_AUTO
}
ListElement {
2021-10-07 02:03:02 +00:00
text: "Deutsch" // German
2021-03-10 02:10:36 +00:00
val: StreamingPreferences . LANG_DE
}
ListElement {
text: "English"
2021-01-30 18:14:31 +00:00
val: StreamingPreferences . LANG_EN
}
ListElement {
2021-10-07 02:03:02 +00:00
text: "Français" // French
2021-01-30 18:14:31 +00:00
val: StreamingPreferences . LANG_FR
}
ListElement {
2021-10-07 02:03:02 +00:00
text: "简体中文" // Simplified Chinese
2021-01-30 18:14:31 +00:00
val: StreamingPreferences . LANG_ZH_CN
}
2021-03-25 01:40:22 +00:00
ListElement {
text: "Norwegian Bokmål"
val: StreamingPreferences . LANG_NB_NO
}
2021-04-04 15:25:06 +00:00
ListElement {
text: "русский" // Russian
val: StreamingPreferences . LANG_RU
}
2021-12-11 20:32:38 +00:00
ListElement {
2021-04-04 15:25:06 +00:00
text: "Español" // Spanish
val: StreamingPreferences . LANG_ES
2021-12-11 20:32:38 +00:00
}
2021-05-13 05:24:00 +00:00
ListElement {
text: "日本語" // Japanese
val: StreamingPreferences . LANG_JA
}
ListElement {
text: "Tiếng Việt" // Vietnamese
val: StreamingPreferences . LANG_VI
}
2021-05-22 18:44:45 +00:00
ListElement {
text: "ภาษาไทย" // Thai
val: StreamingPreferences . LANG_TH
}
ListElement {
text: "한국어" // Korean
val: StreamingPreferences . LANG_KO
}
2024-06-29 22:19:35 +00:00
ListElement {
2021-10-07 02:03:02 +00:00
text: "Magyar" // Hungarian
val: StreamingPreferences . LANG_HU
2024-06-29 22:19:35 +00:00
}
2021-10-07 02:03:02 +00:00
ListElement {
text: "Nederlands" // Dutch
val: StreamingPreferences . LANG_NL
}
ListElement {
text: "Svenska" // Swedish
val: StreamingPreferences . LANG_SV
}
/ * L i s t E l e m e n t {
text: "Türkçe" // Turkish
val: StreamingPreferences . LANG_TR
} * /
/ * L i s t E l e m e n t {
text: "Українська" // Ukrainian
val: StreamingPreferences . LANG_UK
} * /
2021-12-11 20:32:38 +00:00
ListElement {
2023-04-13 15:46:12 +00:00
text: "繁體中文" // Traditional Chinese
2022-12-16 04:48:44 +00:00
val: StreamingPreferences . LANG_ZH_TW
2021-12-11 20:32:38 +00:00
}
2022-02-20 20:14:34 +00:00
ListElement {
text: "Português" // Portuguese
val: StreamingPreferences . LANG_PT
}
/ * L i s t E l e m e n t {
text: "Português do Brasil" // Brazilian Portuguese
val: StreamingPreferences . LANG_PT_BR
} * /
ListElement {
text: "Ελληνικά" // Greek
val: StreamingPreferences . LANG_EL
}
ListElement {
text: "Italiano" // Italian
val: StreamingPreferences . LANG_IT
}
/ * L i s t E l e m e n t {
text: "हिन्दी, हिंदी" // Hindi
val: StreamingPreferences . LANG_HI
} * /
2022-05-07 19:32:07 +00:00
ListElement {
text: "Język polski" // Polish
val: StreamingPreferences . LANG_PL
}
ListElement {
text: "Čeština" // Czech
val: StreamingPreferences . LANG_CS
}
2023-07-16 20:47:31 +00:00
/ * L i s t E l e m e n t {
text: "עִבְרִית" // Hebrew
val: StreamingPreferences . LANG_HE
} * /
2024-05-01 03:35:04 +00:00
/ * L i s t E l e m e n t {
text: "کرمانجیی خواروو" // Central Kurdish
val: StreamingPreferences . LANG_CKB
} * /
2024-06-29 22:17:45 +00:00
/ * L i s t E l e m e n t {
text: "Lietuvių kalba" // Lithuanian
val: StreamingPreferences . LANG_LT
} * /
2021-01-30 18:14:31 +00:00
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
2021-01-31 21:28:00 +00:00
// Retranslating is expensive, so only do it if the language actually changed
var new_language = languageListModel . get ( currentIndex ) . val
if ( StreamingPreferences . language !== new_language ) {
StreamingPreferences . language = languageListModel . get ( currentIndex ) . val
if ( ! StreamingPreferences . retranslate ( ) ) {
ToolTip . show ( qsTr ( "You must restart Moonlight for this change to take effect" ) , 5000 )
}
2021-03-05 23:44:31 +00:00
else {
// Force the back operation to pop any AppView pages that exist.
// The AppView stops working after retranslate() for some reason.
window . clearOnBack = true
2022-04-10 18:17:42 +00:00
// Signal other controls to adjust their text
languageChanged ( )
2021-03-05 23:44:31 +00:00
}
2021-01-30 18:14:31 +00:00
}
}
}
2021-01-10 15:50:12 +00:00
Label {
2020-05-10 18:12:37 +00:00
width: parent . width
2021-01-10 15:50:12 +00:00
id: uiDisplayModeTitle
2021-01-10 16:09:31 +00:00
text: qsTr ( "GUI display mode" )
2018-11-16 00:41:02 +00:00
font.pointSize: 12
2021-01-10 15:50:12 +00:00
wrapMode: Text . Wrap
2021-03-13 21:20:58 +00:00
visible: SystemProperties . hasDesktopEnvironment
2021-01-10 15:50:12 +00:00
}
AutoResizingComboBox {
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
2021-02-07 16:49:29 +00:00
if ( ! visible ) {
// Do nothing if the control won't even be visible
return
2021-01-10 15:50:12 +00:00
}
2021-02-07 16:49:29 +00:00
var saved_uidisplaymode = StreamingPreferences . uiDisplayMode
currentIndex = 0
for ( var i = 0 ; i < uiDisplayModeListModel . count ; i ++ ) {
var el_uidisplaymode = uiDisplayModeListModel . get ( i ) . val ;
if ( saved_uidisplaymode === el_uidisplaymode ) {
currentIndex = i
break
}
2021-01-10 16:09:31 +00:00
}
2021-01-10 15:50:12 +00:00
activated ( currentIndex )
}
id: uiDisplayModeComboBox
2021-03-13 21:20:58 +00:00
visible: SystemProperties . hasDesktopEnvironment
2021-01-10 15:50:12 +00:00
textRole: "text"
model: ListModel {
id: uiDisplayModeListModel
ListElement {
text: qsTr ( "Windowed" )
val: StreamingPreferences . UI_WINDOWED
}
ListElement {
text: qsTr ( "Maximized" )
2021-01-10 16:09:31 +00:00
val: StreamingPreferences . UI_MAXIMIZED
2021-01-10 15:50:12 +00:00
}
ListElement {
text: qsTr ( "Fullscreen" )
val: StreamingPreferences . UI_FULLSCREEN
}
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
StreamingPreferences . uiDisplayMode = uiDisplayModeListModel . get ( currentIndex ) . val
2018-11-16 00:41:02 +00:00
}
}
2019-03-19 03:21:52 +00:00
CheckBox {
id: connectionWarningsCheck
2020-05-10 18:12:37 +00:00
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Show connection quality warnings" )
2019-03-19 03:21:52 +00:00
font.pointSize: 12
2019-03-28 01:13:20 +00:00
checked: StreamingPreferences . connectionWarnings
2019-03-19 03:21:52 +00:00
onCheckedChanged: {
2019-03-28 01:13:20 +00:00
StreamingPreferences . connectionWarnings = checked
2019-03-19 03:21:52 +00:00
}
}
2019-06-30 00:40:30 +00:00
CheckBox {
visible: SystemProperties . hasDiscordIntegration
id: discordPresenceCheck
2020-05-10 18:12:37 +00:00
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Discord Rich Presence integration" )
2019-06-30 00:40:30 +00:00
font.pointSize: 12
checked: StreamingPreferences . richPresence
onCheckedChanged: {
StreamingPreferences . richPresence = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2020-11-21 17:42:16 +00:00
ToolTip.text: qsTr ( "Updates your Discord status to display the name of the game you're streaming." )
2019-06-30 00:40:30 +00:00
}
2022-03-17 02:48:56 +00:00
CheckBox {
id: keepAwakeCheck
width: parent . width
2022-03-17 02:59:06 +00:00
text: qsTr ( "Keep the display awake while streaming" )
2022-03-17 02:48:56 +00:00
font.pointSize: 12
checked: StreamingPreferences . keepAwake
onCheckedChanged: {
StreamingPreferences . keepAwake = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2022-03-17 02:59:06 +00:00
ToolTip.text: qsTr ( "Prevents the screensaver from starting or the display from going to sleep while streaming." )
2022-03-17 02:48:56 +00:00
}
2018-11-16 00:41:02 +00:00
}
}
2018-07-20 22:47:50 +00:00
}
Column {
padding: 10
2020-05-10 20:02:10 +00:00
rightPadding: 20
2018-07-20 22:47:50 +00:00
anchors.left: settingsColumn1 . right
id: settingsColumn2
2020-05-10 19:30:15 +00:00
width: settingsPage . width / 2
spacing: 15
2018-07-08 18:12:22 +00:00
GroupBox {
2020-12-25 18:19:22 +00:00
id: inputSettingsGroupBox
2020-05-10 20:02:10 +00:00
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
2018-07-08 18:12:22 +00:00
padding: 12
2020-11-21 19:15:54 +00:00
title: "<font color=\"skyblue\">" + qsTr ( "Input Settings" ) + "</font>"
2018-07-08 18:12:22 +00:00
font.pointSize: 12
2018-11-16 00:41:02 +00:00
Column {
2018-07-08 18:12:22 +00:00
anchors.fill: parent
spacing: 5
2018-09-09 20:21:11 +00:00
CheckBox {
2020-04-25 20:00:39 +00:00
id: absoluteMouseCheck
2018-11-04 21:31:28 +00:00
hoverEnabled: true
2020-05-10 18:12:37 +00:00
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Optimize mouse for remote desktop instead of games" )
2018-09-09 20:21:11 +00:00
font.pointSize: 12
2021-01-16 01:46:27 +00:00
checked: StreamingPreferences . absoluteMouseMode
2018-09-09 20:21:11 +00:00
onCheckedChanged: {
2020-04-25 20:00:39 +00:00
StreamingPreferences . absoluteMouseMode = checked
2018-09-09 20:21:11 +00:00
}
2018-11-04 21:31:28 +00:00
ToolTip.delay: 1000
2021-01-10 00:03:48 +00:00
ToolTip.timeout: 10000
2018-11-04 21:31:28 +00:00
ToolTip.visible: hovered
2021-01-10 00:03:48 +00:00
ToolTip.text: qsTr ( "This enables seamless mouse control without capturing the client's mouse cursor. It is ideal for remote desktop usage but will not work in most games." ) + " " +
qsTr ( "You can toggle this while streaming using Ctrl+Alt+Shift+M." ) + "\n\n" +
qsTr ( "NOTE: Due to a bug in GeForce Experience, this option may not work properly if your host PC has multiple monitors." )
2018-09-09 20:21:11 +00:00
}
2019-06-30 01:24:59 +00:00
2021-02-27 22:47:38 +00:00
Row {
spacing: 5
2021-01-13 02:05:18 +00:00
width: parent . width
2021-02-27 22:47:38 +00:00
CheckBox {
id: captureSysKeysCheck
hoverEnabled: true
text: qsTr ( "Capture system keyboard shortcuts" )
font.pointSize: 12
2021-03-13 21:20:58 +00:00
enabled: SystemProperties . hasDesktopEnvironment
checked: StreamingPreferences . captureSysKeysMode !== StreamingPreferences . CSK_OFF || ! SystemProperties . hasDesktopEnvironment
2021-02-27 22:47:38 +00:00
ToolTip.delay: 1000
ToolTip.timeout: 10000
ToolTip.visible: hovered
ToolTip.text: qsTr ( "This enables the capture of system-wide keyboard shortcuts like Alt+Tab that would normally be handled by the client OS while streaming." ) + "\n\n" +
qsTr ( "NOTE: Certain keyboard shortcuts like Ctrl+Alt+Del on Windows cannot be intercepted by any application, including Moonlight." )
2021-01-13 02:05:18 +00:00
}
2021-02-27 22:47:38 +00:00
AutoResizingComboBox {
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
if ( ! visible ) {
// Do nothing if the control won't even be visible
return
}
var saved_syskeysmode = StreamingPreferences . captureSysKeysMode
currentIndex = 0
for ( var i = 0 ; i < captureSysKeysModeListModel . count ; i ++ ) {
var el_syskeysmode = captureSysKeysModeListModel . get ( i ) . val ;
if ( saved_syskeysmode === el_syskeysmode ) {
currentIndex = i
break
}
}
activated ( currentIndex )
}
2021-03-13 20:59:32 +00:00
enabled: captureSysKeysCheck . checked && captureSysKeysCheck . enabled
2021-02-27 22:47:38 +00:00
textRole: "text"
model: ListModel {
id: captureSysKeysModeListModel
ListElement {
text: qsTr ( "in fullscreen" )
val: StreamingPreferences . CSK_FULLSCREEN
}
ListElement {
text: qsTr ( "always" )
val: StreamingPreferences . CSK_ALWAYS
}
}
function updatePref ( ) {
if ( ! enabled ) {
StreamingPreferences . captureSysKeysMode = StreamingPreferences . CSK_OFF
}
else {
StreamingPreferences . captureSysKeysMode = captureSysKeysModeListModel . get ( currentIndex ) . val
}
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated: {
updatePref ( )
}
// This handles transition of the checkbox state
onEnabledChanged: {
updatePref ( )
}
}
2021-01-13 02:05:18 +00:00
}
2020-05-01 03:55:15 +00:00
CheckBox {
id: absoluteTouchCheck
hoverEnabled: true
2020-05-10 18:12:37 +00:00
width: parent . width
2020-12-25 18:19:22 +00:00
text: qsTr ( "Use touchscreen as a virtual trackpad" )
2020-05-01 03:55:15 +00:00
font.pointSize: 12
checked: ! StreamingPreferences . absoluteTouchMode
onCheckedChanged: {
StreamingPreferences . absoluteTouchMode = ! checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2020-11-21 17:42:16 +00:00
ToolTip.text: qsTr ( "When checked, the touchscreen acts like a trackpad. When unchecked, the touchscreen will directly control the mouse pointer." )
2020-05-01 03:55:15 +00:00
}
2019-06-30 01:24:59 +00:00
CheckBox {
2020-12-25 18:19:22 +00:00
id: swapMouseButtonsCheck
2019-06-30 01:24:59 +00:00
hoverEnabled: true
2020-05-10 18:12:37 +00:00
width: parent . width
2020-12-25 18:19:22 +00:00
text: qsTr ( "Swap left and right mouse buttons" )
2019-06-30 01:24:59 +00:00
font.pointSize: 12
2020-12-25 18:19:22 +00:00
checked: StreamingPreferences . swapMouseButtons
2019-06-30 01:24:59 +00:00
onCheckedChanged: {
2020-12-25 18:19:22 +00:00
StreamingPreferences . swapMouseButtons = checked
}
}
2020-12-26 04:21:20 +00:00
CheckBox {
id: reverseScrollButtonsCheck
hoverEnabled: true
width: parent . width
text: qsTr ( "Reverse mouse scrolling direction" )
font.pointSize: 12
checked: StreamingPreferences . reverseScrollDirection
onCheckedChanged: {
StreamingPreferences . reverseScrollDirection = checked
}
}
2020-12-25 18:19:22 +00:00
}
}
GroupBox {
id: gamepadSettingsGroupBox
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
padding: 12
title: "<font color=\"skyblue\">" + qsTr ( "Gamepad Settings" ) + "</font>"
font.pointSize: 12
Column {
anchors.fill: parent
spacing: 5
2020-12-26 04:21:20 +00:00
CheckBox {
id: swapFaceButtonsCheck
width: parent . width
text: qsTr ( "Swap A/B and X/Y gamepad buttons" )
font.pointSize: 12
checked: StreamingPreferences . swapFaceButtons
onCheckedChanged: {
2024-04-14 18:01:30 +00:00
StreamingPreferences . swapFaceButtons = checked
2020-12-26 04:21:20 +00:00
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr ( "This switches gamepads into a Nintendo-style button layout" )
}
2020-12-25 18:19:22 +00:00
CheckBox {
id: singleControllerCheck
width: parent . width
2020-12-25 21:32:11 +00:00
text: qsTr ( "Force gamepad #1 always connected" )
2020-12-25 18:19:22 +00:00
font.pointSize: 12
checked: ! StreamingPreferences . multiController
onCheckedChanged: {
StreamingPreferences . multiController = ! checked
2019-06-30 01:24:59 +00:00
}
ToolTip.delay: 1000
2020-12-25 18:19:22 +00:00
ToolTip.timeout: 5000
2019-06-30 01:24:59 +00:00
ToolTip.visible: hovered
2020-12-27 21:34:49 +00:00
ToolTip.text: qsTr ( "Forces a single gamepad to always stay connected to the host, even if no gamepads are actually connected to this PC." ) + " " +
2020-12-25 18:19:22 +00:00
qsTr ( "Only enable this option when streaming a game that doesn't support gamepads being connected after startup." )
2019-06-30 01:24:59 +00:00
}
2020-08-23 14:05:00 +00:00
CheckBox {
2020-12-25 18:19:22 +00:00
id: gamepadMouseCheck
2020-08-23 14:05:00 +00:00
hoverEnabled: true
width: parent . width
2020-12-27 21:41:47 +00:00
text: qsTr ( "Enable mouse control with gamepads by holding the 'Start' button" )
font.pointSize: 12
2020-12-25 18:19:22 +00:00
checked: StreamingPreferences . gamepadMouse
2020-08-23 14:05:00 +00:00
onCheckedChanged: {
2020-12-25 18:19:22 +00:00
StreamingPreferences . gamepadMouse = checked
2020-08-23 14:05:00 +00:00
}
}
2020-12-25 21:32:11 +00:00
CheckBox {
id: backgroundGamepadCheck
width: parent . width
text: qsTr ( "Process gamepad input when Moonlight is in the background" )
font.pointSize: 12
2021-03-13 21:20:58 +00:00
visible: SystemProperties . hasDesktopEnvironment
2020-12-25 21:32:11 +00:00
checked: StreamingPreferences . backgroundGamepad
onCheckedChanged: {
StreamingPreferences . backgroundGamepad = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr ( "Allows Moonlight to capture gamepad inputs even if it's not the current window in focus" )
}
2018-07-08 18:12:22 +00:00
}
}
GroupBox {
id: hostSettingsGroupBox
2020-05-10 20:02:10 +00:00
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
2018-07-08 18:12:22 +00:00
padding: 12
2020-11-21 19:15:54 +00:00
title: "<font color=\"skyblue\">" + qsTr ( "Host Settings" ) + "</font>"
2018-07-08 18:12:22 +00:00
font.pointSize: 12
2018-11-16 00:41:02 +00:00
Column {
2018-07-08 18:12:22 +00:00
anchors.fill: parent
spacing: 5
CheckBox {
id: optimizeGameSettingsCheck
2020-05-10 18:12:37 +00:00
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Optimize game settings for streaming" )
2018-07-08 18:12:22 +00:00
font.pointSize: 12
2019-03-28 01:13:20 +00:00
checked: StreamingPreferences . gameOptimizations
2018-07-09 05:56:38 +00:00
onCheckedChanged: {
2019-03-28 01:13:20 +00:00
StreamingPreferences . gameOptimizations = checked
2018-07-09 05:56:38 +00:00
}
2018-07-08 18:12:22 +00:00
}
2020-06-24 05:17:40 +00:00
CheckBox {
id: quitAppAfter
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Quit app on host PC after ending stream" )
2020-06-24 05:17:40 +00:00
font.pointSize: 12
checked: StreamingPreferences . quitAppAfter
onCheckedChanged: {
StreamingPreferences . quitAppAfter = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
2020-11-21 17:42:16 +00:00
ToolTip.text: qsTr ( "This will close the app or game you are streaming when you end your stream. You will lose any unsaved progress!" )
2020-06-24 05:17:40 +00:00
}
2018-07-08 18:12:22 +00:00
}
}
GroupBox {
id: advancedSettingsGroupBox
2020-05-10 20:02:10 +00:00
width: ( parent . width - ( parent . leftPadding + parent . rightPadding ) )
2018-07-08 18:12:22 +00:00
padding: 12
2020-11-21 19:15:54 +00:00
title: "<font color=\"skyblue\">" + qsTr ( "Advanced Settings" ) + "</font>"
2018-07-08 18:12:22 +00:00
font.pointSize: 12
Column {
anchors.fill: parent
spacing: 5
2018-07-20 21:14:38 +00:00
Label {
width: parent . width
id: resVDSTitle
text: qsTr ( "Video decoder" )
font.pointSize: 12
wrapMode: Text . Wrap
2018-07-08 18:12:22 +00:00
}
2018-07-20 21:14:38 +00:00
2018-10-06 19:12:05 +00:00
AutoResizingComboBox {
2018-07-20 21:14:38 +00:00
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
2019-03-28 01:13:20 +00:00
var saved_vds = StreamingPreferences . videoDecoderSelection
2018-07-20 21:14:38 +00:00
currentIndex = 0
2018-09-08 06:16:13 +00:00
for ( var i = 0 ; i < decoderListModel . count ; i ++ ) {
2018-07-20 21:14:38 +00:00
var el_vds = decoderListModel . get ( i ) . val ;
2018-09-08 06:16:13 +00:00
if ( saved_vds === el_vds ) {
currentIndex = i
break
}
2018-07-20 21:14:38 +00:00
}
2018-09-08 06:16:13 +00:00
activated ( currentIndex )
2018-07-20 21:14:38 +00:00
}
id: decoderComboBox
textRole: "text"
2022-04-06 01:09:09 +00:00
enabled: ! enableHdr . checked
2018-07-20 21:14:38 +00:00
model: ListModel {
id: decoderListModel
ListElement {
2020-11-21 17:42:16 +00:00
text: qsTr ( "Automatic (Recommended)" )
2018-07-20 21:14:38 +00:00
val: StreamingPreferences . VDS_AUTO
}
ListElement {
2020-11-21 17:42:16 +00:00
text: qsTr ( "Force software decoding" )
2018-07-20 21:14:38 +00:00
val: StreamingPreferences . VDS_FORCE_SOFTWARE
}
ListElement {
2020-11-21 17:42:16 +00:00
text: qsTr ( "Force hardware decoding" )
2018-07-20 21:14:38 +00:00
val: StreamingPreferences . VDS_FORCE_HARDWARE
}
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
2022-04-06 01:09:09 +00:00
onActivated: {
if ( enabled ) {
StreamingPreferences . videoDecoderSelection = decoderListModel . get ( currentIndex ) . val
}
}
// This handles the state of the enableHdr checkbox changing
onEnabledChanged: {
if ( enabled ) {
StreamingPreferences . videoDecoderSelection = decoderListModel . get ( currentIndex ) . val
}
else {
StreamingPreferences . videoDecoderSelection = StreamingPreferences . VDS_AUTO
}
2018-07-20 21:14:38 +00:00
}
2022-04-06 01:09:09 +00:00
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered && ! enabled
ToolTip.text: qsTr ( "Enabling HDR overrides manual decoder selections." )
2018-07-20 21:14:38 +00:00
}
2018-07-20 21:22:22 +00:00
Label {
width: parent . width
id: resVCCTitle
text: qsTr ( "Video codec" )
font.pointSize: 12
wrapMode: Text . Wrap
}
2018-10-06 19:12:05 +00:00
AutoResizingComboBox {
2018-07-20 21:22:22 +00:00
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
2019-03-28 01:13:20 +00:00
var saved_vcc = StreamingPreferences . videoCodecConfig
2022-02-19 17:17:41 +00:00
// Default to Automatic (relevant if HDR is enabled,
// where we will match none of the codecs in the list)
2018-07-20 21:22:22 +00:00
currentIndex = 0
2022-02-19 17:17:41 +00:00
2018-07-20 21:22:22 +00:00
for ( var i = 0 ; i < codecListModel . count ; i ++ ) {
var el_vcc = codecListModel . get ( i ) . val ;
2018-09-08 06:16:13 +00:00
if ( saved_vcc === el_vcc ) {
currentIndex = i
break
}
2018-07-20 21:22:22 +00:00
}
2022-02-19 17:17:41 +00:00
2018-09-08 06:16:13 +00:00
activated ( currentIndex )
2018-07-20 21:22:22 +00:00
}
id: codecComboBox
textRole: "text"
model: ListModel {
id: codecListModel
ListElement {
2020-11-21 17:42:16 +00:00
text: qsTr ( "Automatic (Recommended)" )
2018-07-20 21:22:22 +00:00
val: StreamingPreferences . VCC_AUTO
}
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "H.264" )
2018-07-20 21:22:22 +00:00
val: StreamingPreferences . VCC_FORCE_H264
}
ListElement {
2020-11-21 19:15:54 +00:00
text: qsTr ( "HEVC (H.265)" )
2018-07-20 21:22:22 +00:00
val: StreamingPreferences . VCC_FORCE_HEVC
}
2023-07-16 20:38:58 +00:00
ListElement {
text: qsTr ( "AV1 (Experimental)" )
val: StreamingPreferences . VCC_FORCE_AV1
}
2018-07-20 21:22:22 +00:00
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
2022-02-19 17:17:41 +00:00
if ( enabled ) {
StreamingPreferences . videoCodecConfig = codecListModel . get ( currentIndex ) . val
}
2018-07-20 21:22:22 +00:00
}
2022-02-19 17:17:41 +00:00
}
CheckBox {
id: enableHdr
width: parent . width
text: qsTr ( "Enable HDR (Experimental)" )
font.pointSize: 12
2023-07-16 20:38:58 +00:00
2022-02-19 17:17:41 +00:00
enabled: SystemProperties . supportsHdr
2023-07-16 20:38:58 +00:00
checked: enabled && StreamingPreferences . enableHdr
onCheckedChanged: {
StreamingPreferences . enableHdr = checked
}
2022-02-19 17:17:41 +00:00
// Updating StreamingPreferences.videoCodecConfig is handled above
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: enabled ?
qsTr ( "The stream will be HDR-capable, but some games may require an HDR monitor on your host PC to enable HDR mode." )
:
qsTr ( "HDR streaming is not supported on this PC." )
2018-07-20 21:22:22 +00:00
}
2024-05-15 13:22:42 +00:00
CheckBox {
id: enableYUV444
width: parent . width
text: qsTr ( "Enable YUV 4:4:4 (Experimental)" )
font.pointSize: 12
checked: StreamingPreferences . enableYUV444
onCheckedChanged: {
StreamingPreferences . enableYUV444 = checked
StreamingPreferences . bitrateKbps = StreamingPreferences . getDefaultBitrate ( StreamingPreferences . width ,
StreamingPreferences . height ,
StreamingPreferences . fps ,
StreamingPreferences . enableYUV444 ) ;
slider . value = StreamingPreferences . bitrateKbps
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: enabled ?
2024-07-26 06:24:27 +00:00
qsTr ( "Good for streaming desktop and text-heavy games, but not recommended for fast-paced games." )
2024-05-15 13:22:42 +00:00
:
qsTr ( "YUV 4:4:4 is not supported on this PC." )
}
2018-09-09 19:33:19 +00:00
CheckBox {
id: enableMdns
2020-05-10 18:12:37 +00:00
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Automatically find PCs on the local network (Recommended)" )
2018-09-09 19:33:19 +00:00
font.pointSize: 12
2019-03-28 01:13:20 +00:00
checked: StreamingPreferences . enableMdns
2018-09-09 19:33:19 +00:00
onCheckedChanged: {
2019-03-25 02:15:35 +00:00
// This is called on init, so only do the work if we've
// actually changed the value.
2019-03-28 01:13:20 +00:00
if ( StreamingPreferences . enableMdns != checked ) {
StreamingPreferences . enableMdns = checked
2019-03-25 02:15:35 +00:00
// Restart polling so the mDNS change takes effect
if ( window . pollingActive ) {
ComputerManager . stopPollingAsync ( )
ComputerManager . startPolling ( )
}
2018-09-09 19:33:19 +00:00
}
}
}
2020-08-09 01:29:36 +00:00
CheckBox {
id: detectNetworkBlocking
width: parent . width
2020-11-21 17:42:16 +00:00
text: qsTr ( "Automatically detect blocked connections (Recommended)" )
2020-08-09 01:29:36 +00:00
font.pointSize: 12
checked: StreamingPreferences . detectNetworkBlocking
onCheckedChanged: {
2024-04-14 18:01:30 +00:00
StreamingPreferences . detectNetworkBlocking = checked
2020-08-09 01:29:36 +00:00
}
}
2024-05-01 02:37:52 +00:00
CheckBox {
id: showPerformanceOverlay
width: parent . width
text: qsTr ( "Show performance stats while streaming" )
font.pointSize: 12
checked: StreamingPreferences . showPerformanceOverlay
onCheckedChanged: {
2024-05-01 02:42:38 +00:00
StreamingPreferences . showPerformanceOverlay = checked
2024-05-01 02:37:52 +00:00
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr ( "Display real-time stream performance information while streaming." ) + "\n\n" +
qsTr ( "You can toggle it at any time while streaming using Ctrl+Alt+Shift+S or Select+L1+R1+X." ) + "\n\n" +
qsTr ( "The performance overlay is not supported on Steam Link or Raspberry Pi." )
}
2018-07-08 17:19:08 +00:00
}
}
}
2018-07-08 05:15:02 +00:00
}