Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement copy/pasting of glance traits #92

Merged
merged 2 commits into from
Mar 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
330 changes: 294 additions & 36 deletions totalRP3/modules/register/main/register_glance.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ local _, TRP3_API = ...;
-- API
TRP3_API.register.glance = {};
local Utils, Events, Globals = TRP3_API.utils, TRP3_API.events, TRP3_API.globals;
local tostring, _G, pairs, type, tinsert, assert, wipe = tostring, _G, pairs, type, tinsert, assert, wipe;
local tostring, _G, pairs, type, tinsert, assert, wipe, bit, strsplit = tostring, _G, pairs, type, tinsert, assert, wipe, bit, strsplit;
local tsize, loc = Utils.table.size, TRP3_API.loc;
local color, getIcon, tableRemove = Utils.str.color, Utils.str.icon, Utils.table.remove;
local setTooltipForSameFrame, toast = TRP3_API.ui.tooltip.setTooltipForSameFrame, TRP3_API.ui.tooltip.toast;
Expand All @@ -33,6 +33,13 @@ local crop = TRP3_API.Ellyb.Strings.crop;
local shouldCropTexts = TRP3_API.ui.tooltip.shouldCropTexts;

local AtFirstGlanceChatLinkModule;

-- CONSTANTS
local TYPE_CHARACTER = TRP3_API.ui.misc.TYPE_CHARACTER;
local TYPE_PET = TRP3_API.ui.misc.TYPE_PET;
local TYPE_BATTLE_PET = TRP3_API.ui.misc.TYPE_BATTLE_PET;
local EMPTY = Globals.empty;

--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-- Glance utils
--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Expand All @@ -50,11 +57,19 @@ local function presetMessage(message, duration)
end

local presetStructureForTargetFrame = {};
local function getSlotPresetDataForList()
wipe(presetStructureForTargetFrame);
local function getSlotPresetDataForList(output)
-- If we're not given an output table then we'll reuse the one created
-- above, to maintain backwards compatibility.
--
-- If we are given one, don't wipe it. Could be an in-progress creation.
if type(output) ~= "table" then
output = presetStructureForTargetFrame;
wipe(output);
end

-- Title
tinsert(presetStructureForTargetFrame, {loc("REG_PLAYER_GLANCE_PRESET_SELECT"), nil});
tinsert(presetStructureForTargetFrame, {loc("REG_PLAYER_GLANCE_PRESET_CREATE"), -1});
tinsert(output, {loc("REG_PLAYER_GLANCE_PRESET_SELECT"), nil});
tinsert(output, {loc("REG_PLAYER_GLANCE_PRESET_CREATE"), -1});
-- Category sorting
local tmp = {};
for category, _ in pairs(GLANCE_PRESETS_CATEGORY) do
Expand All @@ -74,10 +89,10 @@ local function getSlotPresetDataForList()
}
});
end
tinsert(presetStructureForTargetFrame, categoryListElementTarget);
tinsert(output, categoryListElementTarget);
end

return presetStructureForTargetFrame;
return output;
end
TRP3_API.register.glance.getSlotPresetDataForList = getSlotPresetDataForList;

Expand All @@ -92,6 +107,243 @@ local function swapDataFromSlots(dataTab, from, to)
end
TRP3_API.register.glance.swapDataFromSlots = swapDataFromSlots;

--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-- Glance slot context menu population
--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

--- GLANCE_MENU_FLAG_PRESETS indicates the dropdown entries should include
-- options for managing presets.
local GLANCE_MENU_FLAG_PRESETS = 0x01;

--- GLANCE_MENU_FLAG_CLIPBOARD indicates dropdown entries should include
-- options for copying and pasting glances.
local GLANCE_MENU_FLAG_CLIPBOARD = 0x02;

--- GLANCE_MENU_FLAGSET_DEFAULT is a set of flags used by default when
-- populating a list for the glance context menu.
local GLANCE_MENU_FLAGSET_DEFAULT = bit.bor(GLANCE_MENU_FLAG_CLIPBOARD);

--- GLANCE_MENU_FLAGSET_IS_MINE is a set of flags used when populating
-- the preset context menu for a glance owned by the user.
local GLANCE_MENU_FLAGSET_IS_USER = bit.bor(GLANCE_MENU_FLAGSET_DEFAULT, GLANCE_MENU_FLAG_PRESETS);

--- GLANCE_MENU_ENTRY_HANDLERS is a mapping of menu entry value IDs to
-- functions to be called by dispatchGlanceMenuEntryAction.
local GLANCE_MENU_ENTRY_HANDLERS = {};

--- getGlanceMenuEntries returns a table containing a list of entries
-- displayable in a context menu for a slot.
--
-- The entries returned can be filtered by passing flags into this function.
-- By default, only the flags present in GLANCE_MENU_FLAGSET_DEFAULT are
-- used if none are passed.
--
-- @param button The button that will own the menu.
-- @param ... Flags indicating the entries to include. Multiple flags can be
-- passed and will be OR'd together automatically.
local function getGlanceMenuEntries(button, ...)
-- You can either pass a single flag or multiple - we'll combine them
-- automatically.
local flags = (...) and bit.bor(...) or GLANCE_MENU_FLAGSET_DEFAULT;

-- Create a new table for the entries on call.
--
-- If table churn is an issue, I think this should be resolved at a dropdown
-- level - it'd be nice if the dropdown code could take an iterator in
-- the values position and build a list that way and recycle appropriately.
local entries = {}

-- Mirror the clipboard management items.
if bit.band(flags, GLANCE_MENU_FLAG_CLIPBOARD) ~= 0 then
TRP3_API.register.glance.getGlanceMenuClipboardEntries(button, entries);
end

-- Mirror the presets.
if bit.band(flags, GLANCE_MENU_FLAG_PRESETS) ~= 0 then
TRP3_API.register.glance.getSlotPresetDataForList(entries);
end

return entries;
end
TRP3_API.register.glance.getGlanceMenuEntries = getGlanceMenuEntries;

--- dispatchGlanceMenuEntryAction attempts to dispatch any registered handler
-- belonging to the given key. If there is no exact match for a given key,
-- a prefix check will be applied up-to the first occurence of a colon.
--
-- @param key The key of the entry to be dispatched.
-- @param ... Additional arguments to pass to the handler.
-- @return True if a handler exists and was called.
local function dispatchGlanceMenuEntryAction(key, ...)
-- Search the key up-to the first colon for advanced items.
local keyPrefix = tostring(key):match("^[^:]+");

local handler = GLANCE_MENU_ENTRY_HANDLERS[key] or GLANCE_MENU_ENTRY_HANDLERS[keyPrefix];
if handler then
handler(key, ...);
return true;
end
end
TRP3_API.register.glance.dispatchGlanceMenuEntryAction = dispatchGlanceMenuEntryAction;

--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-- Glance editor clipboard management
--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

--- GLANCE_MENU_ENTRY_CLIPBOARD_COPY_PREFIX is the prefix for menu actions
-- that should trigger a copy glance operation.
local GLANCE_MENU_ENTRY_CLIPBOARD_COPY_PREFIX = "clipboard/copy";

--- GLANCE_MENU_ENTRY_CLIPBOARD_PASTE_PREFIX is the prefix for menu actions
-- that should trigger a paste glance operation.
local GLANCE_MENU_ENTRY_CLIPBOARD_PASTE_PREFIX = "clipboard/paste";

--- GLANCE_MENU_CLIPBOARD_ID_CHARACTER is the key used by the clipboard
-- for storing and retrieving a glance slot of a character.
local GLANCE_MENU_CLIPBOARD_ID_CHARACTER = "CHARACTER";

--- GLANCE_MENU_CLIPBOARD_ID_CHARACTER is the key used by the clipboard
-- for storing and retrieving a glance slot of a companion.
local GLANCE_MENU_CLIPBOARD_ID_COMPANION = "COMPANION";

--- clipboardCurrentEntries holds the current clipboard selections for
-- player and companion profiles.
local clipboardCurrentEntries = {
-- Preallocate the slots and document the fact we use them.
[GLANCE_MENU_CLIPBOARD_ID_CHARACTER] = nil,
[GLANCE_MENU_CLIPBOARD_ID_COMPANION] = nil,
};

--- getGlanceMenuClipboardEntries populates a given output list with items
-- suitable for use on a context menu dropdown.
--
-- The entries will always contain a "Copy" item that copies a slot
-- into our local store, and will include a "Paste" item if there is data
-- in the clipboard.
--
-- The button passed to this function must have a global name, or no entries
-- are added. This is because we rely on the button to access the data of the
-- entry to copy.
--
-- @param button The button that will own the dropdown menu. Must have a
-- global name.
-- @param output The list of entries to append our own menu items to.
local function getGlanceMenuClipboardEntries(button, output)
-- If the button has no name we'll fail because we can't do a copy,
-- and I don't much trust our chances of doing a paste either.
if not button:GetName() then
return;
end

-- What clipboard section should we be dealing with here?
local clipboardType = (button.targetType == TYPE_CHARACTER)
and GLANCE_MENU_CLIPBOARD_ID_CHARACTER
or GLANCE_MENU_CLIPBOARD_ID_COMPANION;

-- Create a key for the copy entry of the form "prefix:type,buttonName".
local copyKey = string.format("%s:%s,%s",
GLANCE_MENU_ENTRY_CLIPBOARD_COPY_PREFIX,
clipboardType,
button:GetName()
);

tinsert(output, { loc("REG_PLAYER_GLANCE_MENU_COPY"), copyKey });

-- The paste operation should only be present if you have something,
-- and if this is your own profile.
--
-- As this is a destructive operation potentially, we'll also validate
-- the slot ID is present since defaulting it could silently misbehave.
if not clipboardCurrentEntries[clipboardType] or not button.isCurrentMine or not button.slot then
return;
end

-- Paste key follows the same principle as the copy key except in the
-- form "prefix:type,slot[,target,profile]".
local pasteKey = string.format("%s:%s,%s,%s,%s",
GLANCE_MENU_ENTRY_CLIPBOARD_PASTE_PREFIX,
clipboardType,
button.slot,
button.targetID or "",
button.profileID or ""
);

-- We'll also put the name of the item that you'd paste into the menu
-- so that you have a rough idea on what would happen.
local pasteName = clipboardCurrentEntries[clipboardType].TI or "";
tinsert(output, { loc("REG_PLAYER_GLANCE_MENU_PASTE"):format(pasteName), pasteKey });
end
TRP3_API.register.glance.getGlanceMenuClipboardEntries = getGlanceMenuClipboardEntries;

--- onGlanceMenuCopySelected is called when the "Copy" item in a glance slot
-- menu is clicked. Rather self-explanatorily, it'll copy the item.
--
-- @param key The key of the menu item itself with comma-delimited parameters
-- of the form "prefix:type,button".
local function onGlanceMenuCopySelected(key)
-- Split the key up into its little bits.
local segments = key:match(":(.+)$");
local typeID, buttonName = strsplit(",", segments);

-- Don't do much of anything if the key is malformed.
if not typeID or not buttonName then
return;
end

-- Copy the data stored on the button itself.
local button = _G[buttonName];
if not button then
return;
end

clipboardCurrentEntries[typeID] = {};
Utils.table.copy(clipboardCurrentEntries[typeID], button.data);
end

--- onGlanceMenuCopySelected is called when the "Paste" item in a glance slot
-- menu is clicked. To no great surprise, this handles pasting the data
-- in our clipboard to the slot in question.
--
-- @param key The key of the menu item itself with comma-delimited parameters
-- of the form "prefix:type,slot[,target,profile]".
local function onGlanceMenuPasteSelected(key)
-- Split the key up into its little bits.
local segments = key:match(":(.+)$");
local typeID, slotID, targetID, profileID = strsplit(",", segments);

-- Nil out the optionals if they aren't a thing.
targetID = (targetID ~= "") and targetID or nil;
profileID = (profileID ~= "") and profileID or nil;

-- Ignore broken keys.
if not typeID or not slotID then
return;
end

-- Get the data to be pasted, if there are none we'll do nothing.
local pasteData = clipboardCurrentEntries[typeID];
if not pasteData then
return;
end

-- Dispatch the correct update function.
local updateFunc;
if typeID == GLANCE_MENU_CLIPBOARD_ID_CHARACTER then
updateFunc = TRP3_API.register.applyPeekSlot;
elseif typeID == GLANCE_MENU_CLIPBOARD_ID_COMPANION then
updateFunc = TRP3_API.companions.player.applyPeekSlot;
else
-- Bad type ID, can't update.
return;
end

return updateFunc(slotID, pasteData.IC, pasteData.AC, pasteData.TI, pasteData.TX, false, targetID, profileID);
end

-- Register the handlers in the table.
GLANCE_MENU_ENTRY_HANDLERS[GLANCE_MENU_ENTRY_CLIPBOARD_COPY_PREFIX] = onGlanceMenuCopySelected;
GLANCE_MENU_ENTRY_HANDLERS[GLANCE_MENU_ENTRY_CLIPBOARD_PASTE_PREFIX] = onGlanceMenuPasteSelected;

--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-- Glance slot presets
--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Expand Down Expand Up @@ -225,12 +477,6 @@ local function onInit()
ui_GlanceBar = CreateFrame("Frame", "TRP3_GlanceBar", UIParent, "TRP3_GlanceBarTemplate");
end

-- CONSTANTS
local TYPE_CHARACTER = TRP3_API.ui.misc.TYPE_CHARACTER;
local TYPE_PET = TRP3_API.ui.misc.TYPE_PET;
local TYPE_BATTLE_PET = TRP3_API.ui.misc.TYPE_BATTLE_PET;
local EMPTY = Globals.empty;

--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-- Logic
--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Expand Down Expand Up @@ -305,6 +551,12 @@ local function onGlanceSelection(presetAction, button)
return;
end

-- If we've got a handler in the dispatcher table then this'll call it
-- and return a truthy value of some description.
if dispatchGlanceMenuEntryAction(presetAction, button) then
return;
end

if type(presetAction) == "string" then
local action = presetAction:sub(1, 1);
if action == LOAD_PREFIX then
Expand All @@ -319,33 +571,39 @@ local function onGlanceSelection(presetAction, button)
end

local function onGlanceSlotClick(button, clickType)
if button.isCurrentMine then
if clickType == "LeftButton" then
if IsShiftKeyDown() then
local glanceTab = getDataDefault("misc/PE", EMPTY, get("player") or EMPTY);
local glance = glanceTab[button.slot];
if glance and glance.AC then
TRP3_API.ChatLinks:OpenMakeImportablePrompt(loc.CL_GLANCE, function(canBeImported)
AtFirstGlanceChatLinkModule:InsertLink(button.slot, canBeImported);
end);
end
else
if clickType == "LeftButton" and button.isCurrentMine then
if IsShiftKeyDown() then
local glanceTab = getDataDefault("misc/PE", EMPTY, get("player") or EMPTY);
local glance = glanceTab[button.slot];
if glance and glance.AC then
TRP3_API.ChatLinks:OpenMakeImportablePrompt(loc.CL_GLANCE, function(canBeImported)
AtFirstGlanceChatLinkModule:InsertLink(button.slot, canBeImported);
end);
end
else

if TRP3_AtFirstGlanceEditor:IsVisible() and TRP3_AtFirstGlanceEditor.current == button then
TRP3_AtFirstGlanceEditor:Hide();
else
local x, y = GetCursorPosition();
local scale = UIParent:GetEffectiveScale();
y = y / scale;
TRP3_API.ui.frame.configureHoverFrame(TRP3_AtFirstGlanceEditor, button, y <= 200 and "BOTTOM" or "TOP");
TRP3_AtFirstGlanceEditor.current = button;
openGlanceEditor(button.slot, button.data or button.glanceTab[button.slot] or {}, getOnGlanceEditorConfirmFunction(button), TRP3_AtFirstGlanceEditor, button.targetID, button.profileID);
end
if TRP3_AtFirstGlanceEditor:IsVisible() and TRP3_AtFirstGlanceEditor.current == button then
TRP3_AtFirstGlanceEditor:Hide();
else
local x, y = GetCursorPosition();
local scale = UIParent:GetEffectiveScale();
y = y / scale;
TRP3_API.ui.frame.configureHoverFrame(TRP3_AtFirstGlanceEditor, button, y <= 200 and "BOTTOM" or "TOP");
TRP3_AtFirstGlanceEditor.current = button;
openGlanceEditor(button.slot, button.data or button.glanceTab[button.slot] or {}, getOnGlanceEditorConfirmFunction(button), TRP3_AtFirstGlanceEditor, button.targetID, button.profileID);
end
elseif clickType == "RightButton" then
displayDropDown(button, getSlotPresetDataForList(), function(value) onGlanceSelection(value, button) end, 0, true);
end
elseif clickType == "RightButton" then
-- Pick the right set of flags for getting the entries used in the menu.
local flags = GLANCE_MENU_FLAGSET_DEFAULT;
if button.isCurrentMine then
flags = GLANCE_MENU_FLAGSET_IS_USER;
end

local values = getGlanceMenuEntries(button, flags);
local onSelected = function(value) onGlanceSelection(value, button) end;

displayDropDown(button, values, onSelected, 0, true);
end
end
TRP3_API.register.glance.onGlanceSlotClick = onGlanceSlotClick;
Expand Down
Loading