diff --git a/app/assets/css/launcher.css b/app/assets/css/launcher.css index 2037221..20ffd8e 100644 --- a/app/assets/css/launcher.css +++ b/app/assets/css/launcher.css @@ -1337,6 +1337,21 @@ input:checked + .toggleSwitchSlider:before { * Settings View (Java Tab) * * */ +/* Style links on the Java tab. */ +#settingsTabJava a { + color: rgba(202, 202, 202, 0.75); + transition: 0.25s ease; + outline: none; +} +#settingsTabJava a:hover, +#settingsTabJava a:focus { + color: rgba(255, 255, 255, 0.75); +} +#settingsTabJava a:active { + color: rgba(165, 165, 165, 0.75); +} + +/* Main container for memory management. */ #settingsMemoryContainer { width: 75%; display: flex; @@ -1345,12 +1360,14 @@ input:checked + .toggleSwitchSlider:before { margin-bottom: 20px; } +/* Memory management title. */ #settingsMemoryTitle { margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px solid rgba(255, 255, 255, 0.5); } +/* Memory management content. */ #settingsMemoryContent { display: flex; justify-content: space-between; @@ -1365,33 +1382,31 @@ input:checked + .toggleSwitchSlider:before { margin-right: 10%; } +/* Header for memory sliders. */ .settingsMemoryHeader { font-size: 14px; } +/* Wrapper container for a memory slider and label. */ .settingsMemoryActionContainer { display: flex; align-items: center; justify-content: space-between; } +/* Label which displays a memory slider's value. */ .settingsMemoryLabel { font-size: 14px; margin-right: 3%; } -#settingsMemoryDesc { - font-size: 10px; - margin: 20px 0px; - color: lightgrey; - font-weight: bold; -} - +/* Range sliders for min and max memory settings. */ #settingsMaxRAMRange, #settingsMinRAMRange { width: 85%; } +/* Memory status elements. */ #settingsMemoryStatus { display: flex; flex-direction: column; @@ -1414,6 +1429,15 @@ input:checked + .toggleSwitchSlider:before { font-size: 16px; } +/* Description for memory management. */ +#settingsMemoryDesc { + font-size: 10px; + margin: 20px 0px; + color: lightgrey; + font-weight: bold; +} + +/* Main container for the Java executable setting. */ #settingsJavaExecContainer { display: flex; flex-direction: column; @@ -1422,40 +1446,48 @@ input:checked + .toggleSwitchSlider:before { width: 75%; } +/* Java executable setting title. */ #settingsJavaExecTitle { margin-bottom: 10px; } +/* Status text which displays details on the selected executable. */ #settingsJavaExecDetails { font-weight: bold; color: grey; font-size: 12px; } +/* Wrapper container for the actionable elements. */ #settingsJavaExecActions { display: flex; + width: 90%; } -#settingsJavaExecIcon { +/* Java icon settings. */ +.settingsJavaIcon { display: flex; align-items: center; background: rgba(126, 126, 126, 0.57); border-radius: 3px 0px 0px 3px; padding: 5px; + transition: 0.25s ease; } -#settingsJavaExecSVG { +.settingsJavaIconSVG { width: 20px; height: 20px; fill: white; } +/* Disabled text field which stores the selected executable path. */ #settingsJavaExecVal { border-radius: 0px !important; - width: 70%; + width: 100%; padding: 5px 10px; font-size: 12px; } +/* File input for executable selection. */ #settingsJavaExecSel { width: 0px; height: 0px; @@ -1465,6 +1497,7 @@ input:checked + .toggleSwitchSlider:before { display: none; } +/* Wrapper label to add a custom style to the file input. */ #settingsJavaExecLabel { border-left: 0px; border-radius: 0px 3px 3px 0px; @@ -1475,6 +1508,7 @@ input:checked + .toggleSwitchSlider:before { align-items: center; background: rgba(126, 126, 126, 0.57); transition: 0.25s ease; + white-space: nowrap; } #settingsJavaExecLabel:hover, #settingsJavaExecLabel:focus, @@ -1482,6 +1516,7 @@ input:checked + .toggleSwitchSlider:before { text-shadow: 0px 0px 20px white; } +/* Description for the Java executable setting. */ #settingsJavaExecDesc { font-size: 10px; margin: 20px 0px; @@ -1493,6 +1528,42 @@ input:checked + .toggleSwitchSlider:before { font-family: 'Avenir Medium'; } +/* Main container for the JVM options setting. */ +#settingsJVMOptsContainer { + width: 75%; +} + +/* JVM options title. */ +#settingsJVMOptsTitle { + margin-bottom: 10px; +} + +/* Wrapper container for the actionable elements. */ +#settingsJVMOptsContent { + display: flex; + width: 90%; +} + +/* Text field to input the JVM options. */ +#settingsJVMOptsVal { + border-radius: 0px 3px 3px 0px !important; + width: 100%; + padding: 5px 10px; + font-size: 12px; +} +#settingsJVMOptsContent:focus-within > .settingsJavaIcon { + background: rgba(126, 126, 126, 0.87); +} + +/* Description for the JVM options setting. */ +#settingsJVMOptsDesc { + font-size: 10px; + margin: 20px 0px; + color: lightgrey; + font-weight: bold; + width: 89%; +} + /******************************************************************************* * * * Landing View (Structural Styles) * diff --git a/app/assets/js/scripts/settings.js b/app/assets/js/scripts/settings.js index e3958fa..0465585 100644 --- a/app/assets/js/scripts/settings.js +++ b/app/assets/js/scripts/settings.js @@ -17,9 +17,10 @@ const settingsMaxRAMLabel = document.getElementById('settingsMaxRAMLabel') const settingsMinRAMLabel = document.getElementById('settingsMinRAMLabel') const settingsMemoryTotal = document.getElementById('settingsMemoryTotal') const settingsMemoryAvail = document.getElementById('settingsMemoryAvail') -const settingsJavaExecDetails = document.getElementById('settingsJavaExecDetails') -const settingsJavaExecVal = document.getElementById('settingsJavaExecVal') -const settingsJavaExecSel = document.getElementById('settingsJavaExecSel') +const settingsJavaExecDetails = document.getElementById('settingsJavaExecDetails') +const settingsJavaExecVal = document.getElementById('settingsJavaExecVal') +const settingsJavaExecSel = document.getElementById('settingsJavaExecSel') +const settingsJVMOptsVal = document.getElementById('settingsJVMOptsVal') const settingsState = { invalid: new Set() @@ -76,12 +77,15 @@ function initSettingsValues(){ if(typeof gFn === 'function'){ if(v.tagName === 'INPUT'){ if(v.type === 'number' || v.type === 'text'){ - v.value = gFn() - // Special Conditions const cVal = v.getAttribute('cValue') if(cVal === 'JavaExecutable'){ populateJavaExecDetails(v.value) + v.value = gFn() + } else if(cVal === 'JVMOptions'){ + v.value = gFn().join(' ') + } else { + v.value = gFn() } } else if(v.type === 'checkbox'){ v.checked = gFn() @@ -119,7 +123,13 @@ function saveSettingsValues(){ if(typeof sFn === 'function'){ if(v.tagName === 'INPUT'){ if(v.type === 'number' || v.type === 'text'){ - sFn(v.value) + // Special Conditions + const cVal = v.getAttribute('cValue') + if(cVal === 'JVMOptions'){ + sFn(v.value.split(' ')) + } else { + sFn(v.value) + } } else if(v.type === 'checkbox'){ sFn(v.checked) // Special Conditions @@ -348,6 +358,9 @@ function populateAuthAccounts(){ settingsCurrentAccounts.innerHTML = authAccountStr } +/** + * Prepare the accounts tab for display. + */ function prepareAccountsTab() { populateAuthAccounts() bindAuthAccountSelect() @@ -376,16 +389,29 @@ settingsGameHeight.addEventListener('keydown', (e) => { * Java Tab */ -settingsMaxRAMRange.setAttribute('max', ConfigManager.getAbsoluteMaxRAM()) -settingsMaxRAMRange.setAttribute('min', ConfigManager.getAbsoluteMinRAM()) -settingsMinRAMRange.setAttribute('max', ConfigManager.getAbsoluteMaxRAM()) -settingsMinRAMRange.setAttribute('min', ConfigManager.getAbsoluteMinRAM()) +// Store maximum memory values. +const SETTINGS_MAX_MEMORY = ConfigManager.getAbsoluteMaxRAM() +const SETTINGS_MIN_MEMORY = ConfigManager.getAbsoluteMinRAM() +// Set the max and min values for the ranged sliders. +settingsMaxRAMRange.setAttribute('max', SETTINGS_MAX_MEMORY) +settingsMaxRAMRange.setAttribute('min', SETTINGS_MIN_MEMORY) +settingsMinRAMRange.setAttribute('max', SETTINGS_MAX_MEMORY) +settingsMinRAMRange.setAttribute('min', SETTINGS_MIN_MEMORY ) + +// Bind on change event for min memory container. settingsMinRAMRange.onchange = (e) => { + + // Current range values const sMaxV = Number(settingsMaxRAMRange.getAttribute('value')) const sMinV = Number(settingsMinRAMRange.getAttribute('value')) + + // Get reference to range bar. const bar = e.target.getElementsByClassName('rangeSliderBar')[0] + // Calculate effective total memory. const max = (os.totalmem()-1000000000)/1000000000 + + // Change range bar color based on the selected value. if(sMinV >= max/2){ bar.style.background = '#e86060' } else if(sMinV >= max/4) { @@ -393,20 +419,31 @@ settingsMinRAMRange.onchange = (e) => { } else { bar.style.background = null } + + // Increase maximum memory if the minimum exceeds its value. if(sMaxV < sMinV){ const sliderMeta = calculateRangeSliderMeta(settingsMaxRAMRange) updateRangedSlider(settingsMaxRAMRange, sMinV, ((sMinV-sliderMeta.min)/sliderMeta.step)*sliderMeta.inc) settingsMaxRAMLabel.innerHTML = sMinV.toFixed(1) + 'G' } + + // Update label settingsMinRAMLabel.innerHTML = sMinV.toFixed(1) + 'G' } +// Bind on change event for max memory container. settingsMaxRAMRange.onchange = (e) => { + // Current range values const sMaxV = Number(settingsMaxRAMRange.getAttribute('value')) const sMinV = Number(settingsMinRAMRange.getAttribute('value')) + + // Get reference to range bar. const bar = e.target.getElementsByClassName('rangeSliderBar')[0] + // Calculate effective total memory. const max = (os.totalmem()-1000000000)/1000000000 + + // Change range bar color based on the selected value. if(sMaxV >= max/2){ bar.style.background = '#e86060' } else if(sMaxV >= max/4) { @@ -414,6 +451,8 @@ settingsMaxRAMRange.onchange = (e) => { } else { bar.style.background = null } + + // Decrease the minimum memory if the maximum value is less. if(sMaxV < sMinV){ const sliderMeta = calculateRangeSliderMeta(settingsMaxRAMRange) updateRangedSlider(settingsMinRAMRange, sMaxV, @@ -423,21 +462,12 @@ settingsMaxRAMRange.onchange = (e) => { settingsMaxRAMLabel.innerHTML = sMaxV.toFixed(1) + 'G' } -settingsJavaExecSel.onchange = (e) => { - settingsJavaExecVal.value = settingsJavaExecSel.files[0].path - populateJavaExecDetails(settingsJavaExecVal.value) -} - -function populateJavaExecDetails(execPath){ - AssetGuard._validateJavaBinary(execPath).then(v => { - if(v.valid){ - settingsJavaExecDetails.innerHTML = `Selected: Java ${v.version.major} Update ${v.version.update} (x${v.arch})` - } else { - settingsJavaExecDetails.innerHTML = 'Invalid Selection' - } - }) -} - +/** + * Calculate common values for a ranged slider. + * + * @param {Element} v The range slider to calculate against. + * @returns {Object} An object with meta values for the provided ranged slider. + */ function calculateRangeSliderMeta(v){ const val = { max: Number(v.getAttribute('max')), @@ -449,30 +479,46 @@ function calculateRangeSliderMeta(v){ return val } +/** + * Binds functionality to the ranged sliders. They're more than + * just divs now :'). + */ function bindRangeSlider(){ Array.from(document.getElementsByClassName('rangeSlider')).map((v) => { + + // Reference the track (thumb). const track = v.getElementsByClassName('rangeSliderTrack')[0] + // Set the initial slider value. const value = v.getAttribute('value') const sliderMeta = calculateRangeSliderMeta(v) - updateRangedSlider(v, value, - ((value-sliderMeta.min)/sliderMeta.step)*sliderMeta.inc) + updateRangedSlider(v, value, ((value-sliderMeta.min)/sliderMeta.step)*sliderMeta.inc) + + // The magic happens when we click on the track. track.onmousedown = (e) => { + // Stop moving the track on mouse up. document.onmouseup = (e) => { document.onmousemove = null document.onmouseup = null } + // Move slider according to the mouse position. document.onmousemove = (e) => { + + // Distance from the beginning of the bar in pixels. const diff = e.pageX - v.offsetLeft - track.offsetWidth/2 + // Don't move the track off the bar. if(diff >= 0 && diff <= v.offsetWidth-track.offsetWidth/2){ + // Convert the difference to a percentage. const perc = (diff/v.offsetWidth)*100 + // Calculate the percentage of the closest notch. const notch = Number(perc/sliderMeta.inc).toFixed(0)*sliderMeta.inc + // If we're close to that notch, stick to it. if(Math.abs(perc-notch) < sliderMeta.inc/2){ updateRangedSlider(v, sliderMeta.min+(sliderMeta.step*(notch/sliderMeta.inc)), notch) } @@ -482,18 +528,29 @@ function bindRangeSlider(){ }) } +/** + * Update a ranged slider's value and position. + * + * @param {Element} element The ranged slider to update. + * @param {string | number} value The new value for the ranged slider. + * @param {number} notch The notch that the slider should now be at. + */ function updateRangedSlider(element, value, notch){ const oldVal = element.getAttribute('value') const bar = element.getElementsByClassName('rangeSliderBar')[0] const track = element.getElementsByClassName('rangeSliderTrack')[0] + element.setAttribute('value', value) + const event = new MouseEvent('change', { target: element, type: 'change', bubbles: false, cancelable: true }) + let cancelled = !element.dispatchEvent(event) + if(!cancelled){ track.style.left = notch + '%' bar.style.width = notch + '%' @@ -502,14 +559,42 @@ function updateRangedSlider(element, value, notch){ } } -function bindMemoryStatus(){ +/** + * Display the total and available RAM. + */ +function populateMemoryStatus(){ settingsMemoryTotal.innerHTML = Number((os.totalmem()-1000000000)/1000000000).toFixed(1) + 'G' settingsMemoryAvail.innerHTML = Number(os.freemem()/1000000000).toFixed(1) + 'G' } +// Bind the executable file input to the display text input. +settingsJavaExecSel.onchange = (e) => { + settingsJavaExecVal.value = settingsJavaExecSel.files[0].path + populateJavaExecDetails(settingsJavaExecVal.value) +} + +/** + * Validate the provided executable path and display the data on + * the UI. + * + * @param {string} execPath The executable path to populate against. + */ +function populateJavaExecDetails(execPath){ + AssetGuard._validateJavaBinary(execPath).then(v => { + if(v.valid){ + settingsJavaExecDetails.innerHTML = `Selected: Java ${v.version.major} Update ${v.version.update} (x${v.arch})` + } else { + settingsJavaExecDetails.innerHTML = 'Invalid Selection' + } + }) +} + +/** + * Prepare the Java tab for display. + */ function prepareJavaTab(){ bindRangeSlider() - bindMemoryStatus() + populateMemoryStatus() } diff --git a/app/settings.ejs b/app/settings.ejs index e214ddf..c5e9d98 100644 --- a/app/settings.ejs +++ b/app/settings.ejs @@ -140,8 +140,8 @@
Selected: Java 8 Update 172 (x64)
-
- +
+ @@ -161,6 +161,27 @@
The Java executable is validated before game launch. Requires Java 8 x64.
The path should end with bin<%= process.platform === 'win32' ? '\\javaw.exe' : '/java' %>.
+
+
Additional JVM Options
+
+
+ + + + + + + + + + + + +
+ +
+
Options to be provided to the JVM at runtime. -Xms and -Xmx should not be included.
Available Options for Java 8.
+