From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 2A2501FF14C for ; Fri, 15 May 2026 10:55:24 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 881DC11C2D; Fri, 15 May 2026 10:54:37 +0200 (CEST) From: Dominik Csapak To: pve-devel@lists.proxmox.com Subject: [PATCH manager v3 07/13] ui: qemu: hardware: wrap in container Date: Fri, 15 May 2026 10:44:25 +0200 Message-ID: <20260515085349.1123127-8-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260515085349.1123127-1-d.csapak@proxmox.com> References: <20260515085349.1123127-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.050 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: M4ZPYU6JUWXLJUJLG7XUA22YP2JZOSIU X-Message-ID-Hash: M4ZPYU6JUWXLJUJLG7XUA22YP2JZOSIU X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: this is preparation for showing multiple grids in the hardware view, so we move the actual PendingObjectGrid one level deeper. Introduces some helpers that redirect to the PendingObjectGrid to keep the diff a bit smaller. Explicitly create the rstore outside the PendingObjectGrid because we'll need it anyway later on. Signed-off-by: Dominik Csapak --- www/manager6/qemu/HardwareView.js | 328 +++++++++++++++++------------- 1 file changed, 181 insertions(+), 147 deletions(-) diff --git a/www/manager6/qemu/HardwareView.js b/www/manager6/qemu/HardwareView.js index 58f86779..b5b2600d 100644 --- a/www/manager6/qemu/HardwareView.js +++ b/www/manager6/qemu/HardwareView.js @@ -1,46 +1,23 @@ Ext.define('PVE.qemu.HardwareView', { - extend: 'Proxmox.grid.PendingObjectGrid', + extend: 'Ext.container.Container', alias: ['widget.PVE.qemu.HardwareView'], onlineHelp: 'qm_virtual_machines_settings', - renderKey: function (key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - var iconCls = rowdef.iconCls; - var icon = ''; - var txt = rowdef.header || key; - - metaData.tdAttr = 'valign=middle'; - - if (rowdef.isOnStorageBus) { - let value = me.getObjectValue(key, '', false); - if (value === '') { - value = me.getObjectValue(key, '', true); - } - if (value.match(/vm-.*-cloudinit/)) { - iconCls = 'cloud'; - txt = rowdef.cloudheader; - } else if (PVE.Utils.diskIsCdrom(value)) { - metaData.tdCls = 'pve-itype-icon-cdrom'; - return rowdef.cdheader; - } - } + referenceHolder: true, - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } else if (iconCls) { - icon = ""; - metaData.tdCls += ' pve-itype-fa'; - } + // helpers to redirect the methods to the hardwareGrid - // only return icons in grid but not remove dialog - if (rowIndex !== undefined) { - return icon + txt; - } else { - return txt; - } + getObjectValue: function () { + let me = this; + let hardwareGrid = me.lookup('hardwareGrid'); + return hardwareGrid.getObjectValue(...arguments); + }, + + reload: function () { + let me = this; + let hardwareGrid = me.lookup('hardwareGrid'); + return hardwareGrid.reload(); }, initComponent: function () { @@ -612,7 +589,7 @@ Ext.define('PVE.qemu.HardwareView', { baseurl, rows, renderKey: function () { - return me.renderKey(...arguments); + return me.lookup('hardwareGrid').renderKey(...arguments); }, reloadCallback: () => me.reload(), }); @@ -649,7 +626,8 @@ Ext.define('PVE.qemu.HardwareView', { }; let set_button_status = function () { - let selection_model = me.getSelectionModel(); + let hardwareGrid = me.lookup('hardwareGrid'); + let selection_model = hardwareGrid.getSelectionModel(); let rec = selection_model.getSelection()[0]; counts = {}; // en/disable hardwarebuttons @@ -699,7 +677,7 @@ Ext.define('PVE.qemu.HardwareView', { const row = rows[key]; const deleted = !!rec.data.delete; - const pending = deleted || me.hasPendingChanges(key); + const pending = deleted || hardwareGrid.hasPendingChanges(key); const isRunning = me.pveSelNode.data.running; const isCloudInit = isCloudInitKey(value); @@ -766,118 +744,174 @@ Ext.define('PVE.qemu.HardwareView', { }); }; - Ext.apply(me, { + me.rstore = Ext.create('Proxmox.data.ObjectStore', { + model: 'KeyValuePendingDelete', + readArray: true, url: `/api2/json/nodes/${nodename}/qemu/${vmid}/pending`, interval: 5000, - selModel: sm, - run_editor: run_editor, - tbar: [ + rows, + }); + + Ext.apply(me, { + items: [ { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - cls: 'pve-add-hw-menu', - items: [ - { - text: gettext('Hard Disk'), - iconCls: 'fa fa-fw fa-hdd-o black', - disabled: !caps.vms['VM.Config.Disk'], - handler: editorFactory('HDEdit'), - }, - { - text: gettext('Import Hard Disk'), - iconCls: 'fa fa-fw fa-cloud-download', - disabled: !caps.vms['VM.Config.Disk'], - handler: editorFactory('HDEdit', { importDisk: true }), - }, - { - text: gettext('CD/DVD Drive'), - iconCls: 'pve-itype-icon-cdrom', - disabled: !caps.vms['VM.Config.CDROM'], - handler: editorFactory('CDEdit'), - }, - { - text: gettext('Network Device'), - itemId: 'addNet', - iconCls: 'fa fa-fw fa-exchange black', - disabled: !caps.vms['VM.Config.Network'], - handler: editorFactory('NetworkEdit'), - }, - efidisk_menuitem, - { - text: gettext('TPM State'), - itemId: 'addTpmState', - iconCls: 'fa fa-fw fa-hdd-o black', - disabled: !caps.vms['VM.Config.Disk'], - handler: editorFactory('TPMDiskEdit'), - }, - { - text: gettext('USB Device'), - itemId: 'addUsb', - iconCls: 'fa fa-fw fa-usb black', - disabled: - !caps.nodes['Sys.Console'] && !caps.mapping['Mapping.Use'], - handler: editorFactory('USBEdit'), - }, - { - text: gettext('PCI Device'), - itemId: 'addPci', - iconCls: 'pve-itype-icon-pci', - disabled: - !caps.nodes['Sys.Console'] && !caps.mapping['Mapping.Use'], - handler: editorFactory('PCIEdit'), - }, - { - text: gettext('Serial Port'), - itemId: 'addSerial', - iconCls: 'pve-itype-icon-serial', - disabled: !caps.vms['VM.Config.Options'], - handler: editorFactory('SerialEdit'), - }, - { - text: gettext('CloudInit Drive'), - itemId: 'addCloudinitDrive', - iconCls: 'fa fa-fw fa-cloud black', - disabled: - !caps.vms['VM.Config.CDROM'] || - !caps.vms['VM.Config.Cloudinit'], - handler: editorFactory('CIDriveEdit'), - }, - { - text: gettext('Audio Device'), - itemId: 'addAudio', - iconCls: 'fa fa-fw fa-volume-up black', - disabled: !caps.vms['VM.Config.HWType'], - handler: editorFactory('AudioEdit'), - }, - { - text: gettext('VirtIO RNG'), - itemId: 'addRng', - iconCls: 'pve-itype-icon-die', - disabled: - !caps.vms['VM.Config.HWType'] && !caps.mapping['Mapping.Use'], - handler: editorFactory('RNGEdit'), - }, - { - text: gettext('Virtiofs'), - itemId: 'addVirtiofs', - iconCls: 'fa fa-folder', - disabled: !caps.nodes['Sys.Console'], - handler: editorFactory('VirtiofsEdit'), - }, - ], - }), + xtype: 'proxmoxPendingObjectGrid', + reference: 'hardwareGrid', + border: false, + rstore: me.rstore, + interval: 5000, + selModel: sm, + run_editor: run_editor, + renderKey: function (key, metaData, rec, rowIndex, colIndex, store) { + var me = this; + var rows = me.rows; + var rowdef = rows[key] || {}; + var iconCls = rowdef.iconCls; + var icon = ''; + var txt = rowdef.header || key; + + metaData.tdAttr = 'valign=middle'; + + if (rowdef.isOnStorageBus) { + let value = me.getObjectValue(key, '', false); + if (value === '') { + value = me.getObjectValue(key, '', true); + } + if (value.match(/vm-.*-cloudinit/)) { + iconCls = 'cloud'; + txt = rowdef.cloudheader; + } else if (PVE.Utils.diskIsCdrom(value)) { + metaData.tdCls = 'pve-itype-icon-cdrom'; + return rowdef.cdheader; + } + } + + if (rowdef.tdCls) { + metaData.tdCls = rowdef.tdCls; + } else if (iconCls) { + icon = ""; + metaData.tdCls += ' pve-itype-fa'; + } + + // only return icons in grid but not remove dialog + if (rowIndex !== undefined) { + return icon + txt; + } else { + return txt; + } + }, + tbar: [ + { + text: gettext('Add'), + menu: new Ext.menu.Menu({ + cls: 'pve-add-hw-menu', + items: [ + { + text: gettext('Hard Disk'), + iconCls: 'fa fa-fw fa-hdd-o black', + disabled: !caps.vms['VM.Config.Disk'], + handler: editorFactory('HDEdit'), + }, + { + text: gettext('Import Hard Disk'), + iconCls: 'fa fa-fw fa-cloud-download', + disabled: !caps.vms['VM.Config.Disk'], + handler: editorFactory('HDEdit', { importDisk: true }), + }, + { + text: gettext('CD/DVD Drive'), + iconCls: 'pve-itype-icon-cdrom', + disabled: !caps.vms['VM.Config.CDROM'], + handler: editorFactory('CDEdit'), + }, + { + text: gettext('Network Device'), + itemId: 'addNet', + iconCls: 'fa fa-fw fa-exchange black', + disabled: !caps.vms['VM.Config.Network'], + handler: editorFactory('NetworkEdit'), + }, + efidisk_menuitem, + { + text: gettext('TPM State'), + itemId: 'addTpmState', + iconCls: 'fa fa-fw fa-hdd-o black', + disabled: !caps.vms['VM.Config.Disk'], + handler: editorFactory('TPMDiskEdit'), + }, + { + text: gettext('USB Device'), + itemId: 'addUsb', + iconCls: 'fa fa-fw fa-usb black', + disabled: + !caps.nodes['Sys.Console'] && + !caps.mapping['Mapping.Use'], + handler: editorFactory('USBEdit'), + }, + { + text: gettext('PCI Device'), + itemId: 'addPci', + iconCls: 'pve-itype-icon-pci', + disabled: + !caps.nodes['Sys.Console'] && + !caps.mapping['Mapping.Use'], + handler: editorFactory('PCIEdit'), + }, + { + text: gettext('Serial Port'), + itemId: 'addSerial', + iconCls: 'pve-itype-icon-serial', + disabled: !caps.vms['VM.Config.Options'], + handler: editorFactory('SerialEdit'), + }, + { + text: gettext('CloudInit Drive'), + itemId: 'addCloudinitDrive', + iconCls: 'fa fa-fw fa-cloud black', + disabled: + !caps.vms['VM.Config.CDROM'] || + !caps.vms['VM.Config.Cloudinit'], + handler: editorFactory('CIDriveEdit'), + }, + { + text: gettext('Audio Device'), + itemId: 'addAudio', + iconCls: 'fa fa-fw fa-volume-up black', + disabled: !caps.vms['VM.Config.HWType'], + handler: editorFactory('AudioEdit'), + }, + { + text: gettext('VirtIO RNG'), + itemId: 'addRng', + iconCls: 'pve-itype-icon-die', + disabled: + !caps.vms['VM.Config.HWType'] && + !caps.mapping['Mapping.Use'], + handler: editorFactory('RNGEdit'), + }, + { + text: gettext('Virtiofs'), + itemId: 'addVirtiofs', + iconCls: 'fa fa-folder', + disabled: !caps.nodes['Sys.Console'], + handler: editorFactory('VirtiofsEdit'), + }, + ], + }), + }, + remove_btn, + edit_btn, + diskaction_btn, + revert_btn, + ], + rows: rows, + sorterFn: sorterFn, + listeners: { + itemdblclick: run_editor, + selectionchange: set_button_status, + }, }, - remove_btn, - edit_btn, - diskaction_btn, - revert_btn, ], - rows: rows, - sorterFn: sorterFn, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status, - }, }); me.callParent(); @@ -885,6 +919,6 @@ Ext.define('PVE.qemu.HardwareView', { me.on('activate', me.rstore.startUpdate, me.rstore); me.on('destroy', me.rstore.stopUpdate, me.rstore); - me.mon(me.getStore(), 'datachanged', set_button_status, me); + me.mon(me.rstore, 'datachanged', set_button_status, me); }, }); -- 2.47.3