Settings: text opacity, name/time gap, place colour vibrance, text outline; window opacity spares text; checkbox and open fixes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyQKCL92ZLwPi9qmYQtNdc
This commit is contained in:
Claude
2026-10-12 16:23:41 -04:00
parent 20d4608c21
commit 15e9d5185c
4 changed files with 108 additions and 35 deletions
+2 -1
View File
@@ -5,13 +5,14 @@
### New
- **Accurate without the DLL.** OctoBus now ships with each route's restart offset (how many seconds into its trip it docks, counted from the last server restart), measured on 12 October. At every login it asks the server how long it has been up and sets every route it has not seen since that restart from the restart time plus the offset. A brand new copy with no DLL and no sightings gets every timer right from the first login, and stays right after later restarts.
- **Settings window**: `/ob options`, or right-click the minimap icon. Background opacity, window opacity, text size, window size, lock, which corner stays put as the list grows or shrinks, auto-show, which routes to show, and bell alerts. Changes apply at once.
- **Settings window**: `/ob options`, or right-click the minimap icon. Background opacity, window opacity, text size, text opacity, the gap between names and times, place colour vibrance, text outline (thin, thick or shadow), window size, lock, which corner stays put as the list grows or shrinks, auto-show, which routes to show, and bell alerts. Changes apply at once.
### Changed
- The "no DLL: estimates only" note is gone from the panel: without the DLL the timers are now accurate. `/ob debug` still shows whether the DLL is loaded.
- The footer no longer names the route with the oldest data (the restart correction makes it unnecessary). It only appears after a server restart, while a route in the window has not been seen since.
### Fixed
- The settings window needed two right-clicks to open, and some checkboxes on a shared line (outline, sound) toggled their neighbour.
- Window size, look, lock, position and the minimap icon's spot were not restored after logging in: they were applied before the game had loaded the saved settings.
## 1.3.4 (12 October 2026)
+61 -15
View File
@@ -311,13 +311,27 @@ local OB_OK, OB_ERR = pcall(function ()
local RowName = ShownName
-- Each place has its own colour in the rows; renamed or unknown places stay white.
-- These are the full colours; the vibrance setting (CFG.vibrance, default 75%) darkens them.
local PLACE_COLOR = {
["orgrimmar"] = "bf3f33", ["undercity"] = "8769bf", ["gromgol"] = "4ca53f",
["kargath"] = "a8733f", ["thunder bluff"] = "ae9959", ["sparkwater"] = "3399bf",
["revantusk"] = "268c78", ["revantusk village"] = "268c78", ["ratchet"] = "bfa526",
["booty bay"] = "bf6699",
["orgrimmar"] = "ff5544", ["undercity"] = "b48cff", ["gromgol"] = "66dd55",
["kargath"] = "e09a55", ["thunder bluff"] = "e8cc77", ["sparkwater"] = "44ccff",
["revantusk"] = "33bba0", ["revantusk village"] = "33bba0", ["ratchet"] = "ffdd33",
["booty bay"] = "ff88cc",
}
local function Paint(n) return "|cff" .. (PLACE_COLOR[Norm(n)] or "ffffff") .. n .. "|r" end
local paintCache, paintAt = {}, nil
local function Paint(n)
local base = PLACE_COLOR[Norm(n)]
if not base then return "|cffffffff" .. n .. "|r" end
local v = CFG.vibrance or 0.75
if v ~= paintAt then paintCache = {} paintAt = v end
local hex = paintCache[base]
if not hex then
local function ch(i) return math.floor(tonumber(string.sub(base, i, i + 1), 16) * v + 0.5) end
hex = string.format("%02x%02x%02x", ch(1), ch(3), ch(5))
paintCache[base] = hex
end
return "|cff" .. hex .. n .. "|r"
end
local function ColoredName(r)
local a, b = r.flipTo, Away(r)
if Flipped(r) then a, b = b, a end
@@ -938,7 +952,7 @@ local OB_OK, OB_ERR = pcall(function ()
-- the timer window
-- sizes at text size 100%; ApplyLook scales them with the text size setting
local textScale = CFG.textScale or 1
local WIDTH = math.floor(280 * textScale + 0.5)
local WIDTH = math.floor(280 * textScale * (CFG.widthScale or 1) + 0.5)
local ROW_H = math.floor(18 * textScale + 0.5)
local TOP = math.floor(22 * textScale + 0.5)
@@ -1076,6 +1090,18 @@ local OB_OK, OB_ERR = pcall(function ()
local rows = {}
-- text outline: "thin" (default), "thick", or "shadow" (thin outline plus a drop shadow)
local function StyleText(fs, size)
local o = CFG.outline or "thin"
fs:SetFont(font, size, o == "thick" and "THICKOUTLINE" or "OUTLINE")
if o == "shadow" then
fs:SetShadowColor(0, 0, 0, 1)
fs:SetShadowOffset(1.5, -1.5)
else
fs:SetShadowOffset(0, 0)
end
end
local function PlaceRow(row, i)
row:SetHeight(ROW_H)
row:ClearAllPoints()
@@ -1083,8 +1109,13 @@ local OB_OK, OB_ERR = pcall(function ()
row:SetPoint("RIGHT", panel, "RIGHT", -4, 0)
local fs = math.floor(10 * textScale + 0.5)
if row.name then
row.name:SetFont(font, fs, "OUTLINE")
row.time:SetFont(font, fs, "OUTLINE")
StyleText(row.name, fs)
StyleText(row.time, fs)
row.name:SetAlpha(CFG.textAlpha or 1)
row.time:SetAlpha(CFG.textAlpha or 1)
row.icon:SetAlpha(CFG.alpha or 1)
row.bell:SetAlpha(CFG.alpha or 1)
row.bg:SetAlpha(CFG.alpha or 1)
end
end
@@ -1201,19 +1232,26 @@ local OB_OK, OB_ERR = pcall(function ()
-- Appearance (set from the settings window, /ob options): background opacity, window
-- opacity and text size. Text size scales the fonts and the row height and width with them.
local function ApplyLook()
-- window opacity fades everything but the text, which has its own opacity
local wa = CFG.alpha or 1
local bg = CFG.bgAlpha or 0.75
panel:SetBackdropColor(0.02, 0.02, 0.02, bg)
panel:SetBackdropBorderColor(0.22, 0.22, 0.22, math.min(1, bg + 0.25))
panel:SetAlpha(CFG.alpha or 1)
panel:SetAlpha(1)
panel:SetBackdropColor(0.02, 0.02, 0.02, bg * wa)
panel:SetBackdropBorderColor(0.22, 0.22, 0.22, math.min(1, bg + 0.25) * wa)
grip:SetAlpha(wa)
textScale = CFG.textScale or 1
WIDTH = math.floor(280 * textScale + 0.5)
WIDTH = math.floor(280 * textScale * (CFG.widthScale or 1) + 0.5)
ROW_H = math.floor(18 * textScale + 0.5)
TOP = math.floor(22 * textScale + 0.5)
panel:SetWidth(WIDTH)
panel.title:SetFont(font, math.floor(10 * textScale + 0.5), "OUTLINE")
panel.status:SetFont(font, math.floor(9 * textScale + 0.5), "OUTLINE")
panel.foot:SetFont(font, math.floor(9 * textScale + 0.5), "OUTLINE")
StyleText(panel.title, math.floor(10 * textScale + 0.5))
StyleText(panel.status, math.floor(9 * textScale + 0.5))
StyleText(panel.foot, math.floor(9 * textScale + 0.5))
for i = 1, table.getn(rows) do PlaceRow(rows[i], i) end
local ta = CFG.textAlpha or 1
panel.title:SetAlpha(ta)
panel.status:SetAlpha(ta)
panel.foot:SetAlpha(ta)
end
ApplyLook()
@@ -1343,6 +1381,14 @@ local OB_OK, OB_ERR = pcall(function ()
end
end
for i = n + 1, table.getn(rows) do rows[i]:Hide() end
-- width: the chosen width, but never so narrow that a name runs into its time
local need = 0
for i = 1, n do
local w = (rows[i].name:GetStringWidth() or 0) + (rows[i].time:GetStringWidth() or 0)
if w > need then need = w end
end
need = math.floor(need + 4 + 2 + 14 + 4 + 20 + 12 * textScale + 4)
panel:SetWidth(need > WIDTH and need or WIDTH)
panel:SetHeight(TOP + n * ROW_H + ((cal or (panel.foot:GetText() or "") ~= "") and math.floor(18 * textScale + 0.5) or 10))
panel.title:SetText("|cff33ffccTransports|r" .. (CFG.full and " |cff777777- all|r" or ""))
if recOn then
+44 -18
View File
@@ -67,6 +67,9 @@ local function Check(label, x, y, get, set)
local text = getglobal(name .. "Text")
text:SetFont(font, 10)
text:SetText(label)
-- the template makes the box clickable 100 px to its right, which reaches over the
-- next box on the same line: limit the click area to the box and its own label
c:SetHitRectInsets(0, -((text:GetStringWidth() or 0) + 6), 0, 0)
c.Update = function() c:SetChecked(get() and 1 or nil) end
c:SetScript("OnClick", function() set(this:GetChecked() and true or false) end)
table.insert(controls, c)
@@ -80,7 +83,7 @@ local function Build()
local C = OB.cfg
frame = CreateFrame("Frame", "OctoBusOptions", UIParent)
frame:SetWidth(W)
frame:SetHeight(538)
frame:SetHeight(676)
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 60)
frame:SetFrameStrata("DIALOG")
frame:SetClampedToScreen(true)
@@ -99,26 +102,46 @@ local function Build()
local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
close:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 2, 2)
close:SetFrameLevel(frame:GetFrameLevel() + 5)
close:SetScript("OnClick", function() frame:Hide() end)
local function look(key) return function(v) C[key] = v OB.ApplyLook() end end
Heading("Window", -34)
Slider("Background opacity", -64, 0, 1, 0.05, function() return C.bgAlpha or 0.75 end, look("bgAlpha"), pct)
Slider("Window opacity", -102, 0.2, 1, 0.05, function() return C.alpha or 1 end, look("alpha"), pct)
Slider("Window opacity (all but text)", -102, 0.2, 1, 0.05, function() return C.alpha or 1 end, look("alpha"), pct)
Slider("Text size", -140, 0.7, 1.5, 0.05, function() return C.textScale or 1 end, look("textScale"), pct)
Slider("Window size", -178, OB.scaleMin, OB.scaleMax, 0.05, function() return C.scale or 1 end,
Slider("Text opacity", -178, 0, 1, 0.05, function() return C.textAlpha or 1 end, look("textAlpha"), pct)
Slider("Gap between names and times", -216, 0.6, 1.6, 0.05, function() return C.widthScale or 1 end, look("widthScale"), pct)
Slider("Place colour vibrance", -254, 0.2, 1, 0.05, function() return C.vibrance or 0.75 end, look("vibrance"), pct)
local outl = frame:CreateFontString(nil, "OVERLAY")
outl:SetFont(font, 10)
outl:SetPoint("TOPLEFT", frame, "TOPLEFT", 18, -284)
outl:SetText("Text outline:")
outl:SetTextColor(1, 0.82, 0)
local OUTLINES = { { "thin", "Thin", 96 }, { "thick", "Thick", 156 }, { "shadow", "Shadow", 222 } }
for i = 1, table.getn(OUTLINES) do
local key = OUTLINES[i][1]
Check(OUTLINES[i][2], OUTLINES[i][3], -278, function() return (C.outline or "thin") == key end,
function()
C.outline = key
OB.ApplyLook()
for j = 1, table.getn(controls) do controls[j].Update() end
end)
end
Slider("Window size", -316, OB.scaleMin, OB.scaleMax, 0.05, function() return C.scale or 1 end,
function(v) OB.SetScale(v) end, pct)
Check("Lock position and size", 14, -204, function() return C.locked end, function(v) OB.SetLocked(v) end)
Check("Lock position and size", 14, -342, function() return C.locked end, function(v) OB.SetLocked(v) end)
-- which corner stays in place when the list gets longer or shorter
local corner = frame:CreateFontString(nil, "OVERLAY")
corner:SetFont(font, 10)
corner:SetPoint("TOPLEFT", frame, "TOPLEFT", 18, -234)
corner:SetPoint("TOPLEFT", frame, "TOPLEFT", 18, -372)
corner:SetText("Corner that stays put as the list grows or shrinks:")
corner:SetTextColor(1, 0.82, 0)
local CORNERS = {
{ "TOPLEFT", "Top left", 14, -248 }, { "TOPRIGHT", "Top right", 150, -248 },
{ "BOTTOMLEFT", "Bottom left", 14, -272 }, { "BOTTOMRIGHT", "Bottom right", 150, -272 },
{ "TOPLEFT", "Top left", 14, -386 }, { "TOPRIGHT", "Top right", 150, -386 },
{ "BOTTOMLEFT", "Bottom left", 14, -410 }, { "BOTTOMRIGHT", "Bottom right", 150, -410 },
}
for i = 1, table.getn(CORNERS) do
local a = CORNERS[i][1]
@@ -129,20 +152,20 @@ local function Build()
end)
end
Heading("Routes", -302)
Check("Open by itself only at route ends", 14, -320, function() return C.zoneOnly end,
Heading("Routes", -440)
Check("Open by itself only at route ends", 14, -458, function() return C.zoneOnly end,
function(v) C.zoneOnly = v end)
Check("Show all routes (not just for where you are)", 14, -344, function() return C.full end,
Check("Show all routes (not just for where you are)", 14, -482, function() return C.full end,
function(v) C.full = v OB.Refresh() end)
Check("Show the Sparkwater Port boat", 14, -368, function() return not C.hideBoat end,
Check("Show the Sparkwater Port boat", 14, -506, function() return not C.hideBoat end,
function(v) C.hideBoat = not v OB.Refresh() end)
Heading("Alerts (click a row to put a bell on it)", -400)
Slider("Warn before arrival", -430, 10, 120, 5, function() return C.alert or 45 end,
Heading("Alerts (click a row to put a bell on it)", -538)
Slider("Warn before arrival", -568, 10, 120, 5, function() return C.alert or 45 end,
function(v) C.alert = v end, function(v) return math.floor(v + 0.5) .. " s" end)
Check("Chat message", 14, -452, function() return C.chat end, function(v) C.chat = v end)
Check("Sound", 120, -452, function() return C.sound end, function(v) C.sound = v end)
Check("Flash the minimap icon", 14, -476, function() return C.flash end, function(v) C.flash = v end)
Check("Chat message", 14, -590, function() return C.chat end, function(v) C.chat = v end)
Check("Sound", 120, -590, function() return C.sound end, function(v) C.sound = v end)
Check("Flash the minimap icon", 14, -614, function() return C.flash end, function(v) C.flash = v end)
local reset = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
reset:SetWidth(150)
@@ -150,7 +173,7 @@ local function Build()
reset:SetPoint("BOTTOM", frame, "BOTTOM", 0, 12)
reset:SetText("Reset window look")
reset:SetScript("OnClick", function()
C.bgAlpha, C.alpha, C.textScale = nil, nil, nil
C.bgAlpha, C.alpha, C.textScale, C.textAlpha, C.widthScale, C.vibrance, C.outline = nil, nil, nil, nil, nil, nil, nil
OB.SetScale(1)
OB.ApplyLook()
for i = 1, table.getn(controls) do controls[i].Update() end
@@ -162,7 +185,10 @@ function OctoBus_Options()
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccOctoBus:|r not running, so its settings are unavailable.")
return
end
if not frame then Build() end
if not frame then
Build()
frame:Hide() -- a new frame starts out shown; without this the first click hid it
end
if frame:IsShown() then frame:Hide() return end
for i = 1, table.getn(controls) do controls[i].Update() end
OctoBus.Show() -- show the timer window so changes can be seen
+1 -1
View File
@@ -81,7 +81,7 @@ Saved data lives in `WTF\...\SavedVariables\OctoBus.lua`.
- **Auto-show**: the window opens by itself at either end of any route (the zones in the table above), and while a tracked transport is in view, such as on a crossing. It hides elsewhere. The icon toggle always works manually. Add more auto-show places with `/ob allow <zone or sub-zone>` (capitals do not matter; `/ob where` shows the names), remove one with `/ob disallow <name>`, or turn the behaviour off with `/ob zone`.
- **Bells**: click a row to toggle a bell. Belled routes print a chat message `/ob alert` seconds (default 45) before the transport docks at the row's start, e.g. `Orgrimmar -> Undercity: docks at Orgrimmar in 0:45`. Nothing is printed to chat for routes without a bell.
- **Reverse a row**: right-click it.
- **Settings window**: `/ob options`, or right-click the minimap icon. Background opacity, window opacity, text size, window size, lock, and which corner stays put as the list grows or shrinks, plus auto-show, which routes to show, and bell alerts. Changes apply at once.
- **Settings window**: `/ob options`, or right-click the minimap icon. Background opacity, window opacity, text size, text opacity, the gap between names and times, place colour vibrance, text outline (thin, thick or shadow), window size, lock, and which corner stays put as the list grows or shrinks, plus auto-show, which routes to show, and bell alerts. Changes apply at once.
- **Move and size the window**: drag it to move it; drag the dots in the bottom-right corner (or use `/ob scale`) to resize it. `/ob lock` locks position and size; `/ob resetpos` puts the window and icon back at their default place and size.
## How the timing works