-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local General = TSM.MainUI.Settings:NewPackage("General") local L = TSM.Include("Locale").GetTable() local Log = TSM.Include("Util.Log") local TempTable = TSM.Include("Util.TempTable") local Table = TSM.Include("Util.Table") local Theme = TSM.Include("Util.Theme") local Settings = TSM.Include("Service.Settings") local Sync = TSM.Include("Service.Sync") local PlayerInfo = TSM.Include("Service.PlayerInfo") local Tooltip = TSM.Include("UI.Tooltip") local UIElements = TSM.Include("UI.UIElements") local private = { frame = nil, characterList = {}, guildList = {}, chatFrameList = {}, } -- ============================================================================ -- Module Functions -- ============================================================================ function General.OnInitialize() TSM.MainUI.Settings.RegisterSettingPage(L["General Settings"], "top", private.GetGeneralSettingsFrame) Sync.RegisterConnectionChangedCallback(private.SyncConnectionChangedCallback) end -- ============================================================================ -- General Settings UI -- ============================================================================ function private.GetGeneralSettingsFrame() TSM.UI.AnalyticsRecordPathChange("main", "settings", "general") wipe(private.chatFrameList) local defaultChatFrame = nil for i = 1, NUM_CHAT_WINDOWS do local name = strlower(GetChatWindowInfo(i) or "") if DEFAULT_CHAT_FRAME == _G["ChatFrame"..i] then defaultChatFrame = name end if name ~= "" and _G["ChatFrame"..i.."Tab"]:IsVisible() then tinsert(private.chatFrameList, name) end end if not tContains(private.chatFrameList, TSM.db.global.coreOptions.chatFrame) then if tContains(private.chatFrameList, defaultChatFrame) then TSM.db.global.coreOptions.chatFrame = defaultChatFrame Log.SetChatFrame(defaultChatFrame) else -- all chat frames are hidden, so just disable the setting wipe(private.chatFrameList) end end wipe(private.characterList) for _, character in PlayerInfo.CharacterIterator(true) do if character ~= UnitName("player") then tinsert(private.characterList, character) end end wipe(private.guildList) for guild in PlayerInfo.GuildIterator(true) do tinsert(private.guildList, guild) end return UIElements.New("ScrollFrame", "generalSettings") :SetPadding(8, 8, 8, 0) :SetScript("OnUpdate", private.FrameOnUpdate) :SetScript("OnHide", private.FrameOnHide) :AddChild(TSM.MainUI.Settings.CreateExpandableSection("General", "general", L["General Options"], L["Some general TSM options are below."]) :AddChild(UIElements.New("Frame", "check1") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 0, 12) :AddChild(UIElements.New("Checkbox", "globalOperations") :SetWidth("AUTO") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Store operations globally"]) :SetChecked(TSM.Operations.IsStoredGlobally()) :SetScript("OnValueChanged", private.GlobalOperationsOnValueChanged) ) :AddChild(UIElements.New("Spacer", "spacer")) ) :AddChild(UIElements.New("Frame", "check2") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 0, 12) :AddChild(UIElements.New("Checkbox", "protectAuctionHouse") :SetWidth("AUTO") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Prevent closing the Auction House with the esc key"]) :SetSettingInfo(TSM.db.global.coreOptions, "protectAuctionHouse") ) :AddChild(UIElements.New("Spacer", "spacer")) ) :AddChild(TSM.MainUI.Settings.CreateInputWithReset("generalGroupPriceField", L["Filter group item lists based on the following price source"], "global.coreOptions.groupPriceSource")) :AddChild(UIElements.New("Frame", "dropdownLabelLine") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 12, 4) :AddChild(UIElements.New("Text", "chatTabLabel") :SetMargin(0, 12, 0, 0) :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Chat Tab"]) ) :AddChild(UIElements.New("Text", "forgetLabel") :SetMargin(0, 12, 0, 0) :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Forget Character"]) ) :AddChild(UIElements.New("Text", "ignoreLabel") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Ignore Guilds"]) ) ) :AddChild(UIElements.New("Frame", "dropdownLabelLine") :SetLayout("HORIZONTAL") :SetHeight(24) :AddChild(UIElements.New("SelectionDropdown", "chatTabDropdown") :SetMargin(0, 16, 0, 0) :SetItems(private.chatFrameList, private.chatFrameList) :SetSettingInfo(next(private.chatFrameList) and TSM.db.global.coreOptions or nil, "chatFrame") :SetScript("OnSelectionChanged", private.ChatTabOnSelectionChanged) ) :AddChild(UIElements.New("SelectionDropdown", "forgetDropdown") :SetMargin(0, 16, 0, 0) :SetItems(private.characterList, private.characterList) :SetScript("OnSelectionChanged", private.ForgetCharacterOnSelectionChanged) ) :AddChild(UIElements.New("MultiselectionDropdown", "ignoreDropdown") :SetItems(private.guildList, private.guildList) :SetSettingInfo(TSM.db.factionrealm.coreOptions, "ignoreGuilds") :SetSelectionText(L["No Guilds"], L["%d Guilds"], L["All Guilds"]) ) ) ) :AddChild(TSM.MainUI.Settings.CreateExpandableSection("General", "profiles", L["Profiles"], L["Set your active profile or create a new one."]) :AddChildrenWithFunction(private.AddProfileRows) :AddChild(UIElements.New("Text", "profileLabel") :SetHeight(20) :SetMargin(0, 0, 4, 4) :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Create new profile"]) ) :AddChild(UIElements.New("Input", "newProfileInput") :SetHeight(24) :SetBackgroundColor("ACTIVE_BG") :SetHintText(L["Enter profile name"]) :SetScript("OnEnterPressed", private.NewProfileInputOnEnterPressed) ) ) :AddChild(TSM.MainUI.Settings.CreateExpandableSection("General", "accountSync", L["Account Syncing"], L["TSM can automatically sync data between multiple WoW accounts."]) :AddChildrenWithFunction(private.AddAccountSyncRows) :AddChild(UIElements.New("Text", "profileLabel") :SetHeight(20) :SetMargin(0, 0, 4, 4) :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Add account"]) ) :AddChild(UIElements.New("Input", "newProfileInput") :SetHeight(24) :SetBackgroundColor("ACTIVE_BG") :SetHintText(L["Enter name of logged-in character on other account"]) :SetScript("OnEnterPressed", private.NewAccountSyncInputOnEnterPressed) ) ) end function private.AddProfileRows(frame) for index, profileName in TSM.db:ProfileIterator() do local isCurrentProfile = profileName == TSM.db:GetCurrentProfile() local row = UIElements.New("Frame", "profileRow_"..index) :SetLayout("HORIZONTAL") :SetHeight(28) :SetMargin(0, 0, 0, 8) :SetPadding(8, 8, 4, 4) :SetBackgroundColor(isCurrentProfile and "ACTIVE_BG" or "PRIMARY_BG_ALT", true) :SetContext(profileName) :SetScript("OnEnter", private.ProfileRowOnEnter) :SetScript("OnLeave", private.ProfileRowOnLeave) :AddChild(UIElements.New("Frame", "content") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Checkbox", "checkbox") :SetWidth("AUTO") :SetCheckboxPosition("LEFT") :SetText(profileName) :SetFont("BODY_BODY2") :SetChecked(isCurrentProfile) :SetScript("OnValueChanged", private.ProfileCheckboxOnValueChanged) :PropagateScript("OnEnter") :PropagateScript("OnLeave") ) :PropagateScript("OnEnter") :PropagateScript("OnLeave") :AddChild(UIElements.New("Spacer", "spacer")) ) :AddChild(UIElements.New("Button", "resetBtn") :SetBackgroundAndSize("iconPack.18x18/Reset") :SetMargin(4, 0, 0, 0) :SetScript("OnClick", private.ResetProfileOnClick) :SetScript("OnEnter", private.ResetProfileOnEnter) :SetScript("OnLeave", private.ResetProfileOnLeave) ) :AddChild(UIElements.New("Button", "renameBtn") :SetBackgroundAndSize("iconPack.18x18/Edit") :SetMargin(4, 0, 0, 0) :SetScript("OnClick", private.RenameProfileOnClick) :SetScript("OnEnter", private.RenameProfileOnEnter) :SetScript("OnLeave", private.RenameProfileOnLeave) ) :AddChild(UIElements.New("Button", "duplicateBtn") :SetBackgroundAndSize("iconPack.18x18/Duplicate") :SetMargin(4, 0, 0, 0) :SetScript("OnClick", private.DuplicateProfileOnClick) :SetScript("OnEnter", private.DuplicateProfileOnEnter) :SetScript("OnLeave", private.DuplicateProfileOnLeave) ) :AddChild(UIElements.New("Button", "deleteBtn") :SetBackgroundAndSize("iconPack.18x18/Delete") :SetMargin(4, 0, 0, 0) :SetScript("OnClick", private.DeleteProfileOnClick) :SetScript("OnEnter", private.DeleteProfileOnEnter) :SetScript("OnLeave", private.DeleteProfileOnLeave) ) row:GetElement("deleteBtn"):Hide() if not isCurrentProfile then row:GetElement("resetBtn"):Hide() row:GetElement("renameBtn"):Hide() row:GetElement("duplicateBtn"):Hide() end frame:AddChild(row) end end function private.AddAccountSyncRows(frame) local newAccountStatusText = Sync.GetNewAccountStatus() if newAccountStatusText then local row = private.CreateAccountSyncRow("new", newAccountStatusText) row:GetElement("sendProfileBtn"):Hide() row:GetElement("removeBtn"):Hide() frame:AddChild(row) end for _, account in TSM.db:SyncAccountIterator() do local characters = TempTable.Acquire() for _, character in Settings.CharacterByAccountFactionrealmIterator(account) do tinsert(characters, character) end sort(characters) local isConnected, connectedCharacter = Sync.GetConnectionStatus(account) local statusText = nil if isConnected then statusText = Theme.GetFeedbackColor("GREEN"):ColorText(format(L["Connected to %s"], connectedCharacter)) else statusText = Theme.GetFeedbackColor("RED"):ColorText(L["Offline"]) end statusText = statusText.." | "..table.concat(characters, ", ") TempTable.Release(characters) local row = private.CreateAccountSyncRow("accountSyncRow_"..account, statusText) row:SetContext(account) row:GetElement("sendProfileBtn"):Hide() row:GetElement("removeBtn"):Hide() frame:AddChild(row) end end function private.CreateAccountSyncRow(id, statusText) local row = UIElements.New("Frame", id) :SetLayout("HORIZONTAL") :SetHeight(28) :SetMargin(0, 0, 0, 8) :SetPadding(8, 8, 4, 4) :SetBackgroundColor("PRIMARY_BG_ALT", true) :SetScript("OnEnter", private.AccountSyncRowOnEnter) :SetScript("OnLeave", private.AccountSyncRowOnLeave) :AddChild(UIElements.New("Text", "status") :SetFont("BODY_BODY2") :SetText(statusText) :SetScript("OnEnter", private.AccountSyncTextOnEnter) :SetScript("OnLeave", private.AccountSyncTextOnLeave) ) :AddChild(UIElements.New("Button", "sendProfileBtn") :SetBackgroundAndSize("iconPack.18x18/Operation") :SetMargin(4, 0, 0, 0) :SetScript("OnClick", private.SendProfileOnClick) :SetScript("OnEnter", private.SendProfileOnEnter) :SetScript("OnLeave", private.SendProfileOnLeave) ) :AddChild(UIElements.New("Button", "removeBtn") :SetBackgroundAndSize("iconPack.18x18/Delete") :SetMargin(4, 0, 0, 0) :SetScript("OnClick", private.RemoveAccountSyncOnClick) :SetScript("OnEnter", private.RemoveAccountOnEnter) :SetScript("OnLeave", private.RemoveAccountOnLeave) ) return row end -- ============================================================================ -- Local Script Handlers -- ============================================================================ function private.SyncConnectionChangedCallback() if private.frame then private.frame:GetParentElement():ReloadContent() end end function private.FrameOnUpdate(frame) frame:SetScript("OnUpdate", nil) private.frame = frame end function private.FrameOnHide(frame) private.frame = nil end function private.GlobalOperationsOnValueChanged(checkbox, value) -- restore the previous value until it's confirmed checkbox:SetChecked(not value, true) local desc = L["If you have multiple profiles set up with operations, enabling this will cause all but the current profile's operations to be irreversibly lost."] checkbox:GetBaseElement():ShowConfirmationDialog(L["Make Operations Global?"], desc, private.GlobalOperationsConfirmed, checkbox, value) end function private.GlobalOperationsConfirmed(checkbox, newValue) checkbox:SetChecked(newValue, true) :Draw() TSM.Operations.SetStoredGlobally(newValue) end function private.ChatTabOnSelectionChanged(dropdown) Log.SetChatFrame(dropdown:GetSelectedItem()) end function private.ForgetCharacterOnSelectionChanged(self) local character = self:GetSelectedItem() if not character then return end TSM.db:RemoveSyncCharacter(character) TSM.db.factionrealm.internalData.pendingMail[character] = nil TSM.db.factionrealm.internalData.characterGuilds[character] = nil Log.PrintfUser(L["%s removed."], character) assert(Table.RemoveByValue(private.characterList, character) == 1) self:SetSelectedItem(nil) :SetItems(private.characterList) :Draw() end function private.ProfileRowOnEnter(frame) local isCurrentProfile = frame:GetContext() == TSM.db:GetCurrentProfile() frame:SetBackgroundColor("ACTIVE_BG", true) if not isCurrentProfile then frame:GetElement("resetBtn"):Show() frame:GetElement("renameBtn"):Show() frame:GetElement("duplicateBtn"):Show() frame:GetElement("deleteBtn"):Show() end frame:Draw() end function private.ProfileRowOnLeave(frame) local isCurrentProfile = frame:GetContext() == TSM.db:GetCurrentProfile() frame:SetBackgroundColor(isCurrentProfile and "ACTIVE_BG" or "PRIMARY_BG_ALT", true) if not isCurrentProfile then frame:GetElement("resetBtn"):Hide() frame:GetElement("renameBtn"):Hide() frame:GetElement("duplicateBtn"):Hide() frame:GetElement("deleteBtn"):Hide() end frame:Draw() end function private.ProfileCheckboxOnValueChanged(checkbox, value) if not value then -- can't uncheck profile checkboxes checkbox:SetChecked(true, true) checkbox:Draw() return end -- uncheck the current profile row local currentProfileIndex = nil for index, profileName in TSM.db:ProfileIterator() do if profileName == TSM.db:GetCurrentProfile() then assert(not currentProfileIndex) currentProfileIndex = index end end local prevRow = checkbox:GetElement("__parent.__parent.__parent.profileRow_"..currentProfileIndex) prevRow:GetElement("content.checkbox") :SetChecked(false, true) prevRow:GetElement("resetBtn"):Hide() prevRow:GetElement("renameBtn"):Hide() prevRow:GetElement("duplicateBtn"):Hide() prevRow:GetElement("deleteBtn"):Hide() prevRow:SetBackgroundColor("PRIMARY_BG_ALT", true) prevRow:Draw() -- set the profile TSM.db:SetProfile(checkbox:GetText()) -- set this row as the current one local newRow = checkbox:GetElement("__parent.__parent") newRow:SetBackgroundColor("ACTIVE_BG", true) newRow:GetElement("resetBtn"):Show() newRow:GetElement("renameBtn"):Show() newRow:GetElement("duplicateBtn"):Show() newRow:GetElement("deleteBtn"):Hide() newRow:Draw() end function private.RenameProfileOnClick(button) local profileName = button:GetParentElement():GetContext() local dialogFrame = UIElements.New("Frame", "frame") :SetLayout("VERTICAL") :SetSize(600, 187) :AddAnchor("CENTER") :SetBackgroundColor("FRAME_BG") :SetBorderColor("ACTIVE_BG") :AddChild(UIElements.New("Text", "title") :SetHeight(44) :SetMargin(16, 16, 24, 16) :SetFont("BODY_BODY2_BOLD") :SetJustifyH("CENTER") :SetText(L["Rename Profile"]) ) :AddChild(UIElements.New("Input", "nameInput") :SetHeight(26) :SetMargin(16, 16, 0, 25) :SetBackgroundColor("PRIMARY_BG_ALT") :SetContext(profileName) :SetValue(profileName) :SetScript("OnEnterPressed", private.RenameProfileInputOnEnterPressed) ) :AddChild(UIElements.New("Frame", "buttons") :SetLayout("HORIZONTAL") :SetMargin(16, 16, 0, 16) :AddChild(UIElements.New("Spacer", "spacer")) :AddChild(UIElements.New("ActionButton", "closeBtn") :SetSize(126, 26) :SetText(CLOSE) :SetScript("OnClick", private.DialogCloseBtnOnClick) ) ) button:GetBaseElement():ShowDialogFrame(dialogFrame) dialogFrame:GetElement("nameInput"):SetFocused(true) end function private.DialogCloseBtnOnClick(button) private.RenameProfileInputOnEnterPressed(button:GetElement("__parent.__parent.nameInput")) end function private.RenameProfileInputOnEnterPressed(input) local profileName = input:GetValue() local prevProfileName = input:GetContext() if not TSM.db:IsValidProfileName(profileName) then Log.PrintUser(L["This is not a valid profile name. Profile names must be at least one character long and may not contain '@' characters."]) return elseif TSM.db:ProfileExists(profileName) then Log.PrintUser(L["A profile with this name already exists."]) return end -- create a new profile, copy over the settings, then delete the old one local currentProfileName = TSM.db:GetCurrentProfile() TSM.db:SetProfile(profileName) TSM.db:CopyProfile(prevProfileName) TSM.db:DeleteProfile(prevProfileName, profileName) if currentProfileName ~= prevProfileName then TSM.db:SetProfile(currentProfileName) end -- hide the dialog and refresh the settings content local baseElement = input:GetBaseElement() baseElement:HideDialog() baseElement:GetElement("content.settings.contentFrame.content"):ReloadContent() end function private.RenameProfileOnEnter(button) button:ShowTooltip(L["Rename the profile"]) private.ProfileRowOnEnter(button:GetParentElement()) end function private.RenameProfileOnLeave(button) Tooltip.Hide() private.ProfileRowOnLeave(button:GetParentElement()) end function private.DuplicateProfileOnClick(button) local profileName = button:GetParentElement():GetContext() local newName = profileName while TSM.db:ProfileExists(newName) do newName = newName.." Copy" end local activeProfile = TSM.db:GetCurrentProfile() TSM.db:SetProfile(newName) TSM.db:CopyProfile(profileName) TSM.db:SetProfile(activeProfile) button:GetBaseElement():GetElement("content.settings.contentFrame.content"):ReloadContent() end function private.DuplicateProfileOnEnter(button) button:ShowTooltip(L["Duplicate the profile"]) private.ProfileRowOnEnter(button:GetParentElement()) end function private.DuplicateProfileOnLeave(button) Tooltip.Hide() private.ProfileRowOnLeave(button:GetParentElement()) end function private.ResetProfileOnClick(button) local profileName = button:GetParentElement():GetContext() local desc = format(L["This will reset all groups and operations (if not stored globally) to be wiped from '%s'."], profileName) button:GetBaseElement():ShowConfirmationDialog(L["Reset Profile?"], desc, private.ResetProfileConfirmed, profileName) end function private.ResetProfileConfirmed(profileName) local activeProfile = TSM.db:GetCurrentProfile() TSM.db:SetProfile(profileName) TSM.db:ResetProfile() TSM.db:SetProfile(activeProfile) end function private.ResetProfileOnEnter(button) button:ShowTooltip(L["Reset the current profile to default settings"]) private.ProfileRowOnEnter(button:GetParentElement()) end function private.ResetProfileOnLeave(button) Tooltip.Hide() private.ProfileRowOnLeave(button:GetParentElement()) end function private.DeleteProfileOnClick(button) local profileName = button:GetParentElement():GetContext() local desc = format(L["This will permanently delete the '%s' profile."], profileName) button:GetBaseElement():ShowConfirmationDialog(L["Delete Profile?"], desc, private.DeleteProfileConfirmed, button, profileName) end function private.DeleteProfileConfirmed(button, profileName) TSM.db:DeleteProfile(profileName) button:GetBaseElement():GetElement("content.settings.contentFrame.content"):ReloadContent() end function private.DeleteProfileOnEnter(button) button:ShowTooltip(L["Delete the profile"]) private.ProfileRowOnEnter(button:GetParentElement()) end function private.DeleteProfileOnLeave(button) Tooltip.Hide() private.ProfileRowOnLeave(button:GetParentElement()) end function private.NewProfileInputOnEnterPressed(input) local profileName = input:GetValue() if not TSM.db:IsValidProfileName(profileName) then Log.PrintUser(L["This is not a valid profile name. Profile names must be at least one character long and may not contain '@' characters."]) return elseif TSM.db:ProfileExists(profileName) then Log.PrintUser(L["A profile with this name already exists."]) return end TSM.db:SetProfile(profileName) input:GetBaseElement():GetElement("content.settings.contentFrame.content"):ReloadContent() end function private.AccountSyncRowOnEnter(frame) local account = frame:GetContext() if account then frame:GetElement("sendProfileBtn"):Show() frame:GetElement("removeBtn"):Show() end frame:SetBackgroundColor("ACTIVE_BG", true) frame:Draw() end function private.AccountSyncRowOnLeave(frame) frame:SetBackgroundColor("PRIMARY_BG_ALT", true) frame:GetElement("sendProfileBtn"):Hide() frame:GetElement("removeBtn"):Hide() frame:Draw() end function private.AccountSyncTextOnEnter(text) local account = text:GetParentElement():GetContext() local tooltipLines = TempTable.Acquire() if account then tinsert(tooltipLines, Theme.GetColor("INDICATOR"):ColorText(L["Sync Status"])) local mirrorConnected, mirrorSynced = Sync.GetMirrorStatus(account) local mirrorStatus = nil if not mirrorConnected then mirrorStatus = Theme.GetFeedbackColor("RED"):ColorText(L["Not Connected"]) elseif not mirrorSynced then mirrorStatus = Theme.GetFeedbackColor("YELLOW"):ColorText(L["Updating"]) else mirrorStatus = Theme.GetFeedbackColor("GREEN"):ColorText(L["Up to date"]) end tinsert(tooltipLines, L["Inventory / Gold Graph"]..TSM.CONST.TOOLTIP_SEP..mirrorStatus) tinsert(tooltipLines, L["Profession Info"]..TSM.CONST.TOOLTIP_SEP..TSM.Crafting.Sync.GetStatus(account)) tinsert(tooltipLines, L["Purchase / Sale Info"]..TSM.CONST.TOOLTIP_SEP..TSM.Accounting.Sync.GetStatus(account)) else tinsert(tooltipLines, L["Establishing connection..."]) end text:ShowTooltip(table.concat(tooltipLines, "\n"), nil, 52) TempTable.Release(tooltipLines) private.AccountSyncRowOnEnter(text:GetParentElement()) end function private.AccountSyncTextOnLeave(text) Tooltip.Hide() private.AccountSyncRowOnLeave(text:GetParentElement()) end function private.SendProfileOnClick(button) local player = Sync.GetConnectedCharacterByAccount(button:GetParentElement():GetContext()) if not player then return end TSM.Groups.Sync.SendCurrentProfile(player) end function private.SendProfileOnEnter(button) button:ShowTooltip(L["Send your active profile to this synced account"]) private.AccountSyncRowOnEnter(button:GetParentElement()) end function private.SendProfileOnLeave(button) Tooltip.Hide() private.AccountSyncRowOnLeave(button:GetParentElement()) end function private.RemoveAccountSyncOnClick(button) Sync.RemoveAccount(button:GetParentElement():GetContext()) button:GetBaseElement():GetElement("content.settings.contentFrame.content"):ReloadContent() Tooltip.Hide() Log.PrintUser(L["Account sync removed. Please delete the account sync from the other account as well."]) end function private.RemoveAccountOnEnter(button) button:ShowTooltip(L["Remove this account sync and all synced data from this account"]) private.AccountSyncRowOnEnter(button:GetParentElement()) end function private.RemoveAccountOnLeave(button) Tooltip.Hide() private.AccountSyncRowOnLeave(button:GetParentElement()) end function private.NewAccountSyncInputOnEnterPressed(input) local character = Ambiguate(input:GetValue(), "none") if Sync.EstablishConnection(character) then Log.PrintfUser(L["Establishing connection to %s. Make sure that you've entered this character's name on the other account."], character) private.SyncConnectionChangedCallback() else input:SetValue("") input:Draw() end end