457 lines
19 KiB
Lua
457 lines
19 KiB
Lua
-- TotemBar - options.lua
|
|
-- Standalone options panel (no Blizzard InterfaceOptions integration). Built
|
|
-- once (lazy) on first ToggleOptions; repopulated from TotemBarDB on show.
|
|
-- Opened by the minimap button's left-click and by /tb options. WoW-API
|
|
-- file (parse-checked only). Widget wiring uses 1.12 templates:
|
|
-- UICheckButtonTemplate (GetChecked() -> 1/nil) and OptionsSliderTemplate
|
|
-- (SetMinMaxValues before SetValue; $parentLow/$parentHigh/$parentText).
|
|
|
|
TotemBar = TotemBar or {}
|
|
|
|
local ChatOut = DEFAULT_CHAT_FRAME or ChatFrame1
|
|
local optionsFrame = nil
|
|
|
|
-- Applies pfUI's font to a FontString (the main "matches pfUI" tweak) at the
|
|
-- given size, or falls back to a small game font when pfUI is absent.
|
|
local function ApplyFont(fontString, size)
|
|
if not fontString then
|
|
return
|
|
end
|
|
if pfUI and pfUI.font_default then
|
|
local sz = size or (pfUI_config and pfUI_config.global and pfUI_config.global.font_size) or 12
|
|
fontString:SetFont(pfUI.font_default, sz)
|
|
else
|
|
fontString:SetFontObject(GameFontNormalSmall)
|
|
end
|
|
end
|
|
|
|
-- Adds a wrapped explanatory GameTooltip on hover. Chains any existing
|
|
-- OnEnter/OnLeave (e.g. pfUI's SkinButton hover-highlight) instead of
|
|
-- replacing it, so skinning still works. No HookScript on 1.12.
|
|
local function AddTooltip(widget, text)
|
|
if not widget or not text then
|
|
return
|
|
end
|
|
widget.tbTip = text
|
|
local oldEnter = widget:GetScript("OnEnter")
|
|
local oldLeave = widget:GetScript("OnLeave")
|
|
widget:SetScript("OnEnter", function()
|
|
if oldEnter then oldEnter() end
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(this.tbTip, 1, 1, 1, 1, 1)
|
|
GameTooltip:Show()
|
|
end)
|
|
widget:SetScript("OnLeave", function()
|
|
if oldLeave then oldLeave() end
|
|
GameTooltip:Hide()
|
|
end)
|
|
end
|
|
|
|
-- Unique widget-name counters (template child labels resolve via getglobal,
|
|
-- so every widget needs a non-nil, unique frame name).
|
|
local cbIndex = 0
|
|
local slIndex = 0
|
|
|
|
-- Factory: a labeled checkbox wired to getter/setter. getter()->boolean,
|
|
-- setter(boolean). Returns the CheckButton.
|
|
local function CreateCheckbox(parent, label, getter, setter)
|
|
cbIndex = cbIndex + 1
|
|
local name = "TotemBarOptCheck" .. cbIndex
|
|
local cb = CreateFrame("CheckButton", name, parent, "UICheckButtonTemplate")
|
|
cb:SetWidth(24)
|
|
cb:SetHeight(24)
|
|
local lbl = getglobal(name .. "Text")
|
|
if lbl then
|
|
lbl:SetText(label)
|
|
ApplyFont(lbl)
|
|
end
|
|
cb.tbGet = getter
|
|
cb.tbSet = setter
|
|
cb:SetScript("OnClick", function()
|
|
this.tbSet(this:GetChecked() == 1)
|
|
end)
|
|
if pfUI and pfUI.api and pfUI.api.SkinCheckbox then
|
|
pfUI.api.SkinCheckbox(cb)
|
|
end
|
|
return cb
|
|
end
|
|
|
|
-- Factory: a labeled slider wired to getter/setter over [minVal,maxVal] in
|
|
-- `step` increments. `fmt` is a printf pattern for the live value text
|
|
-- (e.g. "UI size: %.2f"). Returns the Slider.
|
|
local function CreateSlider(parent, label, minVal, maxVal, step, fmt, getter, setter)
|
|
slIndex = slIndex + 1
|
|
local name = "TotemBarOptSlider" .. slIndex
|
|
local sl = CreateFrame("Slider", name, parent, "OptionsSliderTemplate")
|
|
sl:SetWidth(200)
|
|
sl:SetHeight(16)
|
|
sl:SetMinMaxValues(minVal, maxVal) -- BEFORE SetValue, else clamps to 0
|
|
sl:SetValueStep(step)
|
|
local low, high, txt = getglobal(name .. "Low"), getglobal(name .. "High"), getglobal(name .. "Text")
|
|
if low then low:SetText(tostring(minVal)); ApplyFont(low) end
|
|
if high then high:SetText(tostring(maxVal)); ApplyFont(high) end
|
|
if txt then
|
|
-- Left-align the value label above the slider (the template centers it),
|
|
-- so it lines up with the left-aligned checkbox labels. pfUI font.
|
|
txt:ClearAllPoints()
|
|
txt:SetPoint("BOTTOMLEFT", sl, "TOPLEFT", 0, 2)
|
|
txt:SetJustifyH("LEFT")
|
|
ApplyFont(txt)
|
|
end
|
|
sl.tbLabel = label
|
|
sl.tbFmt = fmt
|
|
sl.tbText = txt
|
|
sl.tbSet = setter
|
|
sl.tbGet = getter
|
|
sl:SetScript("OnValueChanged", function()
|
|
local v = this:GetValue()
|
|
if this.tbText then
|
|
this.tbText:SetText(string.format(this.tbFmt, v))
|
|
end
|
|
this.tbSet(v)
|
|
end)
|
|
if pfUI and pfUI.api and pfUI.api.SkinSlider then
|
|
pfUI.api.SkinSlider(sl)
|
|
end
|
|
return sl
|
|
end
|
|
|
|
-- Label text for the bar-layout cycle button, reflecting the current choice.
|
|
local function BarLayoutLabel()
|
|
return "Layout: " .. (TotemBarDB.barLayout or "1x6")
|
|
end
|
|
|
|
-- Factory: a labeled push button wired to an onClick. Returns the Button.
|
|
-- Named (project convention: name all frames) so the pfDebug profiler can attribute it.
|
|
local function CreateButton(parent, name, label, onClick)
|
|
local btn = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
|
|
btn:SetWidth(140)
|
|
btn:SetHeight(22)
|
|
btn:SetText(label)
|
|
btn:SetScript("OnClick", onClick)
|
|
if pfUI and pfUI.api and pfUI.api.SkinButton then
|
|
pfUI.api.SkinButton(btn)
|
|
end
|
|
return btn
|
|
end
|
|
|
|
-- Factory: a static hairline divider spanning the panel's content width at
|
|
-- cursor position y (Rev 4 UI chrome, section-break decoration). Anonymous
|
|
-- texture - it's a static decoration, not an interactive/named widget.
|
|
local function CreateDivider(parent, y)
|
|
local d = parent:CreateTexture(nil, "ARTWORK")
|
|
d:SetTexture(TotemBar.UI_TEXTURE)
|
|
local c = TotemBar.uiCoords[TotemBar.UI_DIVIDER_CELL]
|
|
d:SetTexCoord(c.l, c.r, c.t, c.b)
|
|
d:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, y)
|
|
d:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, y)
|
|
d:SetHeight(7)
|
|
return d
|
|
end
|
|
|
|
-- All widgets, stored so OnShow can repopulate them from TotemBarDB.
|
|
local widgets = {}
|
|
|
|
local function BuildOptionsFrame()
|
|
if optionsFrame then
|
|
return optionsFrame
|
|
end
|
|
|
|
local f = CreateFrame("Frame", "TotemBarOptionsFrame", UIParent)
|
|
f:SetWidth(280)
|
|
-- 472 + 44 + 28 + 28 + 44 + 28: room for the header emblem (+16, taller
|
|
-- than the old title-only header), two section dividers (+14 each), the
|
|
-- "Show pulse waves" checkbox row (+28, the pulse ring's countdown
|
|
-- readout sits alongside the ripple), the bar-layout cycle button row
|
|
-- (+28), the "Button spacing" slider row (+44, same slider-row
|
|
-- height as guard/gap/scale above it), and the "Show drop-all button"
|
|
-- checkbox row (+28) - see the layout-cursor deltas below (same
|
|
-- mechanism as the Task-5 height bump). Not cosmetic: without the last
|
|
-- +28 the "Key bind mode" button would land on the version footer.
|
|
f:SetHeight(628)
|
|
f:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
|
f:SetFrameStrata("DIALOG")
|
|
-- Rev 4 UI chrome: shared bespoke panel skin (see ui.lua's PANEL_BACKDROP)
|
|
-- instead of the Blizzard dialog backdrop or pfUI's generic skin, so the
|
|
-- options panel matches the bar's own art (emblem/dividers below pair
|
|
-- with this border/bg specifically).
|
|
f:SetBackdrop(TotemBar.PANEL_BACKDROP)
|
|
f:SetBackdropColor(1, 1, 1, 0.97)
|
|
f:SetMovable(true)
|
|
f:EnableMouse(true)
|
|
f:RegisterForDrag("LeftButton")
|
|
f:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
|
f:SetClampedToScreen(true)
|
|
|
|
-- Plain gold title, no emblem (the Rev-4 totem glyph was removed on
|
|
-- user request - the silhouette read, well, unfortunate).
|
|
local title = f:CreateFontString("TotemBarOptionsTitle", "OVERLAY", "GameFontNormal")
|
|
title:SetPoint("TOPLEFT", f, "TOPLEFT", 16, -16)
|
|
title:SetText("TotemBar Options")
|
|
ApplyFont(title, 14)
|
|
title:SetTextColor(0.85, 0.66, 0.31)
|
|
|
|
local close = CreateFrame("Button", "TotemBarOptionsClose", f, "UIPanelCloseButton")
|
|
close:SetPoint("TOPRIGHT", f, "TOPRIGHT", -8, -8)
|
|
if pfUI and pfUI.api and pfUI.api.SkinCloseButton then
|
|
pfUI.api.SkinCloseButton(close, f)
|
|
end
|
|
|
|
-- Layout cursor (back to the title-only header spacing).
|
|
local x = 24
|
|
local y = -44
|
|
local function place(w, dy)
|
|
w:SetPoint("TOPLEFT", f, "TOPLEFT", x, y)
|
|
y = y + dy
|
|
end
|
|
|
|
-- Checkboxes.
|
|
widgets.lock = CreateCheckbox(f, "Lock bar",
|
|
function() return TotemBarDB.locked end,
|
|
function(v) TotemBarDB.locked = v end)
|
|
place(widgets.lock, -28)
|
|
AddTooltip(widgets.lock, "Locks the bar in place so you can't drag it by accident. Uncheck to reposition it.")
|
|
|
|
widgets.autoRecall = CreateCheckbox(f, "Auto-recall before setting",
|
|
function() return TotemBarDB.autoRecall end,
|
|
function(v)
|
|
TotemBarDB.autoRecall = v
|
|
if TotemBar.RefreshRecallIndicator then TotemBar.RefreshRecallIndicator() end
|
|
end)
|
|
place(widgets.autoRecall, -28)
|
|
AddTooltip(widgets.autoRecall, "When on, the Totems macro casts Totemic Recall before re-dropping your totems (refunds some mana when relocating).")
|
|
|
|
widgets.show = CreateCheckbox(f, "Show bar",
|
|
function() return not TotemBarDB.hidden end,
|
|
function(v)
|
|
if v then
|
|
TotemBarDB.hidden = false
|
|
if TotemBarFrame then TotemBarFrame:Show() end
|
|
else
|
|
TotemBarDB.hidden = true
|
|
if TotemBarFrame then TotemBarFrame:Hide() end
|
|
end
|
|
end)
|
|
place(widgets.show, -28)
|
|
AddTooltip(widgets.show, "Shows or hides the totem bar. The choice is remembered between sessions.")
|
|
|
|
-- Same kind of switch as "Show bar" above (visibility only, not a
|
|
-- feature toggle), hence the same group. ApplyBarLayout hides/shows the
|
|
-- button AND re-lays out the grid, so the bar resizes immediately -
|
|
-- no /reload needed.
|
|
widgets.dropSet = CreateCheckbox(f, "Show drop-all button",
|
|
function() return TotemBarDB.showDropSetButton end,
|
|
function(v)
|
|
TotemBarDB.showDropSetButton = v
|
|
if TotemBar.ApplyBarLayout then TotemBar.ApplyBarLayout() end
|
|
end)
|
|
place(widgets.dropSet, -40)
|
|
AddTooltip(widgets.dropSet, "Shows or hides the 'Drop all totems' button on the bar. Its key binding keeps working while it is hidden - rebind it under Key Bindings in the game menu.")
|
|
|
|
widgets.ring = CreateCheckbox(f, "Show duration ring",
|
|
function() return TotemBarDB.showDurationRing end,
|
|
function(v)
|
|
TotemBarDB.showDurationRing = v
|
|
if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
|
|
end)
|
|
place(widgets.ring, -28)
|
|
AddTooltip(widgets.ring, "Shows a colored arc on each button's ring that empties as the totem's remaining time runs out. The ring frame itself always stays visible.")
|
|
|
|
widgets.pulse = CreateCheckbox(f, "Show pulse ring",
|
|
function() return TotemBarDB.showPulseBars end,
|
|
function(v)
|
|
TotemBarDB.showPulseBars = v
|
|
if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
|
|
end)
|
|
place(widgets.pulse, -28)
|
|
AddTooltip(widgets.pulse, "Shows a thin inner ring around the icon that fills up toward the totem's next pulse, then resets.")
|
|
|
|
widgets.pulseWaves = CreateCheckbox(f, "Show pulse waves",
|
|
function() return TotemBarDB.showPulseWaves end,
|
|
function(v)
|
|
TotemBarDB.showPulseWaves = v
|
|
if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
|
|
end)
|
|
place(widgets.pulseWaves, -28)
|
|
AddTooltip(widgets.pulseWaves, "Shows an expanding ripple in the totem's color each time a pulsing totem fires.")
|
|
|
|
widgets.timerText = CreateCheckbox(f, "Show countdown text",
|
|
function() return TotemBarDB.showTimerText end,
|
|
function(v)
|
|
TotemBarDB.showTimerText = v
|
|
if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
|
|
end)
|
|
place(widgets.timerText, -28)
|
|
AddTooltip(widgets.timerText, "Shows the remaining-seconds text under each element button (the ring shows the same information graphically).")
|
|
|
|
-- Bar-layout cycle button: click steps through TotemBar.BAR_LAYOUTS
|
|
-- (wrapping), saves the choice, and re-lays out the live bar.
|
|
widgets.layout = CreateButton(f, "TotemBarOptLayoutButton", BarLayoutLabel(), function()
|
|
local layouts = TotemBar.BAR_LAYOUTS
|
|
local n = table.getn(layouts)
|
|
local current = TotemBarDB.barLayout or "1x6"
|
|
local currentIdx = 1
|
|
for i = 1, n do
|
|
if layouts[i] == current then
|
|
currentIdx = i
|
|
end
|
|
end
|
|
local nextIdx = math.mod(currentIdx, n) + 1
|
|
TotemBarDB.barLayout = layouts[nextIdx]
|
|
this:SetText(BarLayoutLabel())
|
|
if TotemBar.ApplyBarLayout then
|
|
TotemBar.ApplyBarLayout()
|
|
end
|
|
end)
|
|
place(widgets.layout, -28)
|
|
AddTooltip(widgets.layout, "Cycles the bar arrangement: one row of six, two rows of three, or three rows of two.")
|
|
|
|
-- Section divider (Rev 4 UI chrome): checkboxes above, sliders below.
|
|
CreateDivider(f, y)
|
|
y = y - 14
|
|
|
|
-- Sliders (leave headroom below each for its low/high/value text).
|
|
widgets.guard = CreateSlider(f, "Recall guard (sec)", 0, 5, 0.5, "Recall guard: %.1fs",
|
|
function() return TotemBarDB.recallGuardSeconds end,
|
|
function(v) TotemBarDB.recallGuardSeconds = v end)
|
|
place(widgets.guard, -44)
|
|
AddTooltip(widgets.guard, "A second press of the Totems macro within this many seconds skips Totemic Recall, so a double-press won't pull the totems you just dropped. 0 = no guard.")
|
|
|
|
widgets.gap = CreateSlider(f, "Cycle reset gap (sec)", 0.5, 5, 0.5, "Cycle gap: %.1fs",
|
|
function() return TotemBarDB.gapSeconds end,
|
|
function(v) TotemBarDB.gapSeconds = v end)
|
|
place(widgets.gap, -44)
|
|
AddTooltip(widgets.gap, "How long a pause (in seconds) restarts the one-per-press cast cycle back at the first totem.")
|
|
|
|
widgets.scale = CreateSlider(f, "UI size", 0.5, 2.0, 0.05, "UI size: %.2f",
|
|
function() return TotemBarDB.scale end,
|
|
function(v)
|
|
if TotemBar.SetBarScale then
|
|
TotemBar.SetBarScale(v)
|
|
elseif TotemBarFrame then
|
|
TotemBarDB.scale = v
|
|
TotemBarFrame:SetScale(v)
|
|
end
|
|
end)
|
|
place(widgets.scale, -44)
|
|
AddTooltip(widgets.scale, "Scales the whole bar. It stays anchored at its top-left corner, and the flyout scales with it.")
|
|
|
|
widgets.buttonGap = CreateSlider(f, "Button spacing", 10, 30, 1, "Spacing: %.0fpx",
|
|
function() return TotemBarDB.buttonGap end,
|
|
function(v)
|
|
if TotemBar.SetButtonGap then
|
|
TotemBar.SetButtonGap(v)
|
|
else
|
|
TotemBarDB.buttonGap = v
|
|
end
|
|
end)
|
|
place(widgets.buttonGap, -44)
|
|
AddTooltip(widgets.buttonGap, "Distance between the bar buttons. Applies immediately.")
|
|
|
|
-- Section divider (Rev 4 UI chrome): sliders above, action buttons below.
|
|
CreateDivider(f, y)
|
|
y = y - 14
|
|
|
|
optionsFrame = f
|
|
-- Buttons (Reset position, Create macro) are added in Task 4.
|
|
if TotemBar.BuildOptionsButtons then
|
|
TotemBar.BuildOptionsButtons(f, x, y)
|
|
end
|
|
|
|
-- Repopulate every widget from TotemBarDB whenever the panel shows.
|
|
f:SetScript("OnShow", function()
|
|
widgets.lock:SetChecked(TotemBarDB.locked and 1 or nil)
|
|
widgets.autoRecall:SetChecked(TotemBarDB.autoRecall and 1 or nil)
|
|
widgets.show:SetChecked((not TotemBarDB.hidden) and 1 or nil)
|
|
widgets.dropSet:SetChecked(TotemBarDB.showDropSetButton and 1 or nil)
|
|
widgets.ring:SetChecked(TotemBarDB.showDurationRing and 1 or nil)
|
|
widgets.pulse:SetChecked(TotemBarDB.showPulseBars and 1 or nil)
|
|
widgets.pulseWaves:SetChecked(TotemBarDB.showPulseWaves and 1 or nil)
|
|
widgets.timerText:SetChecked(TotemBarDB.showTimerText and 1 or nil)
|
|
widgets.layout:SetText(BarLayoutLabel())
|
|
widgets.guard:SetValue(TotemBar.clampValue(TotemBarDB.recallGuardSeconds, 0, 5))
|
|
widgets.gap:SetValue(TotemBar.clampValue(TotemBarDB.gapSeconds, 0.5, 5))
|
|
widgets.scale:SetValue(TotemBar.clampValue(TotemBarDB.scale, 0.5, 2.0))
|
|
widgets.buttonGap:SetValue(TotemBar.clampValue(TotemBarDB.buttonGap, 10, 30))
|
|
end)
|
|
|
|
-- Version footer (bottom-centre), read from the .toc so it always matches
|
|
-- the real addon version instead of a hardcoded duplicate.
|
|
local ver = (GetAddOnMetadata and GetAddOnMetadata("TotemBar", "Version")) or "0.1.0"
|
|
local verFS = f:CreateFontString("TotemBarOptionsVersion", "OVERLAY")
|
|
ApplyFont(verFS, 10)
|
|
verFS:SetPoint("BOTTOM", f, "BOTTOM", 0, 6)
|
|
verFS:SetText("v" .. ver)
|
|
verFS:SetTextColor(0.6, 0.6, 0.6)
|
|
|
|
-- ESC closes it.
|
|
tinsert(UISpecialFrames, "TotemBarOptionsFrame")
|
|
|
|
f:Hide()
|
|
return f
|
|
end
|
|
|
|
-- Creates or updates the "Totems" convenience macro from TotemBar.macroSpec.
|
|
-- General (account) macro, capped at 18 in 1.12; if the cap is full and no
|
|
-- "Totems" macro exists yet, reports and does nothing. Never auto-places it
|
|
-- on the action bar (no 1.12 API for that - the player drags it).
|
|
local function ApplyTotemsMacro()
|
|
local name, body, icon = TotemBar.macroSpec()
|
|
local existing = GetMacroIndexByName(name)
|
|
if existing and existing > 0 then
|
|
EditMacro(existing, name, icon, body, 1, nil)
|
|
ChatOut:AddMessage("TotemBar: '" .. name .. "' macro updated - drag it to your action bar.")
|
|
return
|
|
end
|
|
local numGlobal = GetNumMacros() -- returns global, perChar in 1.12
|
|
if numGlobal and numGlobal >= 18 then
|
|
ChatOut:AddMessage("TotemBar: macro slots full (18) - free one and retry.")
|
|
return
|
|
end
|
|
CreateMacro(name, icon, body, nil)
|
|
ChatOut:AddMessage("TotemBar: '" .. name .. "' macro created - drag it to your action bar.")
|
|
end
|
|
|
|
-- Adds the Reset-position and Create-macro buttons to the options frame.
|
|
-- Called by BuildOptionsFrame (Task 3) after the sliders, at layout cursor
|
|
-- (x, yStart). Kept separate so the panel body and the action buttons are
|
|
-- two focused units.
|
|
function TotemBar.BuildOptionsButtons(f, x, yStart)
|
|
-- Full content width (frame width minus the left/right margin x), so the
|
|
-- buttons read as a balanced block instead of a narrow left-biased pair.
|
|
local w = f:GetWidth() - (x * 2)
|
|
|
|
local reset = CreateButton(f, "TotemBarOptResetButton", "Reset position", function()
|
|
if TotemBar.ResetPosition then TotemBar.ResetPosition() end
|
|
end)
|
|
reset:SetWidth(w)
|
|
reset:SetPoint("TOPLEFT", f, "TOPLEFT", x, yStart)
|
|
AddTooltip(reset, "Moves the bar back to the center of the screen.")
|
|
|
|
local macro = CreateButton(f, "TotemBarOptMacroButton", "Create 'Totems' macro", function()
|
|
ApplyTotemsMacro()
|
|
end)
|
|
macro:SetWidth(w)
|
|
macro:SetPoint("TOPLEFT", f, "TOPLEFT", x, yStart - 28)
|
|
AddTooltip(macro, "Creates or updates a 'Totems' macro that drops all four chosen totems in one press. Drag it from the macro window to your action bar.")
|
|
|
|
local bind = CreateButton(f, "TotemBarOptBindButton", "Key bind mode", function()
|
|
if TotemBar.ToggleBindMode then TotemBar.ToggleBindMode() end
|
|
end)
|
|
bind:SetWidth(w)
|
|
bind:SetPoint("TOPLEFT", f, "TOPLEFT", x, yStart - 56)
|
|
AddTooltip(bind, "Toggle bind mode, then hover any bar button or a flyout totem and press a key to bind it. ESC clears. Bindings are saved automatically.")
|
|
end
|
|
|
|
-- Show/hide the options panel (builds it lazily). Left-click on the minimap
|
|
-- button and /tb options both call this.
|
|
function TotemBar.ToggleOptions()
|
|
local f = BuildOptionsFrame()
|
|
if f:IsShown() then
|
|
f:Hide()
|
|
else
|
|
f:Show()
|
|
end
|
|
end
|