class UIScene extends Phaser.Scene { constructor() { super({ key: 'UIScene' }); } create() { console.log('🖥️ UIScene: Initialized!'); // Pridobi reference na GameScene podatke (ko bodo na voljo) this.gameScene = this.scene.get('GameScene'); // Setup UI Container // Shared Overlay (DayNight + Weather) // Rendered here to be screen-space (HUD) and ignore zoom this.overlayGraphics = this.add.graphics(); this.overlayGraphics.setDepth(-1000); // Behind UI elements this.createStatusBars(); this.createInventoryBar(); this.createGoldDisplay(); this.createClock(); // this.createDebugInfo(); this.createSettingsButton(); // Resize event this.scale.on('resize', this.resize, this); // Crafting Menu (C) this.input.keyboard.on('keydown-C', () => { this.toggleCraftingMenu(); }); // Save (F5) this.input.keyboard.on('keydown-F5', () => { if (this.gameScene) this.gameScene.saveGame(); }); // Factory Reset (F8) - Fix za "zginla drevesa" this.input.keyboard.on('keydown-F8', () => { console.log('🔥 FACTORY RESET - Clearing Save & Reloading...'); localStorage.removeItem('novafarma_savefile'); window.location.reload(); }); } // ... (rest of class) ... toggleCraftingMenu() { if (!this.craftingContainer) this.createCraftingMenu(); this.craftingContainer.setVisible(!this.craftingContainer.visible); } createCraftingMenu() { const w = 300; const h = 250; const x = this.scale.width / 2; const y = this.scale.height / 2; this.craftingContainer = this.add.container(x, y); this.craftingContainer.setDepth(2000); // Top of everything // Bg const bg = this.add.graphics(); bg.fillStyle(0x222222, 0.95); bg.fillRect(-w / 2, -h / 2, w, h); bg.lineStyle(2, 0x888888, 1); bg.strokeRect(-w / 2, -h / 2, w, h); this.craftingContainer.add(bg); // Title const title = this.add.text(0, -h / 2 + 20, 'CRAFTING', { fontSize: '24px', fontStyle: 'bold', color: '#ffffff' }).setOrigin(0.5); this.craftingContainer.add(title); // Recipes const recipes = [ { name: 'Axe', code: '1', req: '5 Wood', type: 'axe', cost: { wood: 5 } }, { name: 'Pickaxe', code: '2', req: '5 Wood, 2 Stone', type: 'pickaxe', cost: { wood: 5, stone: 2 } }, { name: 'Hoe', code: '3', req: '3 Wood, 2 Stone', type: 'hoe', cost: { wood: 3, stone: 2 } }, { name: 'Sword', code: '4', req: '10 Wood, 5 Stone', type: 'sword', cost: { wood: 10, stone: 5 } } ]; recipes.forEach((r, i) => { const rowY = -h / 2 + 70 + (i * 40); // Text const txt = this.add.text(-w / 2 + 20, rowY, `[${r.code}] ${r.name} (${r.req})`, { fontSize: '16px', color: '#eeeeee' }); this.craftingContainer.add(txt); // Button Logic (Keyboard 1-4 works too via GameScene? No, let's localize input) }); // Instruction const instr = this.add.text(0, h / 2 - 20, 'Press number keys to craft', { fontSize: '12px', color: '#aaaaaa' }).setOrigin(0.5); this.craftingContainer.add(instr); // Input listener for crafting this.input.keyboard.on('keydown', (e) => { if (!this.craftingContainer.visible) return; const key = e.key; const recipe = recipes.find(r => r.code === key); if (recipe) { this.tryCraft(recipe); } }); this.craftingContainer.setVisible(false); } tryCraft(recipe) { if (!this.gameScene || !this.gameScene.inventorySystem) return; const inv = this.gameScene.inventorySystem; // Check cost if (recipe.cost.wood && !inv.hasItem('wood', recipe.cost.wood)) { console.log('Craft fail: Wood'); return; // Add UI feedback "Need Wood" } if (recipe.cost.stone && !inv.hasItem('stone', recipe.cost.stone)) { console.log('Craft fail: Stone'); return; } // Consume if (recipe.cost.wood) inv.removeItem('wood', recipe.cost.wood); if (recipe.cost.stone) inv.removeItem('stone', recipe.cost.stone); // Add Item inv.addItem(recipe.type, 1); console.log(`Crafted ${recipe.name}!`); // Flash effect this.cameras.main.flash(200, 0, 255, 0); // Green flash this.craftingContainer.setVisible(false); } resize(gameSize) { this.width = gameSize.width; this.height = gameSize.height; this.cameras.main.setViewport(0, 0, this.width, this.height); // Re-create UI elements at new positions this.createClock(); this.createGoldDisplay(); this.createInventoryBar(); this.createInventoryBar(); this.createDebugInfo(); this.createSettingsButton(); // Refresh data if (this.gameScene) { if (this.gameScene.inventorySystem) { this.updateInventory(this.gameScene.inventorySystem.slots); } // Clock/Gold update automatically in next frame // Overlay fixes itself via Systems if (this.overlayGraphics) { this.overlayGraphics.clear(); } } } createStatusBars() { const x = 20; const y = 20; const width = 200; const height = 20; const padding = 10; // Style const boxStyle = { fillStyle: { color: 0x000000, alpha: 0.5 }, lineStyle: { width: 2, color: 0xffffff, alpha: 0.8 } }; // 1. Health Bar this.add.text(x, y - 5, 'HP', { fontSize: '12px', fontFamily: 'Courier New', fill: '#ffffff' }); this.healthBar = this.createBar(x + 30, y, width, height, 0xff0000); this.setBarValue(this.healthBar, 100); // 2. Hunger Bar this.add.text(x, y + height + padding - 5, 'HUN', { fontSize: '12px', fontFamily: 'Courier New', fill: '#ffffff' }); this.hungerBar = this.createBar(x + 30, y + height + padding, width, height, 0xff8800); this.setBarValue(this.hungerBar, 80); // 3. Thirst Bar this.add.text(x, y + (height + padding) * 2 - 5, 'H2O', { fontSize: '12px', fontFamily: 'Courier New', fill: '#ffffff' }); this.thirstBar = this.createBar(x + 30, y + (height + padding) * 2, width, height, 0x0088ff); this.setBarValue(this.thirstBar, 90); } createBar(x, y, width, height, color) { // Background const bg = this.add.graphics(); bg.fillStyle(0x000000, 0.5); bg.fillRect(x, y, width, height); bg.lineStyle(2, 0xffffff, 0.2); bg.strokeRect(x, y, width, height); // Fill const fill = this.add.graphics(); fill.fillStyle(color, 1); fill.fillRect(x + 2, y + 2, width - 4, height - 4); return { bg, fill, x, y, width, height, color }; } setBarValue(bar, percent) { // Clamp 0-100 percent = Phaser.Math.Clamp(percent, 0, 100); bar.fill.clear(); bar.fill.fillStyle(bar.color, 1); const maxWidth = bar.width - 4; const currentWidth = (maxWidth * percent) / 100; bar.fill.fillRect(bar.x + 2, bar.y + 2, currentWidth, bar.height - 4); } createInventoryBar() { // Clean up if (this.inventorySlots) { this.inventorySlots.forEach(slot => { if (slot.itemSprite) slot.itemSprite.destroy(); if (slot.itemText) slot.itemText.destroy(); if (slot.countText) slot.countText.destroy(); slot.destroy(); }); } const slotCount = 9; const slotSize = 48; // 48x48 sloti const padding = 5; const totalWidth = (slotCount * slotSize) + ((slotCount - 1) * padding); const startX = (this.scale.width - totalWidth) / 2; const startY = this.scale.height - slotSize - 20; this.inventorySlots = []; if (this.selectedSlot === undefined) this.selectedSlot = 0; for (let i = 0; i < slotCount; i++) { const x = startX + i * (slotSize + padding); // Slot Background const slot = this.add.graphics(); // Draw function to update style based on selection slot.userData = { x, y: startY, size: slotSize, index: i }; this.drawSlot(slot, false); // Add number text this.add.text(x + 2, startY + 2, (i + 1).toString(), { fontSize: '10px', fontFamily: 'monospace', fill: '#ffffff' }); this.inventorySlots.push(slot); } // Select first one initially this.selectSlot(0); // Keyboard inputs 1-9 this.input.keyboard.on('keydown', (event) => { const num = parseInt(event.key); if (!isNaN(num) && num >= 1 && num <= 9) { this.selectSlot(num - 1); } }); // Mouse scroll for inventory (optional) this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => { if (deltaY > 0) { this.selectSlot((this.selectedSlot + 1) % slotCount); } else if (deltaY < 0) { this.selectSlot((this.selectedSlot - 1 + slotCount) % slotCount); } }); } drawSlot(graphics, isSelected) { const { x, y, size } = graphics.userData; graphics.clear(); // Background graphics.fillStyle(0x000000, 0.6); graphics.fillRect(x, y, size, size); // Border if (isSelected) { graphics.lineStyle(3, 0xffff00, 1); // Yellow thick border for selection } else { graphics.lineStyle(2, 0x888888, 0.5); // Grey thin border } graphics.strokeRect(x, y, size, size); } selectSlot(index) { // Deselect current if (this.inventorySlots[this.selectedSlot]) { this.drawSlot(this.inventorySlots[this.selectedSlot], false); } this.selectedSlot = index; // Select new this.drawSlot(this.inventorySlots[this.selectedSlot], true); } updateInventory(slots) { if (!this.inventorySlots) return; for (let i = 0; i < this.inventorySlots.length; i++) { const slotGraphics = this.inventorySlots[i]; // Clean up old visual elements if (slotGraphics.itemSprite) { slotGraphics.itemSprite.destroy(); slotGraphics.itemSprite = null; } if (slotGraphics.itemText) { slotGraphics.itemText.destroy(); slotGraphics.itemText = null; } if (slotGraphics.countText) { slotGraphics.countText.destroy(); slotGraphics.countText = null; } if (slots[i]) { const { x, y, size } = slotGraphics.userData; const type = slots[i].type; const textureKey = `item_${type}`; // e.g., item_axe, item_wood // Check if texture exists if (this.textures.exists(textureKey)) { // Draw Sprite const sprite = this.add.sprite(x + size / 2, y + size / 2, textureKey); // Scale to fit slot (32px slot, maybe 24px icon) const scale = (size - 8) / Math.max(sprite.width, sprite.height); sprite.setScale(scale); slotGraphics.itemSprite = sprite; } else { // Fallback Text const text = this.add.text(x + size / 2, y + size / 2, type.substring(0, 2).toUpperCase(), { fontSize: '12px', align: 'center', color: '#ffff00', stroke: '#000', strokeThickness: 2 } ).setOrigin(0.5); slotGraphics.itemText = text; } // Draw Count (if > 1) if (slots[i].count > 1) { const countText = this.add.text(x + size - 2, y + size - 2, slots[i].count.toString(), { fontSize: '10px', align: 'right', color: '#ffffff', stroke: '#000', strokeThickness: 2 } ).setOrigin(1, 1); slotGraphics.countText = countText; } } } } createClock() { if (this.clockBg) this.clockBg.destroy(); if (this.clockText) this.clockText.destroy(); // Clock box top right const x = this.scale.width - 150; const y = 20; // Background const bg = this.add.graphics(); bg.fillStyle(0x000000, 0.5); bg.fillRect(x, y, 130, 40); bg.lineStyle(2, 0xffffff, 0.8); bg.strokeRect(x, y, 130, 40); this.clockBg = bg; // Save ref this.clockText = this.add.text(x + 65, y + 20, 'Day 1 - 08:00', { fontSize: '14px', fontFamily: 'Courier New', fill: '#ffffff', fontStyle: 'bold' }); this.clockText.setOrigin(0.5, 0.5); } createGoldDisplay() { if (this.goldBg) this.goldBg.destroy(); if (this.goldText) this.goldText.destroy(); const x = this.scale.width - 150; const y = 70; // Below clock // Background const bg = this.add.graphics(); bg.fillStyle(0xDAA520, 0.2); // Goldish bg bg.fillRect(x, y, 130, 30); bg.lineStyle(2, 0xFFD700, 0.8); bg.strokeRect(x, y, 130, 30); this.goldBg = bg; // Save ref this.goldText = this.add.text(x + 65, y + 15, 'GOLD: 0', { fontSize: '16px', fontFamily: 'Courier New', fill: '#FFD700', // Gold color fontStyle: 'bold' }); this.goldText.setOrigin(0.5, 0.5); } updateGold(amount) { if (this.goldText) { this.goldText.setText(`GOLD: ${amount}`); } } createDebugInfo() { if (this.debugText) this.debugText.destroy(); if (this.debugBg) this.debugBg.destroy(); const x = this.scale.width - 170; const y = 120; // Below Gold and Clock area // Background this.debugBg = this.add.graphics(); this.debugBg.fillStyle(0x000000, 0.7); this.debugBg.fillRect(x, y, 160, 70); this.debugBg.setDepth(2999); this.debugText = this.add.text(x + 10, y + 10, 'Waiting for stats...', { fontSize: '12px', fontFamily: 'monospace', fill: '#ffffff', stroke: '#000000', strokeThickness: 2 }); this.debugText.setDepth(3000); } update() { if (!this.gameScene) return; // Sync HP Bar if (this.gameScene.player && this.healthBar) { const hp = this.gameScene.player.hp !== undefined ? this.gameScene.player.hp : 100; const maxHp = this.gameScene.player.maxHp || 100; this.setBarValue(this.healthBar, (hp / maxHp) * 100); } if (this.gameScene.statsSystem && this.hungerBar && this.thirstBar) { const stats = this.gameScene.statsSystem; this.setBarValue(this.hungerBar, stats.hunger); this.setBarValue(this.thirstBar, stats.thirst); } } toggleBuildMenu(isVisible) { if (!this.buildMenuContainer) { this.createBuildMenuInfo(); } this.buildMenuContainer.setVisible(isVisible); } createBuildMenuInfo() { this.buildMenuContainer = this.add.container(this.width / 2, 100); const bg = this.add.graphics(); bg.fillStyle(0x000000, 0.7); bg.fillRect(-150, 0, 300, 100); bg.lineStyle(2, 0x00FF00, 1); bg.strokeRect(-150, 0, 300, 100); this.buildMenuContainer.add(bg); const title = this.add.text(0, 10, 'BUILD MODE [B]', { fontSize: '18px', fill: '#00FF00', fontStyle: 'bold' }).setOrigin(0.5, 0); this.buildMenuContainer.add(title); const info = this.add.text(0, 40, '[1] Fence (2 Wood)\n[2] Wall (2 Stone)\n[3] House (20W 20S 50G)', { fontSize: '14px', fill: '#ffffff', align: 'center' } ).setOrigin(0.5, 0); this.buildMenuContainer.add(info); this.selectedBuildingText = this.add.text(0, 80, 'Selected: Fence', { fontSize: '14px', fill: '#FFFF00' }).setOrigin(0.5, 0); this.buildMenuContainer.add(this.selectedBuildingText); this.buildMenuContainer.setVisible(false); } updateBuildSelection(name) { if (this.selectedBuildingText) { this.selectedBuildingText.setText(`Selected: ${name.toUpperCase()}`); } } showProjectMenu(ruinData, onContribute) { if (!this.projectMenuContainer) { this.createProjectMenu(); } // Update info const costText = `Req: ${ruinData.reqWood} Wood, ${ruinData.reqStone} Stone`; this.projectInfoText.setText(`RESTORING RUINS\n${costText}`); this.onContributeCallback = onContribute; this.projectMenuContainer.setVisible(true); this.projectMenuContainer.setDepth(10000); } createProjectMenu() { this.projectMenuContainer = this.add.container(this.width / 2, this.height / 2); // BG const bg = this.add.graphics(); bg.fillStyle(0x222222, 0.9); bg.fillRect(-150, -100, 300, 200); bg.lineStyle(2, 0x00FFFF, 1); bg.strokeRect(-150, -100, 300, 200); this.projectMenuContainer.add(bg); // Title const title = this.add.text(0, -80, 'PROJECT: RESTORATION', { fontSize: '20px', fill: '#00FFFF', fontStyle: 'bold' }).setOrigin(0.5); this.projectMenuContainer.add(title); // Info this.projectInfoText = this.add.text(0, -20, 'Req: ???', { fontSize: '16px', fill: '#ffffff', align: 'center' }).setOrigin(0.5); this.projectMenuContainer.add(this.projectInfoText); // Button const btnBg = this.add.rectangle(0, 50, 200, 40, 0x00aa00); btnBg.setInteractive(); btnBg.on('pointerdown', () => { if (this.onContributeCallback) this.onContributeCallback(); // Close menu? Or keep open to see result? // For now close this.projectMenuContainer.setVisible(false); }); this.projectMenuContainer.add(btnBg); const btnText = this.add.text(0, 50, 'CONTRIBUTE', { fontSize: '18px', fill: '#ffffff' }).setOrigin(0.5); this.projectMenuContainer.add(btnText); // Close Button const closeBtn = this.add.text(130, -90, 'X', { fontSize: '20px', fill: '#ff0000' }).setOrigin(0.5); closeBtn.setInteractive(); closeBtn.on('pointerdown', () => this.projectMenuContainer.setVisible(false)); this.projectMenuContainer.add(closeBtn); this.projectMenuContainer.setVisible(false); } showTradeMenu(inventorySystem) { if (!this.tradeMenuContainer) { this.createTradeMenu(inventorySystem); } this.updateTradeMenu(inventorySystem); this.tradeMenuContainer.setVisible(true); this.tradeMenuContainer.setDepth(10000); } createTradeMenu(inventorySystem) { this.tradeMenuContainer = this.add.container(this.width / 2, this.height / 2); // BG const bg = this.add.graphics(); bg.fillStyle(0x222222, 0.95); bg.fillRect(-200, -150, 400, 300); bg.lineStyle(2, 0xFFD700, 1); // Gold border bg.strokeRect(-200, -150, 400, 300); this.tradeMenuContainer.add(bg); // Title const title = this.add.text(0, -130, 'MERCHANT SHOP', { fontSize: '24px', fill: '#FFD700', fontStyle: 'bold' }).setOrigin(0.5); this.tradeMenuContainer.add(title); // Close Button const closeBtn = this.add.text(180, -140, 'X', { fontSize: '20px', fill: '#ff0000', fontStyle: 'bold' }).setOrigin(0.5); closeBtn.setInteractive({ useHandCursor: true }); closeBtn.on('pointerdown', () => this.tradeMenuContainer.setVisible(false)); this.tradeMenuContainer.add(closeBtn); // Content Container (for items) this.tradeItemsContainer = this.add.container(0, 0); this.tradeMenuContainer.add(this.tradeItemsContainer); } updateTradeMenu(inventorySystem) { this.tradeItemsContainer.removeAll(true); // Items to Sell (Player has -> Merchant wants) // Hardcoded prices for now const prices = { 'wheat': { price: 10, type: 'sell' }, 'wood': { price: 2, type: 'sell' }, 'seeds': { price: 5, type: 'buy' } }; const startY = -80; let index = 0; // Header const header = this.add.text(-180, startY, 'ITEM PRICE ACTION', { fontSize: '16px', fill: '#888888' }); this.tradeItemsContainer.add(header); // 1. Sell Wheat this.createTradeRow(inventorySystem, 'wheat', prices.wheat.price, 'SELL', index++, startY + 30); // 2. Sell Wood this.createTradeRow(inventorySystem, 'wood', prices.wood.price, 'SELL', index++, startY + 30); // 3. Buy Seeds this.createTradeRow(inventorySystem, 'seeds', prices.seeds.price, 'BUY', index++, startY + 30); } createTradeRow(inv, itemKey, price, action, index, yOffset) { const y = yOffset + (index * 40); // Name const name = this.add.text(-180, y, itemKey.toUpperCase(), { fontSize: '18px', fill: '#ffffff' }); this.tradeItemsContainer.add(name); // Price const priceText = this.add.text(-50, y, `${price}g`, { fontSize: '18px', fill: '#FFD700' }); this.tradeItemsContainer.add(priceText); // Button const btnX = 100; const btnBg = this.add.rectangle(btnX, y + 10, 80, 30, action === 'BUY' ? 0x008800 : 0x880000); btnBg.setInteractive({ useHandCursor: true }); const btnLabel = this.add.text(btnX, y + 10, action, { fontSize: '16px', fill: '#ffffff' }).setOrigin(0.5); btnBg.on('pointerdown', () => { if (action === 'SELL') { if (inv.hasItem(itemKey, 1)) { inv.removeItem(itemKey, 1); inv.gold += price; inv.updateUI(); // Refresh visuals? } else { // Fail feedback btnLabel.setText('NO ITEM'); this.scene.time.delayedCall(500, () => btnLabel.setText(action)); } } else if (action === 'BUY') { if (inv.gold >= price) { inv.gold -= price; inv.addItem(itemKey, 1); inv.updateUI(); } else { // Fail feedback btnLabel.setText('NO GOLD'); this.scene.time.delayedCall(500, () => btnLabel.setText(action)); } } }); this.tradeItemsContainer.add(btnBg); this.tradeItemsContainer.add(btnLabel); } // --- SETTINGS MENU --- createSettingsButton() { if (this.settingsBtn) this.settingsBtn.destroy(); this.settingsBtn = this.add.text(10, 10, '⚙️ SETTINGS', { fontSize: '16px', fill: '#ffffff', backgroundColor: '#000000', padding: { x: 5, y: 5 } }); this.settingsBtn.setInteractive({ useHandCursor: true }); this.settingsBtn.on('pointerdown', () => this.toggleSettingsMenu()); } toggleSettingsMenu() { if (!this.settingsContainer) { this.createSettingsMenu(); } this.settingsContainer.setVisible(!this.settingsContainer.visible); if (this.settingsContainer.visible) { this.settingsContainer.setDepth(20000); // Always on top } } createSettingsMenu() { this.settingsContainer = this.add.container(this.width / 2, this.height / 2); // BG const bg = this.add.graphics(); bg.fillStyle(0x000000, 0.9); bg.fillRect(-150, -120, 300, 240); bg.lineStyle(2, 0x888888, 1); bg.strokeRect(-150, -120, 300, 240); this.settingsContainer.add(bg); // Title const title = this.add.text(0, -90, 'SETTINGS', { fontSize: '24px', fontStyle: 'bold', fill: '#ffffff' }).setOrigin(0.5); this.settingsContainer.add(title); // Close const closeBtn = this.add.text(130, -110, 'X', { fontSize: '20px', fill: '#ff0000' }).setOrigin(0.5); closeBtn.setInteractive({ useHandCursor: true }); closeBtn.on('pointerdown', () => this.toggleSettingsMenu()); this.settingsContainer.add(closeBtn); // Options let y = -40; // 1. View Distance (Simple Toggle for now: Low/High) this.createSettingToggle(0, y, 'VIEW DISTANCE', () => this.gameScene.settings.viewDistance, // getter (val) => { // setter this.gameScene.settings.viewDistance = val; // Apply immediately? Or next frame. // TerrainSystem reads it in updateCulling? We need to ensure it does. if (this.gameScene.terrainSystem) this.gameScene.terrainSystem.lastCullX = -9999; // Force update }, ['LOW', 'HIGH'] ); y += 50; // 2. Weather Particles this.createSettingToggle(0, y, 'PARTICLES', () => this.gameScene.settings.particles, (val) => { this.gameScene.settings.particles = val; // Restart rain if active if (this.gameScene.weatherSystem && (this.gameScene.weatherSystem.currentWeather === 'rain' || this.gameScene.weatherSystem.currentWeather === 'storm')) { this.gameScene.weatherSystem.startRain(this.gameScene.weatherSystem.currentWeather === 'storm'); } }, ['NONE', 'LOW', 'HIGH'] ); y += 50; // 3. Shadows this.createSettingToggle(0, y, 'SHADOWS', () => this.gameScene.settings.shadows ? 'ON' : 'OFF', (val) => { this.gameScene.settings.shadows = (val === 'ON'); // Trigger redraw? Complex. For now just saves state. // Maybe reload scene? }, ['ON', 'OFF'] ); } createSettingToggle(x, y, label, getter, setter, options) { const labelText = this.add.text(x - 80, y, label, { fontSize: '16px', fill: '#aaaaaa' }).setOrigin(1, 0.5); this.settingsContainer.add(labelText); const currentVal = getter(); const valueText = this.add.text(x + 50, y, currentVal, { fontSize: '16px', fill: '#ffffff', fontStyle: 'bold' }).setOrigin(0.5, 0.5); this.settingsContainer.add(valueText); // Click to cycle const hitArea = this.add.rectangle(x + 50, y, 100, 30, 0xffffff, 0.1); hitArea.setInteractive({ useHandCursor: true }); hitArea.on('pointerdown', () => { const cur = getter(); let idx = options.indexOf(cur); idx = (idx + 1) % options.length; const next = options[idx]; setter(next); valueText.setText(next); }); this.settingsContainer.add(hitArea); } }