Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.js (revision 23812) +++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.js (revision 23813) @@ -1,65 +1,70 @@ class TrainerSection { constructor(page) { this.page = page; this.width = 0; this.widthChangedHandlers = new Set(); this.TrainerSection = Engine.GetGUIObjectByName("trainerSection"); this.Trainers = Engine.GetGUIObjectByName("trainers"); this.TrainerSectionHeading = Engine.GetGUIObjectByName("trainerSectionHeading"); this.TrainerSectionHeading.caption = this.Caption; this.trainerBoxes = []; for (let boxIdx in this.Trainers.children) this.trainerBoxes.push(new TrainerBox(this.page, boxIdx)); } registerWidthChangedHandler(handler) { this.widthChangedHandlers.add(handler); } draw(units, civCode) { let caption = this.TrainerSectionHeading; this.width = Engine.GetTextWidth(caption.font, caption.caption) + (caption.size.left + caption.buffer_zone) * 2; let count = 0; for (let unitCode of units.keys()) { let unitTemplate = this.page.TemplateParser.getEntity(unitCode, civCode); if (!unitTemplate.production.units.length && !unitTemplate.production.techs.length && !unitTemplate.upgrades) continue; if (count > this.trainerBoxes.length) { error("\"" + this.activeCiv + "\" has more unit trainers than can be supported by the current GUI layout"); break; } this.width = Math.max( this.width, this.trainerBoxes[count].draw(unitCode, civCode) ); ++count; } hideRemaining(this.Trainers.name, count); // Update width and visibility of section let size = this.TrainerSection.size; this.width += EntityBox.prototype.HMargin; size.left = -this.width + size.right; this.TrainerSection.size = size; this.TrainerSection.hidden = count == 0; for (let handler of this.widthChangedHandlers) handler(this.width, !this.TrainerSection.hidden); } + + isVisible() + { + return !this.TrainerSection.hidden; + } } TrainerSection.prototype.Caption = translate("Trainer Units"); Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.js (revision 23812) +++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.js (revision 23813) @@ -1,77 +1,83 @@ class TreeSection { constructor(page) { this.page = page; this.TreeSection = Engine.GetGUIObjectByName("treeSection"); this.Structures = Engine.GetGUIObjectByName("structures"); this.PhaseIdents = new PhaseIdentManager(this.page); this.rightMargin = this.TreeSection.size.right; + this.vMargin = this.TreeSection.size.top + -this.TreeSection.size.bottom; + this.width = 0; + this.height = 0; this.structureBoxes = []; for (let boxIdx in this.Structures.children) this.structureBoxes.push(new StructureBox(this.page, boxIdx)); page.TrainerSection.registerWidthChangedHandler(this.onTrainerSectionWidthChange.bind(this)); } draw(structures, civCode) { if (structures.size > this.structureBoxes.length) error("\"" + this.activeCiv + "\" has more structures than can be supported by the current GUI layout"); // Draw structures let phaseList = this.page.TemplateParser.phaseList; let count = Math.min(structures.size, this.structureBoxes.length); let runningWidths = Array(phaseList.length).fill(0); let structureIterator = structures.keys(); for (let idx = 0; idx < count; ++idx) this.structureBoxes[idx].draw(structureIterator.next().value, civCode, runningWidths); hideRemaining(this.Structures.name, count); // Position phase idents this.PhaseIdents.draw(phaseList, civCode, runningWidths, this.Structures.size.left); + + this.width = this.Structures.size.left + Math.max(...runningWidths) + EntityBox.prototype.HMargin; + this.height = this.constructor.getPositionOffset(phaseList.length, this.page.TemplateParser); } drawPhaseIcon(phaseIcon, phaseIndex, civCode) { let phaseName = this.page.TemplateParser.phaseList[phaseIndex]; let prodPhaseTemplate = this.page.TemplateParser.getTechnology(phaseName + "_" + civCode, civCode) || this.page.TemplateParser.getTechnology(phaseName, civCode); phaseIcon.sprite = "stretched:" + this.page.IconPath + prodPhaseTemplate.icon; phaseIcon.tooltip = getEntityNamesFormatted(prodPhaseTemplate); }; onTrainerSectionWidthChange(trainerSectionWidth, trainerSectionVisible) { let size = this.TreeSection.size; size.right = this.rightMargin; if (trainerSectionVisible) size.right -= trainerSectionWidth + this.page.SectionGap; this.TreeSection.size = size; } /** * Calculate row position offset (accounting for different number of prod rows per phase). * * This is a static method as it is also used from within the StructureBox and PhaseIdent classes. * * @param {number} idx * @return {number} */ static getPositionOffset(idx, TemplateParser) { let phases = TemplateParser.phaseList.length; let rowHeight = ProductionIcon.Size().rowHeight; let size = EntityBox.IconAndCaptionHeight() * idx; // text, image and offset size += EntityBox.prototype.VMargin * (idx + 1); // Margin above StructureBoxes size += rowHeight * (phases * idx - (idx - 1) * idx / 2); // phase rows (phase-currphase+1 per row) return size; }; } Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/StructreePage.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/structree/StructreePage.js (revision 23812) +++ ps/trunk/binaries/data/mods/public/gui/reference/structree/StructreePage.js (revision 23813) @@ -1,58 +1,92 @@ /** * This class represents the Structure Tree GUI page. * * Further methods are described within draw.js */ class StructreePage extends ReferencePage { constructor(data) { super(); this.structureBoxes = []; this.trainerBoxes = []; + this.StructreePage = Engine.GetGUIObjectByName("structreePage"); + this.Background = Engine.GetGUIObjectByName("background"); this.CivEmblem = Engine.GetGUIObjectByName("civEmblem"); this.CivName = Engine.GetGUIObjectByName("civName"); this.CivHistory = Engine.GetGUIObjectByName("civHistory"); this.TrainerSection = new TrainerSection(this); this.TreeSection = new TreeSection(this); this.civSelection = new CivSelectDropdown(this.civData); if (!this.civSelection.hasCivs()) { this.closePage(); return; } this.civSelection.registerHandler(this.selectCiv.bind(this)); let civInfoButton = new CivInfoButton(this); let closeButton = new CloseButton(this); Engine.SetGlobalHotkey("structree", "Press", this.closePage.bind(this)); + + this.StructreePage.onWindowResized = this.updatePageWidth.bind(this); + this.width = 0; } closePage() { Engine.PopGuiPage({ "civ": this.activeCiv, "page": "page_structree.xml" }); } selectCiv(civCode) { this.setActiveCiv(civCode); this.CivEmblem.sprite = "stretched:" + this.civData[this.activeCiv].Emblem; this.CivName.caption = this.civData[this.activeCiv].Name; this.CivHistory.caption = this.civData[this.activeCiv].History; let templateLists = this.TemplateLister.getTemplateLists(this.activeCiv); this.TreeSection.draw(templateLists.structures, this.activeCiv); this.TrainerSection.draw(templateLists.units, this.activeCiv); + + this.width = this.TreeSection.width + -this.TreeSection.rightMargin; + if (this.TrainerSection.isVisible()) + this.width += this.TrainerSection.width + this.SectionGap; + this.updatePageWidth(); + this.updatePageHeight(); + } + + updatePageHeight() + { + let y = (this.TreeSection.height + this.TreeSection.vMargin) / 2; + let pageSize = this.StructreePage.size; + pageSize.top = -y; + pageSize.bottom = y; + this.StructreePage.size = pageSize; + } + + updatePageWidth() + { + let screenSize = this.Background.getComputedSize(); + let pageSize = this.StructreePage.size; + let x = Math.min(this.width, screenSize.right - this.BorderMargin * 2) / 2; + pageSize.left = -x; + pageSize.right = x; + this.StructreePage.size = pageSize; } } StructreePage.prototype.CloseButtonTooltip = translate("%(hotkey)s: Close Structure Tree."); // Gap between the `TreeSection` and `TrainerSection` gui objects (when the latter is visible) StructreePage.prototype.SectionGap = 12; + +// Margin around the edge of the structree on lower resolutions, +// preventing the UI from being clipped by the edges of the screen. +StructreePage.prototype.BorderMargin = 16; Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.xml (revision 23812) +++ ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.xml (revision 23813) @@ -1,64 +1,64 @@