local function XPopupWindowWithSearch(anchor, title, create_children_func) local popup = OpenDialog("GedNestedElementsList", terminal.desktop) popup.OnShortcut = function(self, shortcut) if shortcut == "Escape" then popup:Close() return "break" elseif shortcut == "Down" then popup.idLeftList.idWin.idList:SetFocus() end end local list = popup.idLeftList.idWin.idList popup.idTitle:SetText(title) local edit = popup.idLeftList.idSearch local container = popup.idRightList edit.OnTextChanged = function(edit) create_children_func(popup, list, container, edit:GetText()) end edit.OnShortcut = function(edit, shortcut, source, ...) if shortcut == "Escape" then return elseif shortcut == "Enter" and edit:GetText() then local list = edit.parent.idWin.idList if list[1] and list[1]:IsKindOf("XTextButton") then list[1]:OnPress() return end end return XEdit.OnShortcut(edit, shortcut, source, ...) end list.OnShortcut = function(list, shortcut, source, ...) local relation = XShortcutToRelation[shortcut] if shortcut == "Down" or shortcut == "Up" or relation == "down" or relation == "up" then local focus = list.desktop.keyboard_focus local order = focus and focus:GetFocusOrder() if shortcut == "Down" or relation == "down" then focus = list:GetRelativeFocus(order or point(0, 0), "next") else focus = list:GetRelativeFocus(order or point(1000000000, 1000000000), "prev") end if focus then list:ScrollIntoView(focus) focus:SetFocus() end return "break" end end create_children_func(popup, list, container, "", "create") popup:SetModal(true) edit:SetFocus() end local function XPopupListWithSearch(anchor, create_children_func) local popup = XPopup:new({}, terminal.desktop) popup.OnKillFocus = function(self) if self.window_state == "open" then popup:Close() end end popup.OnShortcut = function(self, shortcut) if shortcut == "Escape" then popup:Close() return "break" elseif shortcut == "Down" then popup.idPopupList:SetFocus() end end local list = XPopupList:new({ Id = "idPopupList", AutoFocus = false, Dock = "bottom", MaxItems = 10, BorderWidth = 0, min_width = false, OnShortcut = function(list, shortcut, source, ...) -- let the popup handle escapes if shortcut == "Escape" then return end return XPopupList.OnShortcut(list, shortcut, source, ...) end, Measure = function(self, max_width, max_height) -- do not fold the search popup after items have been filtered local _, height if not self.min_width then self.min_width, height = XPopupList.Measure(self, max_width, max_height) else _, height = XPopupList.Measure(self, max_width, max_height) end return self.min_width, height end, OnKillFocus = function(self, new_focus) end, }, popup) local edit = XEdit:new({ Dock = "top", Id = "idSearch", Margins = box(2, 2, 2, 2), OnTextChanged = function(edit) create_children_func(popup, list.idContainer, edit:GetText()) end, OnShortcut = function(list, shortcut, source, ...) if shortcut == "Escape" then return end return XEdit.OnShortcut(list, shortcut, source, ...) end, }, popup) popup:SetAnchor(anchor.box) popup:SetAnchorType("drop-right") popup:SetScaleModifier(anchor.scale) popup:SetOutsideScale(point(1000, 1000)) popup:Open() create_children_func(popup, list.idContainer, "") if #list.idContainer > 5 then edit:SetFocus() else edit:delete() popup:SetFocus() end end function FillUsageSegments(list, segments) local sum = 0 local sorted = {} for _, item in ipairs(list) do if item.use_count then sum = sum + item.use_count table.insert(sorted, item) end end table.sortby_field(sorted, "use_count") local tally, target, segment = 0, 0, 0 for _, item in ipairs(sorted) do tally = tally + item.use_count if tally > target then segment = segment + 1 target = MulDivRound(sum, segment, segments) end item.usage_segment = segment end return sum end function GedOpenCreateItemPopup(panel, title, items, button, create_callback) if items and #items == 1 then create_callback(items[1].value) return end local defined = 0 for i = 1, #items do local cat = items[i].category if cat and cat ~= "" and cat ~= "General" then defined = defined + 1 end end if defined >= 3 then XPopupWindowWithSearch(button, title, function(popup, list, container, search_text, create) local least_dim = GetDarkModeSetting() and 255 or 32 local most_dim = GetDarkModeSetting() and 128 or 140 local modifiable_zone = most_dim - least_dim local suffixes = { "", "•", "••", "•••", } local uses_total = FillUsageSegments(items, 3) local create_button = function(idx, item, container, popup, right) local entry = XTextButton:new({ UseXTextControl = true }, container) entry:SetFocusOrder(point(1, idx)) entry:SetLayoutMethod("Box") entry.idLabel:SetHAlign("stretch") if right and search_text == "" and uses_total ~= 0 then local gamma = most_dim - MulDivRound(modifiable_zone, item.usage_segment, 3) entry:SetText(string.format("%s%s", gamma, gamma, gamma, item.text, suffixes[item.usage_segment + 1])) elseif right and item.usage_segment then entry:SetText(item.text .. suffixes[item.usage_segment + 1]) else entry:SetText(item.text) end entry.OnPress = function(entry) button:SetFocus() create_callback(item.value) button:SetFocus(false) if popup.window_state ~= "destroying" then popup:Close() end end if item.documentation or item.use_count then local texts = {} table.insert(texts, item.documentation or string.format("", item.value)) if item.use_count_in_preset then table.insert(texts, item.use_count and string.format("