diff --git a/resources/report/css/style.css b/resources/report/css/style.css index 8271f0b8..d922158a 100644 --- a/resources/report/css/style.css +++ b/resources/report/css/style.css @@ -159,7 +159,7 @@ nav a { padding: 0.3em 0.6em; width: 100%; } -#main { +main { overflow: auto; flex-shrink: 10; flex-grow: 1; @@ -388,9 +388,6 @@ body.editMode header #headerOverlay { top: 0; left: 0; } -#conflictsPlugin { - margin-left: 2.5em; -} [draggable] { -webkit-user-select: none; } @@ -398,6 +395,12 @@ body.editMode header #headerOverlay { background: #69aaff; color: #f8f8f8; } +plugin-card.highlight { + box-shadow: inset 0 0 10px 3px #69aaff; + background: white; + color: inherit; + outline: 3px solid #69aaff; +} input:invalid { background-color: pink; color: white; diff --git a/resources/report/js/custom.js b/resources/report/js/custom.js index 9c9938f2..54cd0b76 100644 --- a/resources/report/js/custom.js +++ b/resources/report/js/custom.js @@ -26,56 +26,87 @@ /* Create a element type. */ var pluginMenuProto = Object.create(HTMLElement.prototype, { - onMenuItemClick: { + getPluginCard: { + value: function() { + return document.getElementById(this.getAttribute('data-for')); + } + }, + + onShowOnlyConflicts: { value: function(evt) { + /* Depending on what was clicked, this function may be run before + the checkbox state has updated. Handle both cases. */ + var activateFilter; + if (evt.currentTarget == evt.target) { + /* Clicked on the label, checkbox state isn't updated yet. */ + activateFilter = !evt.target.firstElementChild.checked; + } else { + /* Clicked on the checkbox. */ + activateFilter = evt.target.checked; + } + if (activateFilter) { + document.body.setAttribute('data-conflicts', evt.currentTarget.parentNode.host.getPluginCard().getName()); + evt.currentTarget.parentNode.host.getPluginCard().classList.toggle('highlight', true); + } else { + evt.currentTarget.parentNode.host.getPluginCard().classList.toggle('highlight', false); + document.body.removeAttribute('data-conflicts'); + } + togglePlugins(evt); + } + }, - var pluginID = evt.target.parentNode.host.getAttribute('data-for'); - var pluginCard = document.getElementById(pluginID); + onEditMetadata: { + value: function(evt) { + evt.target.parentNode.host.getPluginCard().showEditor(); + } + }, - if (evt.target.id == 'editMetadata') { - /* Show editing controls. */ - pluginCard.showEditor(); + onCopyMetadata: { + value: function(evt) { + var request = JSON.stringify({ + name: 'copyMetadata', + args: [ + evt.target.parentNode.host.getPluginCard().getName() + ] + }); - } else if (evt.target.id == 'copyMetadata') { - var request = JSON.stringify({ - name: 'copyMetadata', - args: [ - pluginCard.getElementsByTagName('h1')[0].textContent - ] - }); + loot.query(request).catch(processCefError); + } + }, - loot.query(request).catch(processCefError); - } else if (evt.target.id == 'clearMetadata') { - showMessageDialog('Clear Plugin Metadata', 'Are you sure you want to clear all existing user-added metadata from "' + pluginCard.getElementsByTagName('h1')[0].textContent + '"?', function(result){ - if (result) { - var request = JSON.stringify({ - name: 'clearPluginMetadata', - args: [ - pluginCard.getElementsByTagName('h1')[0].textContent - ] - }); + onClearMetadata: { + value: function(evt) { + var pluginCard = evt.target.parentNode.host.getPluginCard(); - loot.query(request).then(JSON.parse).then(function(result){ - if (result) { - /* Need to empty the UI-side user metadata. */ - for (var i = 0; i < loot.game.plugins.length; ++i) { - if (loot.game.plugins[i].id == pluginID) { - loot.game.plugins[i].userlist = undefined; + showMessageDialog('Clear Plugin Metadata', 'Are you sure you want to clear all existing user-added metadata from "' + pluginCard.getName() + '"?', function(result){ + if (result) { + var request = JSON.stringify({ + name: 'clearPluginMetadata', + args: [ + pluginCard.getName() + ] + }); - loot.game.plugins[i].modPriority = result.modPriority; - loot.game.plugins[i].isGlobalPriority = result.isGlobalPriority; - loot.game.plugins[i].messages = result.messages; - loot.game.plugins[i].tags = result.tags; - loot.game.plugins[i].isDirty = result.isDirty; + loot.query(request).then(JSON.parse).then(function(result){ + if (result) { + /* Need to empty the UI-side user metadata. */ + for (var i = 0; i < loot.game.plugins.length; ++i) { + if (loot.game.plugins[i].id == pluginCard.id) { + loot.game.plugins[i].userlist = undefined; - break; - } + loot.game.plugins[i].modPriority = result.modPriority; + loot.game.plugins[i].isGlobalPriority = result.isGlobalPriority; + loot.game.plugins[i].messages = result.messages; + loot.game.plugins[i].tags = result.tags; + loot.game.plugins[i].isDirty = result.isDirty; + + break; } } - }).catch(processCefError); - } - }); - } + } + }).catch(processCefError); + } + }); } }, @@ -92,19 +123,40 @@ var pluginMenuProto = Object.create(HTMLElement.prototype, { attachedCallback: { value: function() { /* Add event listeners for the menu items. */ - this.shadowRoot.getElementById('editMetadata').addEventListener('click', this.onMenuItemClick, false); - this.shadowRoot.getElementById('copyMetadata').addEventListener('click', this.onMenuItemClick, false); - this.shadowRoot.getElementById('clearMetadata').addEventListener('click', this.onMenuItemClick, false); + var conflictsPlugin = document.body.getAttribute('data-conflicts'); + if (conflictsPlugin && conflictsPlugin != this.getPluginCard().getName()) { + /* The conflict filter is currently active for another plugin. + Prevent the filter being activated for this one. */ + this.shadowRoot.getElementById('showOnlyConflicts').disabled = true; + this.shadowRoot.getElementById('showOnlyConflicts').parentElement.classList.toggle('disabled', true); + } else { + /* The conflicts filter is either inactive or active for this + plugin. Allow it to be activated or deactivated. For some + reason clicking on the label is processed slower than + the checkbox state, and in the time difference the menu + gets closed, so that the conflicts filter never gets applied. + To get around this, listen for a click on the label rather + than for checkbox state change. */ + if (conflictsPlugin == this.getPluginCard().getName()) { + this.shadowRoot.getElementById('showOnlyConflicts').checked = true; + } + this.shadowRoot.getElementById('showOnlyConflicts').parentElement.addEventListener('click', this.onShowOnlyConflicts, false); + } + this.shadowRoot.getElementById('editMetadata').addEventListener('click', this.onEditMetadata, false); + this.shadowRoot.getElementById('copyMetadata').addEventListener('click', this.onCopyMetadata, false); + this.shadowRoot.getElementById('clearMetadata').addEventListener('click', this.onClearMetadata, false); } }, detachedCallback: { value: function() { - /* Remove event listeners for the menu items. */ - this.shadowRoot.getElementById('editMetadata').removeEventListener('click', this.onMenuItemClick, false); - this.shadowRoot.getElementById('copyMetadata').removeEventListener('click', this.onMenuItemClick, false); - this.shadowRoot.getElementById('clearMetadata').removeEventListener('click', this.onMenuItemClick, false); + /* Nothing happens if we try to remove a listener that doesn't exist, + so don't bother checking first. */ + this.shadowRoot.getElementById('showOnlyConflicts').parentElement.removeEventListener('click', this.onShowOnlyConflicts, false); + this.shadowRoot.getElementById('editMetadata').removeEventListener('click', this.onEditMetadata, false); + this.shadowRoot.getElementById('copyMetadata').removeEventListener('click', this.onCopyMetadata, false); + this.shadowRoot.getElementById('clearMetadata').removeEventListener('click', this.onClearMetadata, false); } } @@ -116,6 +168,12 @@ var PluginMenu = document.registerElement('plugin-menu', {prototype: pluginMenuP /* Create a element type. */ var pluginCardProto = Object.create(HTMLElement.prototype, { + getName: { + value: function() { + return this.getElementsByTagName('h1')[0].textContent; + } + }, + showEditorTable: { value: function(evt) { var tableId = evt.target.getAttribute('data-for'); @@ -427,8 +485,6 @@ var pluginCardProto = Object.create(HTMLElement.prototype, { } } - - break; } } @@ -448,7 +504,7 @@ var pluginCardProto = Object.create(HTMLElement.prototype, { /* Set up drag 'n' drop event handlers. */ elements = document.getElementById('pluginsNav').children; for (var i = 0; i < elements.length; ++i) { - elements[i].setAttribute('draggable', true); + elements[i].draggable = true; elements[i].addEventListener('dragstart', handlePluginDragStart, false); } elements = this.shadowRoot.getElementsByTagName('table'); @@ -487,7 +543,7 @@ var pluginCardProto = Object.create(HTMLElement.prototype, { menu.setAttribute('data-for', card.id); - var main = document.getElementById('main'); + var main = document.getElementsByTagName('main')[0]; main.appendChild(menu); /* Set page position of menu. */ @@ -774,12 +830,12 @@ var EditableTableProto = Object.create(HTMLTableElement.prototype, { if (classMask) { for (var j = 0; j < classMask.length; ++j) { if (inputs[i].classList.contains(classMask[j])) { - inputs[i].setAttribute('readonly', readOnly); + inputs[i].readOnly = readOnly; break; } } } else { - inputs[i].setAttribute('readonly', readOnly); + inputs[i].readOnly = readOnly; } } @@ -788,12 +844,12 @@ var EditableTableProto = Object.create(HTMLTableElement.prototype, { if (classMask) { for (var j = 0; j < classMask.length; ++j) { if (selects[i].classList.contains(classMask[j])) { - selects[i].setAttribute('disabled', readOnly); + selects[i].disabled = readOnly; break; } } } else { - selects[i].setAttribute('disabled', readOnly); + selects[i].disabled = readOnly; } } } diff --git a/resources/report/js/plugin.js b/resources/report/js/plugin.js index f8f9b8ac..740e702d 100644 --- a/resources/report/js/plugin.js +++ b/resources/report/js/plugin.js @@ -157,7 +157,7 @@ function Plugin(obj) { /* Fill in messages. */ this.updateCardMessages(); - document.getElementById('main').appendChild(card); + document.getElementsByTagName('main')[0].appendChild(card); } Plugin.prototype.createListItem = function() { diff --git a/resources/report/js/script.js b/resources/report/js/script.js index bf3e95ce..4d1d837c 100644 --- a/resources/report/js/script.js +++ b/resources/report/js/script.js @@ -72,11 +72,8 @@ function applySavedFilters() { if (loot.settings.filters) { for (var key in loot.settings.filters) { var elem = document.getElementById(key); - if (elem.type == 'checkbox') { - //elem.checked = true; + if (elem) { elem.dispatchEvent(new MouseEvent('click')); - } else { - elem.value = loot.settings.filters[key]; } } } @@ -111,44 +108,45 @@ function toggleDisplayCSS(evt) { } function getConflictingPluginsFromFilter() { - if (document.getElementById('showOnlyConflicts').checked) { - var conflictsPlugin = document.getElementById('conflictsPlugin'); - if (conflictsPlugin.value.length != 0) { + var conflictsPlugin = document.body.getAttribute('data-conflicts'); + if (conflictsPlugin) { + /* Now get conflicts for the plugin. */ + var request = JSON.stringify({ + name: 'getConflictingPlugins', + args: [ + conflictsPlugin + ] + }); - var request = JSON.stringify({ - name: 'getConflictingPlugins', - args: [ - conflictsPlugin.value - ] - }); - - return loot.query(request).then(JSON.parse).then(function(result){ - if (result) { - for (var key in result.crcs) { - for (var i = 0; i < loot.game.plugins.length; ++i) { - if (loot.game.plugins[i].name == key) { - loot.game.plugins[i].crc = result.crcs[key]; - break; - } + return loot.query(request).then(JSON.parse).then(function(result){ + if (result) { + for (var key in result.crcs) { + for (var i = 0; i < loot.game.plugins.length; ++i) { + if (loot.game.plugins[i].name == key) { + loot.game.plugins[i].crc = result.crcs[key]; + break; } } - if (result.conflicts) { - return result.conflicts; - } } - return []; - }).catch(processCefError); - } + if (result.conflicts) { + return result.conflicts; + } else { + /* No conflicts. Filter everything but the plugin itself. */ + return [ conflictsPlugin ]; + } + } + return []; + }).catch(processCefError); } return Promise.resolve([]); } function togglePlugins(evt) { - var sections = document.getElementById('main').children; + var cards = document.getElementsByTagName('main')[0].getElementsByTagName('plugin-card'); var entries = document.getElementById('pluginsNav').children; var hiddenPluginNo = 0; var hiddenMessageNo = 0; - if (sections.length - 2 != entries.length) { + if (cards.length != entries.length) { throw Error("Error: Number of plugins in sidebar doesn't match number of plugins in main area!"); } /* The conflict filter, if enabled, executes C++ code, so needs to be @@ -156,16 +154,15 @@ function togglePlugins(evt) { it is completed. */ getConflictingPluginsFromFilter().then(function(conflicts) { - /* Start at 3rd section to skip summary and general messages. */ - for (var i = 2; i < sections.length; ++i) { + for (var i = 0; i < cards.length; ++i) { var isConflictingPlugin = false; var isMessageless = true; var hasInactivePluginMessages = false; - var messages = sections[i].getElementsByTagName('ul')[0].getElementsByTagName('li'); - if (sections[i].getAttribute('data-active') == 'false') { + var messages = cards[i].getElementsByTagName('ul')[0].getElementsByTagName('li'); + if (cards[i].getAttribute('data-active') == 'false') { hasInactivePluginMessages = true; } - if (conflicts.indexOf(sections[i].getElementsByTagName('h1')[0].textContent) != -1) { + if (conflicts.indexOf(cards[i].getName()) != -1) { isConflictingPlugin = true; } for (var j = 0; j < messages.length; ++j) { @@ -197,11 +194,11 @@ function togglePlugins(evt) { } if ((document.getElementById('hideMessagelessPlugins').checked && isMessageless) || conflicts.length > 0 && !isConflictingPlugin) { - hideElement(sections[i]); + hideElement(cards[i]); hideElement(entries[i - 2]); ++hiddenPluginNo; } else { - showElement(sections[i]); + showElement(cards[i]); showElement(entries[i - 2]); } } @@ -303,17 +300,19 @@ function changeGame(evt) { var elements = document.getElementById('filters').getElementsByTagName('input'); var activeFilters = []; for (var i = 0; i < elements.length; ++i) { - if (elements[i].type == 'checkbox' && elements[i].id != 'showOnlyConflicts' - && elements[i].checked) { + if (elements[i].type == 'checkbox' && elements[i].checked) { activeFilters.push(elements[i]); elements[i].click(); } } - if (document.getElementById('showOnlyConflicts').checked) { - document.getElementById('showOnlyConflicts').click(); + /* Need to do something slightly different for the conflicts filter. + Edit its state and run the filter function manually. */ + if (document.body.hasAttribute('data-conflicts')) { + document.body.removeAttribute('data-conflicts'); + /* Don't need to supply an event arg because togglePlugins doesn't + actually use it. */ + togglePlugins(); } - document.getElementById('conflictsPlugin').value = ''; - /* Clear the UI of all existing game-specific data. Also clear the card and li variables for each plugin object. */ @@ -418,7 +417,7 @@ function updateMasterlist(evt) { function sortUIElements(pluginNames) { /* pluginNames is an array of plugin names in their sorted order. Rearrange the plugin cards and nav entries to match it. */ - var main = document.getElementById('main'); + var main = document.getElementsByTagName('main')[0]; var pluginsNav = document.getElementById('pluginsNav'); var entries = pluginsNav.children; if (main.children.length - 2 != entries.length) { @@ -462,7 +461,7 @@ function sortPlugins(evt) { /* Record the previous order in case the user cancels sorting. */ /* Start at 2 to skip summary and general messages. */ - var cards = document.getElementById('main').children; + var cards = document.getElementsByTagName('main')[0].children; loot.newLoadOrder = result.loadOrder; loot.lastLoadOrder = []; for (var i = 2; i < cards.length; ++i) { @@ -800,11 +799,7 @@ function setupEventHandlers() { /*Set up filter value and CSS setting storage read/write handlers.*/ elements = document.getElementById('filters').getElementsByTagName('input'); for (var i = 0; i < elements.length; ++i) { - if (elements[i].type == 'text') { - elements[i].addEventListener('input', saveFilterState, false); - } else { - elements[i].addEventListener('click', saveFilterState, false); - } + elements[i].addEventListener('click', saveFilterState, false); } /*Set up handlers for filters.*/ @@ -816,7 +811,6 @@ function setupEventHandlers() { document.getElementById('hideInactivePluginMessages').addEventListener('click', togglePlugins, false); document.getElementById('hideAllPluginMessages').addEventListener('click', togglePlugins, false); document.getElementById('hideMessagelessPlugins').addEventListener('click', togglePlugins, false); - document.getElementById('showOnlyConflicts').addEventListener('click', togglePlugins, false); /* Set up handlers for buttons. */ document.getElementById('fileMenu').addEventListener('click', openMenu, false); @@ -981,7 +975,7 @@ function updateInterfaceWithGameInfo(response) { } }); totalMessageNo = loot.game.globalMessages.length; - var pluginsList = document.getElementById('main'); + var pluginsList = document.getElementsByTagName('main')[0]; var pluginsNav = document.getElementById('pluginsNav'); loot.game.plugins.forEach(function(plugin) { diff --git a/resources/report/report.html b/resources/report/report.html index 8578e1be..b5600d2d 100644 --- a/resources/report/report.html +++ b/resources/report/report.html @@ -326,17 +326,29 @@ text-align: left; padding:0; } - div { + div, label { padding: 0.2em 0.5em; cursor: pointer; + display: block; } - div:hover { + div:hover, label:hover { background: #e3e3e3; } span { padding-right: 0.4em; } + input { + position: relative; + top: 0.15em; + margin: 0em 0.3em 0 0.1em; + } + .disabled { + cursor: default; + color: #999; + } + +
Edit Metadata
Copy Metadata As Text
Clear User Metadata
@@ -452,10 +464,7 @@
-
- - - +
Hidden plugins: 0 /
@@ -467,7 +476,7 @@
  • -
    +

    Summary

    @@ -489,7 +498,7 @@ - + diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index 98d266f5..d5da4f90 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -271,6 +271,10 @@ namespace loot { else if (requestName == "closeSettings") { BOOST_LOG_TRIVIAL(trace) << "Settings dialog closed and changes accepted, updating settings object."; g_app_state.UpdateSettings(request["args"][0]); + + // Also update the game details. + g_app_state. + callback->Success(""); return true; } @@ -321,13 +325,11 @@ namespace loot { if (g_app_state.CurrentGame().plugins.begin()->second.FormIDs().size() == 0) g_app_state.CurrentGame().LoadPlugins(false); - map conflictingPlugins; YAML::Node node; for (const auto& pluginPair : g_app_state.CurrentGame().plugins) { if (pluginIt != g_app_state.CurrentGame().plugins.end()) { if (pluginIt->second.DoFormIDsOverlap(pluginPair.second)) { BOOST_LOG_TRIVIAL(debug) << "Found conflicting plugin: " << pluginPair.second.Name(); - conflictingPlugins.emplace(pluginPair.second.Name(), pluginPair.second.Crc()); node["conflicts"].push_back(pluginPair.second.Name()); } }