Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


A filterable list box for dropdown value change

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Extensions
View previous topic :: View next topic  
Author Message
cannonfodderex
Advanced Cheater
Reputation: 0

Joined: 30 Oct 2012
Posts: 66

PostPosted: Thu Nov 06, 2025 2:18 am    Post subject: A filterable list box for dropdown value change Reply with quote

When I try to change a value of address with dropdown, a "change value" dialog popup.
When typing something in the combo box, and press up or down on keyboard, it only matches the start of a string.
So I made a form with text box filter for a list box, and match any substring, not just the start, so I can click to select a value from the filtered list.

This is done with the help of AylinCE and Dark Byte in this post, and AI
https://forum.cheatengine.org/viewtopic.php?p=5794106

How to use:
You need a memory record with dropdown data, in my code it's called "iItemList", you can change it's name in DropdownDataRecordName.
Then you need to modify the function isTargetMemoryRecordValid(memRecord), to judge what memory records you want to modify.
In my example it's 4bytes record with description beginning with "iItem[".
Create a script in your table, paste the code.
Enable the script, a window will popup, you may close it.
Double click the value of a record which you want to change, the window will popup, then double click a line in the list box, the id of that line will be set to the value of your selected record.


Code:

[ENABLE]
{$lua}
-- UNIQUE FORM NAME
local FORM_NAME = "CE_ItemListFilterForm"
local DropdownDataRecordName = "iItemList"

local function isTargetMemoryRecordValid(memRecord)
  --print(memRecord)
  if memRecord and memRecord.Type == 2 and string.sub(memRecord.Description, 1, 6) == "iItem["
    then return true
    else return false
  end
end

-- Find existing form by name using getForm/getFormCount
local function findFormByName(name)
  for i = 0, getFormCount() - 1 do
    local f = getForm(i)
    if f and f.Name == name then return f end
  end
  return nil
end

-- Create UI and store control references in frm.luaData
local function createUI()
  -- If an existing form is present, return it (reuse)
  local existing = findFormByName(FORM_NAME)
  if existing then
    return existing, true
  end

  -- Create new form as a child of main form
  local mf = getMainForm()
  local frm = createForm(mf)
  frm.Name = FORM_NAME
  frm.Caption = "Item List Filter"
  frm.Width = 500
  frm.Height = 400
  frm.Position = "poOwnerFormCenter"

  -- Create controls
  local lblFilter = createLabel(frm); lblFilter.Caption = "Filter:"; lblFilter.Left = 20; lblFilter.Top = 20
  local edtFilter = createEdit(frm); edtFilter.Left = 80; edtFilter.Top = 18; edtFilter.Width = 200; edtFilter.Text = ""; edtFilter.Hint = "Enter keywords for real-time filtering"; edtFilter.ShowHint = true
  local btnClear = createButton(frm); btnClear.Caption = "Clear"; btnClear.Left = 290; btnClear.Top = 18; btnClear.Width = 60
  local lblStatus = createLabel(frm); lblStatus.Caption = "Total 0 items"; lblStatus.Left = 360; lblStatus.Top = 22; lblStatus.Font.Style = '[]'
  local lbItems = createListBox(frm); lbItems.Left = 20; lbItems.Top = 50; lbItems.Width = 460; lbItems.Height = 300; lbItems.MultiSelect = false
  local lblSelected = createLabel(frm); lblSelected.Caption = "Selected: None"; lblSelected.Left = 20; lblSelected.Top = 355; lblSelected.Width = 460; lblSelected.Height = 20

  -- Store references and data on the form object so Disable can find them
  frm.luaData = {
    edtFilter = edtFilter,
    btnClear = btnClear,
    lblStatus = lblStatus,
    lbItems = lbItems,
    lblSelected = lblSelected,
    allItems = {}
  }

  -- Helper: convert id to number for sorting
  local function idToNumber(id) return tonumber(id) or 0 end

  -- Read item data from iItemList dropdown
  local function getItemDataFromDropdown()
    local itemData = {}
    local addressList = getAddressList()
    local iItemListRecord = nil
    for i = 0, addressList.Count - 1 do
      local rec = addressList[i]
      if rec and rec.Description == DropdownDataRecordName then iItemListRecord = rec; break end
    end
    if not iItemListRecord then return itemData end
    if not iItemListRecord.DropDownCount or iItemListRecord.DropDownCount == 0 then return itemData end
    for i = 0, iItemListRecord.DropDownCount - 1 do
      local v = iItemListRecord.DropDownValue[i]
      local d = iItemListRecord.DropDownDescription[i]
      if v and d then itemData[tostring(v)] = d end
    end
    return itemData
  end

  -- Update status label
  local function updateStatus(frm, matchCount)
    local data = frm.luaData
    local total = #data.allItems
    matchCount = matchCount or total
    if matchCount == total then
      data.lblStatus.Caption = string.format("Total %d items", total)
      data.lblStatus.Font.Color = 0x000000
    else
      data.lblStatus.Caption = string.format("Showing %d / %d items", matchCount, total)
      data.lblStatus.Font.Color = 0xFF0000
    end
  end

  -- Initialize list content from dropdown
  local function initializeList(frm)
    local data = frm.luaData
    data.lbItems.Items.clear()
    data.allItems = {}
    local itemData = getItemDataFromDropdown()
    local temp = {}
    for id, name in pairs(itemData) do
      table.insert(temp, { id = id, name = name, display = string.format("[%s] %s", id, name), numericId = idToNumber(id) })
    end
    table.sort(temp, function(a, b) return a.numericId < b.numericId end)
    for _, it in ipairs(temp) do
      table.insert(data.allItems, it)
      data.lbItems.Items.add(it.display)
    end
    updateStatus(frm)
  end

  -- Get currently selected item info from listbox
  local function getSelectedItemInfo(frm)
    local data = frm.luaData
    local idx = data.lbItems.ItemIndex
    if idx >= 0 then
      local txt = data.lbItems.Items[idx]
      local id, name = string.match(txt, "%[(%d+)%] (.+)")
      if id and name then return { id = id, name = name } end
    end
    return nil
  end

  -- Apply selected value to currently selected addresslist record
  local function applyValue(frm, selected)
    local addressList = getAddressList()
    local target = addressList.SelectedRecord
    if not target then showMessage("ERROR: No item memory record is selected in the Cheat Table."); return end
    if isTargetMemoryRecordValid(target) then
      target.Value = selected.id
      addressList.refresh()
    end
  end

  -- Filter list by text
  local function filterList(frm, text)
    local data = frm.luaData
    data.lbItems.Items.clear()
    local f = string.lower(text or "")
    local matches = 0
    for _, it in ipairs(data.allItems) do
      if string.find(string.lower(it.display), f, 1, true)
         or string.find(string.lower(it.name), f, 1, true)
         or string.find(string.lower(it.id), f, 1, true) then
        data.lbItems.Items.add(it.display)
        matches = matches + 1
      end
    end
    updateStatus(frm, matches)
  end

  -- Event bindings using stored control refs
  edtFilter.OnChange = function() filterList(frm, edtFilter.Text) end
  btnClear.OnClick = function() edtFilter.Text = ""; filterList(frm, ""); edtFilter.SetFocus() end
  lbItems.OnSelectionChange = function()
    local s = getSelectedItemInfo(frm)
    if s then frm.luaData.lblSelected.Caption = string.format("Selected: ID=%s, Name=%s", s.id, s.name)
    else frm.luaData.lblSelected.Caption = "Selected: None" end
  end
  lbItems.OnDblClick = function()
    local s = getSelectedItemInfo(frm); if s then applyValue(frm, s) end
  end

  -- Try to make OnClose hide the form instead of forcing destruction
  pcall(function()
    frm.OnClose = function(sender, closeAction)
      if type(closeAction) == "userdata" then closeAction = caHide end
      sender.Hide()
    end
  end)

  -- Populate the list now so it is ready when shown later
  initializeList(frm)

  return frm, false
end

-- Create UI but do not show it now; it will be shown in AddressList.OnValueChange
local ok, frm = pcall(function() return createUI() end)
if not ok then
  print("Failed to create UI in ENABLE:", frm)
end

-- Bind AddressList.OnValueChange safely; this will show the form when appropriate
AddressList.OnValueChange = function(addresslist, memrec)
  if isTargetMemoryRecordValid(memrec) then
    -- Ensure the form exists; create if missing
    local f = findFormByName(FORM_NAME)
    if not f then
      pcall(function() createUI() end)
      f = findFormByName(FORM_NAME)
    end
    -- Show the form safely (only here)
    if f then pcall(function() if not f.Visible then f.Show() end end) end
    return true
  else
    return false
  end
end



[DISABLE]
{$lua}
local FORM_NAME = "CE_ItemListFilterForm"

-- Find existing form by name using getForm/getFormCount
local function findFormByName(name)
  for i=0, getFormCount()-1 do
    local f = getForm(i)
    if f and f.Name == name then return f end
  end
  return nil
end

-- Locate form and safely detach events, hide and destroy it
local frm = findFormByName(FORM_NAME)
if frm then
  pcall(function()
    if frm.luaData then
      if frm.luaData.lbItems then
        frm.luaData.lbItems.OnSelectionChange = nil
        frm.luaData.lbItems.OnDblClick = nil
      end
      if frm.luaData.edtFilter then frm.luaData.edtFilter.OnChange = nil end
      if frm.luaData.btnClear then frm.luaData.btnClear.OnClick = nil end
    end
    if frm.Visible then frm.Hide() end
    frm.Destroy()
  end)
end

-- Clear AddressList callback
AddressList.OnValueChange = nil



address list.PNG
 Description:
 Filesize:  41.32 KB
 Viewed:  180 Time(s)

address list.PNG


Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Extensions All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites