--*********************************************************** --** THE INDIE STONE ** --*********************************************************** require "ISUI/ISPanel" require "ISUI/ISScrollingListBox" require "ISUI/ISTextEntryBox" ISComboBox = ISPanel:derive("ISComboBox"); ISComboBoxEditor = ISTextEntryBox:derive("ISComboBoxEditor"); ISComboBoxPopup = ISScrollingListBox:derive("ISComboBoxPopup"); ----- function ISComboBoxEditor:new(x, y, width, height, comboBox) local o = ISTextEntryBox.new(self, "", x, y, width, height) o.borderColor.a = 0.0 o.backgroundColor.a = 0.0 o.parentCombo = comboBox return o end function ISComboBoxEditor:onTextChange() local filterText = self:getInternalText() self.parentCombo:setFilterText(filterText) end function ISComboBoxEditor:onOtherKey(key) if key == Keyboard.KEY_ESCAPE then self.parentCombo.expanded = false self.parentCombo:hidePopup() end end ----- function ISComboBoxPopup:new(x, y, width, height) local o = ISScrollingListBox:new(x, y, width, height) setmetatable(o, self) self.__index = self self.parentCombo = nil return o end function ISComboBoxPopup:prerender() if not self.parentCombo:isReallyVisible() then -- Hack for gamepad being disconnected self:removeFromUIManager() return end self.tooWide = nil local numVisible = self:size() if self.parentCombo:hasFilterText() then local filterText = self.parentCombo:getFilterText():lower() for i=1,self:size() do local text = self.items[i].text:lower() if not text:contains(filterText) then numVisible = numVisible - 1 end end end -- numVisible = math.max(numVisible, 1) self:setScrollHeight(numVisible * self.itemheight) self:setHeight(math.min(numVisible, 8) * self.itemheight) self.vscroll:setHeight(self.height) ISScrollingListBox.prerender(self) end function ISComboBoxPopup:render() ISScrollingListBox.render(self) self:drawRectBorderStatic(-1, -1, self:getWidth() + 2, self:getHeight() + 2, 0.8, 1, 1, 1) if self.tooWide then local item = self.tooWide local y = self.tooWideY local textWid = getTextManager():MeasureStringX(self.font, item.text) local selectColor = self.parentCombo.backgroundColorMouseOver self:drawRect(0, y, 10 + textWid + 8, item.height-1, selectColor.a, selectColor.r, selectColor.g, selectColor.b) local itemPadY = self.itemPadY or (item.height - self.fontHgt) / 2 self:drawText(item.text, 10, y + itemPadY, self.parentCombo.textColor.r, self.parentCombo.textColor.g, self.parentCombo.textColor.b, self.parentCombo.textColor.a, self.font) end end function ISComboBoxPopup:doDrawItem(y, item, alt) if self.parentCombo:hasFilterText() then if not item.text:lower():contains(self.parentCombo:getFilterText():lower()) then return y end end if item.height == 0 then item.height = self.itemheight end local highlight = (self:isMouseOver() and not self:isMouseOverScrollBar()) and self.mouseoverselected or self.selected if self.parentCombo.joypadFocused then highlight = self.selected end if highlight == item.index then local selectColor = self.parentCombo.backgroundColorMouseOver self:drawRect(0, (y), self:getWidth(), item.height-1, selectColor.a, selectColor.r, selectColor.g, selectColor.b) if self:isMouseOver() and not self:isMouseOverScrollBar() then local textWid = getTextManager():MeasureStringX(self.font, item.text) local scrollBarWid = self:isVScrollBarVisible() and 13 or 0 if 10 + textWid > self.width - scrollBarWid then self.tooWide = item self.tooWideY = y end end end local itemPadY = self.itemPadY or (item.height - self.fontHgt) / 2 self:drawText(item.text, 10, y + itemPadY, self.parentCombo.textColor.r, self.parentCombo.textColor.g, self.parentCombo.textColor.b, self.parentCombo.textColor.a, self.font) y = y + item.height return y end function ISComboBoxPopup:onMouseDown(x, y) if self.parentCombo.disabled then return false; end if not self:isMouseOver() then -- due to setCapture() self.parentCombo.expanded = false self.parentCombo:hidePopup() return end return true end function ISComboBoxPopup:onMouseUp(x, y) if self.parentCombo.disabled then return; end if self.vscroll then self.vscroll.scrolling = false end if not self:isMouseOver() then -- due to setCapture() self.parentCombo.expanded = false self.parentCombo:hidePopup() return end if not self.joypadFocused then local row = self:rowAt(x, y) if row > #self.items then row = #self.items elseif row < 1 then row = 1 end self.parentCombo.selected = row self.parentCombo.expanded = false self.parentCombo:hidePopup() if self.parentCombo.onChange then self.parentCombo.onChange(self.parentCombo.target, self.parentCombo, self.parentCombo.onChangeArgs[1], self.parentCombo.onChangeArgs[2]) end end end function ISComboBoxPopup:setComboBox(comboBox) self:clear() for i=1,#comboBox.options do self:addItem(comboBox:getOptionText(i), nil) end self:setYScroll(0) self.selected = comboBox.selected self:setHeight(math.min(#comboBox.options, 8) * self.itemheight) self:setX(comboBox:getAbsoluteX()) self:setWidth(comboBox:getWidth()) if comboBox.openUpwards or (comboBox:getAbsoluteY() + comboBox:getHeight() + self:getHeight() > getCore():getScreenHeight()) then self:setY(comboBox:getAbsoluteY() - self:getHeight()) else self:setY(comboBox:getAbsoluteY() + comboBox:getHeight()) end self.borderColor = { r = comboBox.borderColor.r, g = comboBox.borderColor.g, b = comboBox.borderColor.b, a = comboBox.borderColor.a } self.backgroundColor = { r = comboBox.backgroundColor.r, g = comboBox.backgroundColor.g, b = comboBox.backgroundColor.b, a = comboBox.backgroundColor.a } self.parentCombo = comboBox self:ensureVisible(self.selected) end --************************************************************************-- --** ISComboBox:initialise --** --************************************************************************-- function ISComboBox:initialise() ISPanel.initialise(self); end function ISComboBox:createChildren() if ISComboBox.SharedPopup then self.popup = ISComboBox.SharedPopup else self.popup = ISComboBoxPopup:new(0, 0, 100, 50) self.popup:initialise() self.popup:instantiate() self.popup:setFont(self.font, 4) self.popup:setAlwaysOnTop(true) self.popup.drawBorder = true self.popup:setCapture(true) ISComboBox.SharedPopup = self.popup end end function ISComboBox:showPopup() getSoundManager():playUISound("UIToggleComboBox") self.popup:setFont(self.font, 4) self.popup:setComboBox(self) self.popup:addToUIManager() end function ISComboBox:hidePopup() getSoundManager():playUISound("UIToggleComboBox") self.popup:removeFromUIManager() if self.editor and self.editor:isVisible() then self.filterText = nil self.editor:unfocus() self.editor:setVisible(false) end end function ISComboBox:setJoypadFocused(focused) self.joypadFocused = focused; if self.expanded and not focused then self:hidePopup() self.expanded = false end end function ISComboBox:onJoypadDirUp(joypadData) self.popup:onJoypadDirUp(joypadData) end function ISComboBox:onJoypadDirDown(joypadData) self.popup:onJoypadDirDown(joypadData) end function ISComboBox:forceClick() if self.disabled then if self.expanded then self:hidePopup(); end self.expanded = false; return; end self.expanded = not self.expanded; if self.expanded then self:showPopup() -- self.joypadIndexFocused = self.selected else self:hidePopup() end if not self.expanded then self.selected = self.popup.selected; if self.onChange then self.onChange(self.target, self, self.onChangeArgs[1], self.onChangeArgs[2]); end end end function ISComboBox:prerender() if not self.disabled then self.fade:setFadeIn(self.joypadFocused or self:isMouseOver()) self.fade:update() end self:drawRect(0, 0, self.width, self.height, self.backgroundColor.a, self.backgroundColor.r, self.backgroundColor.g, self.backgroundColor.b); if self.expanded then elseif not self.joypadFocused then self:drawRect(0, 0, self.width, self.height, self.backgroundColorMouseOver.a * 0.5 * self.fade:fraction(), self.backgroundColorMouseOver.r, self.backgroundColorMouseOver.g, self.backgroundColorMouseOver.b); else self:drawRect(0, 0, self.width, self.height, self.backgroundColorMouseOver.a, self.backgroundColorMouseOver.r, self.backgroundColorMouseOver.g, self.backgroundColorMouseOver.b); end local alpha = math.min(self.borderColor.a + 0.2 * self.fade:fraction(), 1.0) if not self.disabled then self:drawRectBorder(0, 0, self.width, self.height, alpha, self.borderColor.r, self.borderColor.g, self.borderColor.b); else self:drawRectBorder(0, 0, self.width, self.height, alpha, 0.5, 0.5, 0.5); end local fontHgt = getTextManager():getFontHeight(self.font) local y = (self.height - fontHgt) / 2 if self:isEditable() and self.editor and self.editor:isReallyVisible() then -- editor is visible, don't draw text elseif self.options[self.selected] then self:clampStencilRectToParent(0, 0, self.width - self.image:getWidthOrig() - 6, self.height) if not self.disabled then self:drawText(self:getOptionText(self.selected), 10, y, self.textColor.r, self.textColor.g, self.textColor.b, self.textColor.a, self.font); else self:drawText(self:getOptionText(self.selected), 10, y, 0.6, 0.6, 0.6, 1, self.font); end self:clearStencilRect() elseif self.noSelectionText then self:clampStencilRectToParent(0, 0, self.width - self.image:getWidthOrig() - 6, self.height) self:drawText(self.noSelectionText, 10, y, self.textColor.r, self.textColor.g, self.textColor.b, self.textColor.a, self.font); self:clearStencilRect() end if self:isMouseOver() and not self.expanded and self:getOptionTooltip(self.selected) then local text = self:getOptionTooltip(self.selected) if not self.tooltipUI then self.tooltipUI = ISToolTip:new() self.tooltipUI:setOwner(self) self.tooltipUI:setVisible(false) self.tooltipUI:setAlwaysOnTop(true) end if not self.tooltipUI:getIsVisible() then if string.contains(text, "\n") then self.tooltipUI.maxLineWidth = 1000 -- don't wrap the lines else self.tooltipUI.maxLineWidth = 300 end self.tooltipUI:addToUIManager() self.tooltipUI:setVisible(true) end self.tooltipUI.description = text self.tooltipUI:setX(self:getMouseX() + 23) self.tooltipUI:setY(self:getMouseY() + 23) else if self.tooltipUI and self.tooltipUI:getIsVisible() then self.tooltipUI:setVisible(false) self.tooltipUI:removeFromUIManager() end end if not self.disabled then self:drawTexture(self.image, self.width - self.image:getWidthOrig() - 3, (self.baseHeight / 2) - (self.image:getHeight() / 2), 1, 1, 1, 1) else self:drawTexture(self.image, self.width - self.image:getWidthOrig() - 3, (self.baseHeight / 2) - (self.image:getHeight() / 2), 1, 0.5, 0.5, 0.5) end end function ISComboBox:onMouseDown(x, y) if self.disabled then return false; end self.sawMouseDown = true return true end --************************************************************************-- --** ISComboBox:onMouseUp --** --************************************************************************-- function ISComboBox:onMouseUp(x, y) if self.disabled or not self.sawMouseDown then return end self.sawMouseDown = false self.expanded = not self.expanded; if self:isEditable() then if not self.editor then local inset = 2 -- UITextBox2.getInset() local editorX = 10 - 2 local textY = (self.height - getTextManager():getFontHeight(self.font)) / 2 local editorY = textY - inset self.editor = ISComboBoxEditor:new(editorX, editorY, self.width - self.image:getWidthOrig() - 3, self.height, self) self.editor:initialise() self.editor:instantiate() self:addChild(self.editor) end self.editor:setWidth(self.width - self.image:getWidthOrig() - 3) if self.options[self.selected] then self.editor:setText(self:getOptionText(self.selected)) self.editor:selectAll() else self.editor:setText("") end self.editor:setVisible(true) self.editor:focus() end if self.expanded then self:showPopup(); else self:hidePopup(); self.mouseOver = self:isMouseOver() end end function ISComboBox:onMouseDownOutside(x, y) self.sawMouseDown = false if self.expanded then self.expanded = false; self:hidePopup() end end function ISComboBox:onMouseMove(dx, dy) self.mouseOver = true; end function ISComboBox:select(option) for i=1,#self.options do if option == self:getOptionText(i) then self.selected = i; return; end end end function ISComboBox:selectData(data) for i=1,#self.options do if data == self:getOptionData(i) then self.selected = i; return; end end end function ISComboBox:find(func, arg1, arg2) for i=1,#self.options do if func(self:getOptionText(i), self:getOptionData(i), arg1, arg2) then return i end end return -1 end function ISComboBox:addOption(option) table.insert(self.options, option); if self.selected == 0 then for i, k in ipairs(self.options) do self.selected = i; return; end end end function ISComboBox:addOptionWithData(option, data) local item = {} item.text = option item.data = data table.insert(self.options, item); if self.selected == 0 then self.selected = 1 end end function ISComboBox:contains(text) for i, k in ipairs(self.options) do if k.text == text then return true; end end return false; end function ISComboBox:getOptionText(index) local option = self.options[index] if option then if type(option) == "table" then return option.text else return option end end return nil end function ISComboBox:getOptionData(index) local option = self.options[index] if type(option) == "table" then return option.data end return nil end function ISComboBox:getOptionTooltip(index) local option = self.options[index] if option then if type(option) == "table" then if option.tooltip then return option.tooltip end if self.tooltip then return self.tooltip[option.text] or self.tooltip["defaultTooltip"] end else if self.tooltip then return self.tooltip[option] or self.tooltip["defaultTooltip"] end end end return nil end function ISComboBox:onMouseMoveOutside(dx, dy) self.mouseOver = false; end function ISComboBox:setToolTipMap(tooltipmap) self.tooltip = tooltipmap; end function ISComboBox:getSelectedText() return self:getOptionText(self.selected); end function ISComboBox:setWidthToOptions(minWidth) local width = 0 for i=1,#self.options do local text = self:getOptionText(i) width = math.max(width, getTextManager():MeasureStringX(self.font, text or "")) end width = 10 + width + 5 + self.image:getWidthOrig() + 3 self:setWidth(math.max(width, minWidth or 0)) end function ISComboBox:clear() self.options = {} self.tooltip = {} end function ISComboBox:setEditable(editable) self.editable = editable end function ISComboBox:isEditable() return self.editable == true end function ISComboBox:setFilterText(text) text = text and text:trim() or nil if text == "" then text = nil end self.filterText = text end function ISComboBox:getFilterText() return self:isEditable() and self.filterText or nil end function ISComboBox:hasFilterText() local filterText = self:getFilterText() return (filterText ~= nil) and (self.filterText:trim() ~= "") end function ISComboBox:new (x, y, width, height, target, onChange, onChangeArg1, onChangeArg2) local o = {} --o.data = {} o = ISPanel:new(x, y, width, height); setmetatable(o, self) self.__index = self o.x = x; o.y = y; o.backgroundColor = {r=0, g=0, b=0, a=1}; o.backgroundColorMouseOver = {r=0.3, g=0.3, b=0.3, a=1.0}; o.borderColor = {r=1, g=1, b=1, a=0.5}; o.font = UIFont.Small o.textColor = {r=0.9, g=0.9, b=0.9, a=0.9}; o.width = width; o.height = height; o.baseHeight = height; o.anchorLeft = true; o.anchorRight = false; o.anchorTop = true; o.anchorBottom = false; o.target = target; o.onChange = onChange; o.onChangeArgs = { onChangeArg1, onChangeArg2 } o.options = {}; o.selected = 0; o.expanded = false; o.mouseOver = false; o.tooltip = nil; o.isCombobox = true; o.disabled = false; -- o.joypadIndexFocused = 1; o.image = getTexture("media/ui/ArrowDown.png"); o.fade = UITransition.new() o.editable = false return o end -- Event method to check game mode and create K&B tutorial panel if necessary. testcomboBox = function () --print("start testing combobox"); local panel2 = ISComboBox:new(150, 0, 100, 18, nil); --print("panel2 newed"); panel2:initialise(); --print("panel2 initialised"); panel2:addOption("1024x768"); panel2:addOption("1920x1080"); panel2:addToUIManager(); --print("panel2 added ToUIManager"); --panel2 = ISInventoryPage:new(300, 300, 400+32, 400, getPlayer():getInventory()); --panel2:initialise(); --panel2:addToUIManager(); end --Events.OnMainMenuEnter.Add(testcomboBox);