--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] local panelMap = { [ix.type.bool] = "ixSettingsRowBool", [ix.type.array] = "ixSettingsRowArray", [ix.type.string] = "ixSettingsRowString", [ix.type.number] = "ixSettingsRowNumber", [ix.type.color] = "ixSettingsRowColor" } local function EmitChange(pitch) LocalPlayer():EmitSound("weapons/ar2/ar2_empty.wav", 75, pitch or 150, 0.25) end -- color setting local PANEL = {} AccessorFunc(PANEL, "padding", "Padding", FORCE_NUMBER) function PANEL:Init() self.color = table.Copy(color_white) self.padding = SScaleMin ( 4 / 3 ) self.panel = self:Add("Panel") self.panel:SetCursor("hand") self.panel:SetMouseInputEnabled(true) self.panel:Dock(RIGHT) self.panel.Paint = function(panel, width, height) local padding = self.padding surface.SetDrawColor(derma.GetColor("DarkerBackground", self)) surface.DrawRect(0, 0, width, height) surface.SetDrawColor(self.color) surface.DrawRect(padding, padding, width - padding * 2, height - padding * 2) end self.panel.OnMousePressed = function(panel, key) if (key == MOUSE_LEFT) then self:OpenPicker() end end end function PANEL:OpenPicker() if (IsValid(self.picker)) then self.picker:Remove() return end self.picker = vgui.Create("ixSettingsRowColorPicker") self.picker:Attach(self) self.picker:SetValue(self.color) self.picker.OnValueChanged = function(panel) local newColor = panel:GetValue() if (newColor != self.color) then self.color = newColor self:OnValueChanged(newColor) end end self.picker.OnValueUpdated = function(panel) self.color = panel:GetValue() self:OnValueChanged(self.color) end end function PANEL:SetValue(value) self.color = Color(value.r or 255, value.g or 255, value.b or 255, value.a or 255) end function PANEL:GetValue() return self.color end function PANEL:PerformLayout(width, height) surface.SetFont("TitlesFontNoBoldNoClamp") local totalWidth = surface.GetTextSize("999") self.panel:SetSize(totalWidth + self.padding * 2, height) end vgui.Register("ixSettingsRowColor", PANEL, "ixSettingsRow") -- color setting picker DEFINE_BASECLASS("Panel") PANEL = {} AccessorFunc(PANEL, "bDeleteSelf", "DeleteSelf", FORCE_BOOL) function PANEL:Init() self.m_bIsMenuComponent = true self.bDeleteSelf = true self.realHeight = SScaleMin ( 200 / 3 ) self.height = SScaleMin ( 200 / 3 ) self:SetSize(SScaleMin ( 250 / 3 ), SScaleMin ( 200 / 3 )) self:DockPadding(SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 )) self.picker = self:Add("DColorMixer") self.picker:Dock(FILL) self.picker.ValueChanged = function() self:OnValueUpdated() end self:MakePopup() RegisterDermaMenuForClose(self) end function PANEL:SetValue(value) self.picker:SetColor(Color(value.r or 255, value.g or 255, value.b or 255, value.a or 255)) end function PANEL:GetValue() return self.picker:GetColor() end function PANEL:Attach(panel) self.attached = panel end function PANEL:Think() local panel = self.attached if (IsValid(panel)) then local width, height = self:GetSize() local x, y = panel:LocalToScreen(0, 0) self:SetPos( math.Clamp(x + panel:GetWide() - width, 0, ScrW() - width), math.Clamp(y + panel:GetTall(), 0, ScrH() - height) ) end end function PANEL:Paint(width, height) surface.SetDrawColor(derma.GetColor("DarkerBackground", self)) surface.DrawRect(0, 0, width, height) end function PANEL:OnValueChanged() end function PANEL:OnValueUpdated() end function PANEL:Remove() if (self.bClosing) then return end self:OnValueChanged() -- @todo open/close animations self.bClosing = true self:SetMouseInputEnabled(false) self:SetKeyboardInputEnabled(false) BaseClass.Remove(self) end vgui.Register("ixSettingsRowColorPicker", PANEL, "EditablePanel") -- number setting PANEL = {} function PANEL:Init() self.setting = self:Add("ixNumSlider") self.setting.nextUpdate = 0 self.setting:Dock(RIGHT) self.setting.OnValueChanged = function(panel) self:OnValueChanged(self:GetValue()) end self.setting.OnValueUpdated = function(panel) local fraction = panel:GetFraction() if (fraction == 0) then EmitChange(75) return elseif (fraction == 1) then EmitChange(120) return end if (SysTime() > panel.nextUpdate) then EmitChange(85 + fraction * 15) panel.nextUpdate = SysTime() + 0.05 end end local panel = self.setting:GetLabel() panel:SetCursor("hand") panel:SetMouseInputEnabled(true) panel.OnMousePressed = function(_, key) if (key == MOUSE_LEFT) then self:OpenEntry() end end end function PANEL:OpenEntry() if (IsValid(self.entry)) then self.entry:Remove() return end self.entry = vgui.Create("ixSettingsRowNumberEntry") self.entry:Attach(self) self.entry:SetValue(self:GetValue(), true) self.entry.OnValueChanged = function(panel) local value = math.Round(panel:GetValue(), self:GetDecimals()) if (value != self:GetValue()) then self:SetValue(value, true) self:OnValueChanged(value) end end end function PANEL:SetValue(value, bNoNotify) self.setting:SetValue(value, bNoNotify) end function PANEL:GetValue() return self.setting:GetValue() end function PANEL:SetMin(value) self.setting:SetMin(value) end function PANEL:SetMax(value) self.setting:SetMax(value) end function PANEL:SetDecimals(value) self.setting:SetDecimals(value) end function PANEL:GetDecimals() return self.setting:GetDecimals() end function PANEL:PerformLayout(width, height) self.setting:SetWide(width * 0.5) end vgui.Register("ixSettingsRowNumber", PANEL, "ixSettingsRow") -- number setting entry DEFINE_BASECLASS("Panel") PANEL = {} AccessorFunc(PANEL, "bDeleteSelf", "DeleteSelf", FORCE_BOOL) function PANEL:Init() surface.SetFont("TitlesFontNoBoldNoClamp") local width, height = surface.GetTextSize("999999") self.m_bIsMenuComponent = true self.bDeleteSelf = true self.realHeight = SScaleMin ( 200 / 3 ) self.height = SScaleMin ( 200 / 3 ) self:SetSize(width, height) self:DockPadding(SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 )) self.textEntry = self:Add("ixTextEntry") self.textEntry:SetNumeric(true) self.textEntry:SetFont("TitlesFontNoBoldNoClamp") self.textEntry:Dock(FILL) self.textEntry:RequestFocus() self.textEntry.OnEnter = function() self:Remove() end self:MakePopup() RegisterDermaMenuForClose(self) end function PANEL:SetValue(value, bInitial) value = tostring(value) self.textEntry:SetValue(value) if (bInitial) then self.textEntry:SetCaretPos(value:utf8len()) end end function PANEL:GetValue() return tonumber(self.textEntry:GetValue()) or 0 end function PANEL:Attach(panel) self.attached = panel end function PANEL:Think() local panel = self.attached if (IsValid(panel)) then local width, height = self:GetSize() local x, y = panel:LocalToScreen(0, 0) self:SetPos( math.Clamp(x + panel:GetWide() - width, 0, ScrW() - width), math.Clamp(y + panel:GetTall(), 0, ScrH() - height) ) end end function PANEL:Paint(width, height) surface.SetDrawColor(derma.GetColor("DarkerBackground", self)) surface.DrawRect(0, 0, width, height) end function PANEL:OnValueChanged() end function PANEL:OnValueUpdated() end function PANEL:Remove() if (self.bClosing) then return end self:OnValueChanged() -- @todo open/close animations self.bClosing = true self:SetMouseInputEnabled(false) self:SetKeyboardInputEnabled(false) BaseClass.Remove(self) end vgui.Register("ixSettingsRowNumberEntry", PANEL, "EditablePanel") -- string setting PANEL = {} function PANEL:Init() self.setting = self:Add("ixTextEntry") self.setting:Dock(RIGHT) self.setting:SetFont("TitlesFontNoBoldNoClamp") self.setting:SetBackgroundColor(derma.GetColor("DarkerBackground", self)) self.setting.OnEnter = function() self:OnValueChanged(self:GetValue()) end end function PANEL:SetValue(value) self.setting:SetValue(tostring(value)) end function PANEL:GetValue() return self.setting:GetValue() end function PANEL:PerformLayout(width, height) self.setting:SetWide(width * 0.5) end vgui.Register("ixSettingsRowString", PANEL, "ixSettingsRow") -- bool setting PANEL = {} function PANEL:Init() self.setting = self:Add("ixCheckBox") self.setting:Dock(RIGHT) self.setting.DoClick = function(panel) self:OnValueChanged(self:GetValue()) end end function PANEL:SetValue(bValue) bValue = tobool(bValue) self.setting:SetChecked(bValue, true) end function PANEL:GetValue() return self.setting:GetChecked() end vgui.Register("ixSettingsRowBool", PANEL, "ixSettingsRow") -- array setting PANEL = {} function PANEL:Init() self.array = {} self.setting = self:Add("DComboBox") self.setting:Dock(RIGHT) self.setting:SetFont("TitlesFontNoBoldNoClamp") self.setting:SetTextColor(color_white) self.setting.OnSelect = function(panel) self:OnValueChanged(self:GetValue()) panel:SizeToContents() panel:SetWide(panel:GetWide() + SScaleMin ( 12 / 3 )) -- padding for arrow (nice) if (!self.bInitial) then EmitChange() end end end function PANEL:Populate(key, info) if (!isfunction(info.populate)) then ErrorNoHalt(string.format("expected populate function for array option '%s'", key)) return end local entries = info.populate() local i = 1 for k, v in pairs(entries) do self.setting:AddChoice(v, k) self.array[k] = i i = i + 1 end end function PANEL:SetValue(value) --quick fix if (!self.array[value]) then return end self.bInitial = true self.setting:ChooseOptionID(self.array[value]) self.bInitial = false end function PANEL:GetValue() return select(2, self.setting:GetSelected()) end vgui.Register("ixSettingsRowArray", PANEL, "ixSettingsRow") -- settings row PANEL = {} AccessorFunc(PANEL, "backgroundIndex", "BackgroundIndex", FORCE_NUMBER) AccessorFunc(PANEL, "bShowReset", "ShowReset", FORCE_BOOL) function PANEL:Init() self:DockPadding(SScaleMin ( 10 / 3 ), SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 ), SScaleMin ( 4 / 3 )) self.text = self:Add("DLabel") self.text:Dock(LEFT) self.text:SetFont("TitlesFontNoBoldNoClamp") self.text:SetExpensiveShadow(1, color_black) self.backgroundIndex = 0 end function PANEL:SetShowReset(value, name, default) value = tobool(value) if (value and !IsValid(self.reset)) then self.reset = self:Add("DButton") self.reset:SetFont("TitlesFontNoBoldNoClamp") self.reset:SetText("Réinitialiser") self.reset:SetTextColor(ColorAlpha(derma.GetColor("Warning", self), 100)) self.reset:Dock(LEFT) self.reset:DockMargin(SScaleMin ( 4 / 3 ), 0, 0, 0) self.reset:SizeToContents() self.reset.Paint = nil self.reset.DoClick = function() self:OnResetClicked() end self.reset:SetHelixTooltip(function(tooltip) local title = tooltip:AddRow("title") title:SetImportant() title:SetText(L("resetDefault")) title:SetBackgroundColor(derma.GetColor("Warning", self)) title:SizeToContents() local description = tooltip:AddRow("description") description:SetText(L("resetDefaultDescription", tostring(name), tostring(default))) description:SizeToContents() end) elseif (!value and IsValid(self.reset)) then self.reset:Remove() end self.bShowReset = value end function PANEL:Think() if (IsValid(self.reset)) then self.reset:SetVisible(self:IsHovered() or self:IsOurChild(vgui.GetHoveredPanel())) end end function PANEL:OnResetClicked() end function PANEL:GetLabel() return self.text end function PANEL:SetText(text) self.text:SetText(text) self:SizeToContents() end function PANEL:GetText() return self.text:GetText() end -- implemented by row types function PANEL:GetValue() end function PANEL:SetValue(value) end -- meant for array types to populate combo box values function PANEL:Populate(key, info) end -- called when value is changed by user function PANEL:OnValueChanged(newValue) end function PANEL:SizeToContents() local _, top, _, bottom = self:GetDockPadding() self.text:SizeToContents() self:SetTall(self.text:GetTall() + top + bottom) self.ixRealHeight = self:GetTall() self.ixHeight = self.ixRealHeight end function PANEL:Paint(width, height) derma.SkinFunc("PaintSettingsRowBackground", self, width, height) end vgui.Register("ixSettingsRow", PANEL, "EditablePanel") -- settings panel PANEL = {} function PANEL:Init() local titlePushDown = SScaleMin(30 / 3) local topPushDown = SScaleMin(150 / 3) local scale780 = SScaleMin(780 / 3) local scale120 = SScaleMin(120 / 3) self:SetWide(ScrW() - (topPushDown * 2)) local sizeXtitle, sizeYtitle = self:GetWide(), scale120 local sizeXcontent, sizeYcontent = self:GetWide(), (scale780) self.titlePanel = self:Add("Panel") self.titlePanel:SetSize(sizeXtitle, sizeYtitle) self.titlePanel:SetPos(self:GetWide() * 0.5 - self.titlePanel:GetWide() * 0.5) self:CreateTitleText() self.contentFrame = self:Add("Panel") self.contentFrame:SetSize(sizeXcontent, sizeYcontent) self.contentFrame:SetPos(self:GetWide() * 0.5 - self.contentFrame:GetWide() * 0.5, titlePushDown) self:SetTall(scale120 + scale780 + titlePushDown) self:Center() self.rows = {} self.categories = {} -- scroll panel DEFINE_BASECLASS("DScrollPanel") self.canvas = self.contentFrame:Add("DScrollPanel") self.canvas:Dock(FILL) self.canvas.PerformLayout = function(panel) BaseClass.PerformLayout(panel) if (!panel.VBar.Enabled) then panel.pnlCanvas:SetWide(panel:GetWide() - panel.VBar:GetWide()) end end end function PANEL:CreateTitleText() self.settingsTitleIcon = self.titlePanel:Add("DImage") self.settingsTitleIcon:SetImage("willardnetworks/tabmenu/navicons/settings.png") self.settingsTitleIcon:SetSize(SScaleMin(20 / 3), SScaleMin(16 / 3)) self.settingsTitle = self.titlePanel:Add("DLabel") self.settingsTitle:SetFont("TitlesFontNoClamp") self.settingsTitle:SetText("Options") self.settingsTitle:SizeToContents() self.settingsTitle:SetPos(SScaleMin(32 / 3), self.settingsTitleIcon:GetTall() * 0.5 - self.settingsTitle:GetTall() * 0.5) end function PANEL:GetRowPanelName(type) return panelMap[type] or "ixSettingsRow" end function PANEL:AddCategory(name) local panel = self.categories[name] if (!IsValid(panel)) then panel = self.canvas:Add("ixCategoryPanel") panel:SetText(name) panel:Dock(TOP) panel:DockMargin(0, SScaleMin(8 / 3), 0, 0) self.categories[name] = panel return panel end end function PANEL:AddRow(type, category) category = self.categories[category] local id = panelMap[type] if (!id) then ErrorNoHalt("attempted to create row with unimplemented type '" .. tostring(ix.type[type]) .. "'\n") id = "ixSettingsRow" end local panel = (IsValid(category) and category or self.canvas):Add(id) panel:Dock(TOP) panel:SetBackgroundIndex(#self.rows % 2) self.rows[#self.rows + 1] = panel return panel end function PANEL:GetRows() return self.rows end function PANEL:Clear() for _, v in ipairs(self.rows) do if (IsValid(v)) then v:Remove() end end self.rows = {} end function PANEL:SetSearchEnabled(bValue) if (!bValue) then if (IsValid(self.searchEntry)) then self.searchEntry:Remove() end return end -- search entry self.searchEntry = self.contentFrame:Add("ixIconTextEntry") self.searchEntry:Dock(TOP) self.searchEntry:SetEnterAllowed(false) self.searchEntry.OnChange = function(entry) self:FilterRows(entry:GetValue()) end end function PANEL:FilterRows(query) query = string.PatternSafe(query:utf8lower()) local bEmpty = query == "" for categoryName, category in pairs(self.categories) do category.size = 0 category:CreateAnimation(0.5, { index = 21, target = {size = 1}, Think = function(animation, panel) panel:SizeToContents() end }) for _, row in ipairs(category:GetChildren()) do local bFound = bEmpty or row:GetText():utf8lower():find(query) or categoryName:utf8lower():find(query) row:SetVisible(true) row:CreateAnimation(0.5, { index = 21, target = {ixHeight = bFound and row.ixRealHeight or 0}, easing = "outQuint", Think = function(animation, panel) panel:SetTall(bFound and math.min(panel.ixHeight + SScaleMin ( 2 / 3 ), panel.ixRealHeight) or math.max(panel.ixHeight - SScaleMin ( 2 / 3 ), 0)) end, OnComplete = function(animation, panel) panel:SetVisible(bFound) -- need this so categories are sized properly when animations are disabled - there is no guaranteed order -- that animations will think so we SizeToContents here. putting it here will result in redundant calls but -- I guess we have the performance to spare if (ix.option.Get("disableAnimations", false)) then category:SizeToContents() end end }) end end end function PANEL:Paint(width, height) end function PANEL:SizeToContents() for _, v in pairs(self.categories) do v:SizeToContents() end end vgui.Register("ixSettings", PANEL, "Panel") hook.Add("CreateMenuButtons", "ixSettings", function(tabs) tabs["Options"] = { RowNumber = 5, Width = 20, Height = 16, Right = true, Icon = "willardnetworks/tabmenu/navicons/settings.png", Create = function(info, container) local panel = container:Add("ixSettings") panel:SetSearchEnabled(true) for category, options in SortedPairs(ix.option.GetAllByCategories(true)) do category = L(category) panel:AddCategory(category) -- sort options by language phrase rather than the key table.sort(options, function(a, b) return L(a.phrase) < L(b.phrase) end) for _, data in pairs(options) do local key = data.key local row = panel:AddRow(data.type, category) local value = ix.util.SanitizeType(data.type, ix.option.Get(key)) row:SetText(L(data.phrase)) row:Populate(key, data) -- type-specific properties if (data.type == ix.type.number) then row:SetMin(data.min or 0) row:SetMax(data.max or 10) row:SetDecimals(data.decimals or 0) end row:SetValue(value, true) row:SetShowReset(value != data.default, key, data.default) row.OnValueChanged = function() local newValue = row:GetValue() row:SetShowReset(newValue != data.default, key, data.default) ix.option.Set(key, newValue) end row.OnResetClicked = function() row:SetShowReset(false) row:SetValue(data.default, true) ix.option.Set(key, data.default) end row:GetLabel():SetHelixTooltip(function(tooltip) local title = tooltip:AddRow("name") title:SetImportant() title:SetText(key) title:SizeToContents() title:SetMaxWidth(math.max(title:GetMaxWidth(), ScrW() * 0.5)) local description = tooltip:AddRow("description") description:SetText(L(data.description)) description:SizeToContents() end) end end panel:SizeToContents() container.panel = panel end, OnSelected = function(info, container) container.panel.searchEntry:RequestFocus() end } end)