You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2966 lines
91 KiB

local _, addon = ...
local SetModelLight = addon.TransitionAPI.SetModelLight;
local GetMouseFocus = addon.TransitionAPI.GetMouseFocus;
local BACKGROUND_INSET = 3.5;
local TEXT_INSET = 16;
local SPEECH_BALLOON_MIN_SIZE = 16;
local SIMPLE_BALLON_MIN_SIZE = 40;
local TEXTURE_PATH_PREFIX = "Interface\\AddOns\\Narcissus\\Art\\Modules\\PhotoMode\\SpeechBalloon\\";
local backdropInfo = {
white = {
nineSliceName = "chatBubbleWhite",
tailSize = 40,
tailFile = "Tail-White",
},
black = {
nineSliceName = "chatBubbleBlack",
tailSize = 48,
tailFile = "Tail-Black",
},
};
local STROKE_SIZE = {
2, 3, 4, 6;
};
--------------------------------------------------------------
local max = math.max;
local min = math.min;
local floor = math.floor;
local sqrt = math.sqrt;
local abs = math.abs;
local atan2 = math.atan2;
local pi = math.pi;
local pi90 = pi/2;
local upper = string.upper;
local strsplit = strsplit;
local L = Narci.L;
local NarciAPI = NarciAPI;
local FadeFrame = NarciFadeUI.Fade;
local IsMouseButtonDown = IsMouseButtonDown;
local After = C_Timer.After;
local function round(a)
if a then
return floor(a + 0.5)
else
return 0
end
end
local function GetCircleCenter(m, n, p, q, r, positive) --2D points: (m, n) (p, q) radius: r
if q == n then
return false
end
local K = (m - p)/(q - n);
local B = (n + q)/2 - K*(m + p)/2
local a = 1 + K*K;
local b = 2 * (K * B - K * n - m);
local c = m*m + n*n + B*B - 2*B*n - r*r;
local delta = b ^ 2 - 4 * a * c;
if delta and delta >= 0 then
if positive then
positive = 1;
else
positive = -1;
end
local cx = (- b + positive* sqrt(delta)) / ( 2*a );
local cy = K * cx + B;
return cx, cy
else
return false;
end
end
local function GetDegrees(Px, Py, Ox, Oy)
local x = Px - Ox;
local y = Py - Oy;
return atan2(x, y);
end
local function GetFontData(isBold, isItalic)
local fontObject;
if isBold then
if isItalic then
fontObject = NarciSpeechBalloonFontBoldItalic
else
fontObject = NarciSpeechBalloonFontBold
end
else
if isItalic then
fontObject = NarciSpeechBalloonFontItalic
else
fontObject = NarciSpeechBalloonFontRegular
end
end
return fontObject:GetFont()
end
local ActorNameFont = {
["CN"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8},
["RM"] = {"Interface\\AddOns\\Narcissus\\Font\\OpenSans-Semibold.ttf", 9},
["RU"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSans-Medium.ttf", 8},
["KR"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8},
["JP"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8},
};
local function SmartFontType(fontstring, text)
--Automatically apply different font based on given text languange. Change text color after this step.
if not fontstring then return; end;
fontstring:SetText(text);
local Language = NarciAPI.LanguageDetector(text);
if Language and ActorNameFont[Language] then
fontstring:SetFont(ActorNameFont[Language][1] , ActorNameFont[Language][2], "");
end
end
-------------------------------------------------------------------
local Container, EditButton, PrimaryEditBox, Tooltip, Toolbar, ModelDropDownMenu, ColorDropDown, FontSizeDropDown;
local function HideEditor()
Container:HideAllControlNodes();
Toolbar:Hide();
EditButton:Hide();
EditButton:ResetState();
PrimaryEditBox:Hide();
PrimaryEditBox:ConfirmChanges();
end
local function IsWidgetFocused(frame)
return frame and frame:IsFocused()
end
-------------------------------------------------------------------
local function CreateUpdater()
local frame = CreateFrame("Frame");
frame:Hide();
frame.t = 0;
frame.duration = 0.5;
return frame
end
local simple_updateWidth = CreateUpdater();
simple_updateWidth.direction = 1;
simple_updateWidth:SetScript("OnUpdate", function(self, elapsed)
local cursorX, cursorY = GetCursorPosition();
cursorX = cursorX - self.dx;
local x = self.direction * (cursorX - self.x0); --distance from cursor to center
self.parent:SetBoundaryWidth(2 * x);
end);
local simple_updateHeight = CreateUpdater();
simple_updateHeight:SetScript("OnUpdate", function(self, elapsed)
local cursorX, cursorY = GetCursorPosition();
cursorY = cursorY - self.dy;
local y = cursorY - self.y0; --distance from cursor to center
self.parent:SetBoundaryHeight(2 * y);
end);
local simple_updateTailAttach = CreateUpdater();
simple_updateTailAttach:SetScript("OnUpdate", function(self, elapsed)
local cursorX = GetCursorPosition();
cursorX = cursorX - self.dx;
local x = cursorX - self.x0;
local maxOffset = self.maxOffset;
if x > maxOffset then
x = maxOffset;
elseif x < -maxOffset then
x = -maxOffset;
end
self.parent:SetTailAttachOffset(x);
end);
-------------------------------------------------------------------
local SharedSpeechBallonMixin = {};
function SharedSpeechBallonMixin:OnDragStart()
self:StartMoving();
end
function SharedSpeechBallonMixin:OnDragStop()
self:StopMovingOrSizing();
--convert anchor to CENTER;
local x, y = self:GetCenter();
self:ClearAllPoints();
self:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x, y);
end
function SharedSpeechBallonMixin:OnEnter()
if not IsMouseButtonDown() then
if EditButton.parentObject == self then
EditButton:FadeIn(0.25);
end
end
end
function SharedSpeechBallonMixin:OnLeave()
if not self:IsMouseOver() then
EditButton:FadeOut(0.25);
end
end
function SharedSpeechBallonMixin:SetBold(state)
self.isBold = state;
self:UpdateText();
end
function SharedSpeechBallonMixin:SetItalic(state)
self.isItalic = state;
self:UpdateText();
end
function SharedSpeechBallonMixin:SetAllCaps(state)
self.isAllCaps = state;
self:UpdateText();
end
function SharedSpeechBallonMixin:SetNodesTransparency(value)
value = value or 1;
local visible = (value ~= 0)
local node;
for i = 1, #self.Nodes do
node = self.Nodes[i];
node:SetAlpha(value);
node:SetShown(visible);
end
end
function SharedSpeechBallonMixin:OnClick()
if self.Nodes[1]:IsShown() then
HideEditor();
else
Toolbar:SetParentObject(self);
Container:HideAllControlNodes(self);
EditButton:SetParentObject(self);
if not IsMouseButtonDown() then
EditButton:FadeIn(0.25);
end
end
end
function SharedSpeechBallonMixin:IsFocused()
if self:IsMouseOver(16, -16, -16, 16) then
return true
else
for i = 1, #self.Nodes do
if self.Nodes[i]:IsMouseOver() then
return true
end
end
end
return false
end
-------------------------------------------------------------------
NarciSimpleSpeechBalloonMixin = CreateFromMixins(SharedSpeechBallonMixin);
function NarciSimpleSpeechBalloonMixin:OnLoad()
self:RegisterForDrag("LeftButton");
self.balloonTpye = 1;
self:SelectTheme(2);
self:SetText("");
self:SetTailAttachOffset(0);
self:SetBoundaryWidth(180);
self:SetBoundaryHeight(90);
self:SetNodesTransparency(0);
self:UpdateText();
self.padding = 14;
self.WidthNode.transformFunc = function(dx, dy)
simple_updateWidth.x0 = self:GetCenter();
simple_updateWidth.direction = -1;
simple_updateWidth.parent = self;
simple_updateWidth.dx, simple_updateWidth.dy = dx, dy;
simple_updateWidth:Show();
end
self.WidthNodeRight.transformFunc = function(dx, dy)
simple_updateWidth.x0 = self:GetCenter();
simple_updateWidth.direction = 1;
simple_updateWidth.parent = self;
simple_updateWidth.dx, simple_updateWidth.dy = dx, dy;
simple_updateWidth:Show();
end
self.HeightNode.transformFunc = function(dx, dy)
local _;
_, simple_updateHeight.y0 = self:GetCenter();
simple_updateHeight.dx, simple_updateHeight.dy = dx, dy;
simple_updateHeight.parent = self;
simple_updateHeight:Show();
end
self.TailAttachNode.transformFunc = function(dx, dy)
simple_updateTailAttach.parent = self;
simple_updateTailAttach.x0 = self:GetCenter();
simple_updateTailAttach.dx = dx;
simple_updateTailAttach.maxOffset = self:GetWidth()/2 - 16;
simple_updateTailAttach:Show();
end
end
function NarciSimpleSpeechBalloonMixin:AutoSizing()
local textHeight = round( self.ShownText:GetStringHeight() );
textHeight = textHeight - 1;
self:SetHeight( max(SIMPLE_BALLON_MIN_SIZE, 2*(BACKGROUND_INSET + TEXT_INSET) + textHeight) );
local textWidth = round(self.ShownText:GetStringWidth());
self:SetWidth( max(SIMPLE_BALLON_MIN_SIZE, 2*(BACKGROUND_INSET + TEXT_INSET) + textWidth) );
end
function NarciSimpleSpeechBalloonMixin:SetBoundaryWidth(width)
local minWidth = SIMPLE_BALLON_MIN_SIZE;
if width < minWidth then
width = minWidth;
end
self.WidthNode:SetPoint("CENTER", self, "CENTER", -width/2, 0);
self.WidthNodeRight:SetPoint("CENTER", self, "CENTER", width/2, 0);
self:SetWidth(width + 6);
--Update tail position
local maxOffset = width/2 - 16;
local tailAttachOffset = self.tailAttachOffset or 0;
self.maxOffset = maxOffset;
if tailAttachOffset < -maxOffset then
self:SetTailAttachOffset(-maxOffset);
elseif tailAttachOffset > maxOffset then
self:SetTailAttachOffset(maxOffset);
end
end
function NarciSimpleSpeechBalloonMixin:SetBoundaryHeight(height)
local minHeight = SIMPLE_BALLON_MIN_SIZE;
if height < minHeight then
height = minHeight;
end
self.HeightNode:SetPoint("CENTER", self, "CENTER", 0, height/2);
self:SetHeight(height + 6);
end
function NarciSimpleSpeechBalloonMixin:SetTailAttachOffset(offset)
if offset > -8 and offset < 8 then
offset = 0;
end
if offset < 0 then
if self.facingRight then
self.facingRight = false;
self.Tail:SetTexCoord(0, 1, 0, 1);
end
elseif offset > 0 then
if not self.facingRight then
self.facingRight = true;
self.Tail:SetTexCoord(1, 0, 0, 1);
end
end
self.Tail:SetPoint("CENTER", self, "BOTTOM", offset, 4);
self.TailAttachNode:SetPoint("CENTER", self, "BOTTOM", offset, 10);
self.tailAttachOffset = offset;
end
function NarciSimpleSpeechBalloonMixin:SetText(str)
str = str or "";
self.rawText = str;
if self.isAllCaps then
str = upper(str);
end
self.ShownText:SetText(str);
self:AutoSizing();
end
local THEME_PRESETS = {
--For Simple Balloon
{1, 1, 1},
{0.15, 0.15, 0.15},
};
function NarciSimpleSpeechBalloonMixin:SelectTheme(themeID)
themeID = themeID or 1; --Default White
self.themeID = themeID;
self.backgroundColor = THEME_PRESETS[themeID];
local info;
if themeID == 1 then
info = backdropInfo.white;
self.ShownText:SetTextColor(0, 0, 0);
self.ShownText:SetShadowColor(0, 0, 0, 0);
self.ShownText:SetShadowOffset(0, 0);
else
info = backdropInfo.black;
self.ShownText:SetTextColor(1, 0.91, 0.647);
self.ShownText:SetShadowColor(0, 0, 0, 1);
self.ShownText:SetShadowOffset(1, -1);
end
local tailSize = info.tailSize;
self.Tail:SetSize(tailSize, tailSize);
self.Tail:SetTexture(TEXTURE_PATH_PREFIX.. info.tailFile, nil, nil, "TRILINEAR");
NarciAPI.NineSliceUtil.SetUpBackdrop(self, info.nineSliceName);
end
function NarciSimpleSpeechBalloonMixin:UpdateText()
local font, height = GetFontData(self.isBold, self.isItalic);
height = round(height);
self.fontPath = font;
if not self.fontHeight then
self.fontHeight = height;
else
height = self.fontHeight;
end
if self.isAllCaps then
self.ShownText:SetText(upper(self.rawText));
else
self.ShownText:SetText(self.rawText);
end
self.ShownText:SetFont(font, height, "");
end
function NarciSimpleSpeechBalloonMixin:SetFontColor(r, g, b)
self.ShownText:SetTextColor(r, g, b);
end
function NarciSimpleSpeechBalloonMixin:OnClick()
if self.Nodes[1]:IsShown() then
HideEditor();
else
Toolbar:SetParentObject(self);
Container:HideAllControlNodes(self);
EditButton:SetParentObject(self);
if not IsMouseButtonDown() then
EditButton:FadeIn(0.25);
end
end
end
---------------------------------------------------------------------------------------------------------
local LetteringSystem = {};
local wordFrame = CreateFrame("Frame");
wordFrame:SetAlpha(0);
wordFrame:SetSize(8, 8);
wordFrame:SetPoint("TOP", UIParent, "BOTTOM", 0, 0);
LetteringSystem.wordContainer = wordFrame:CreateFontString(nil, "BACKGROUND", "NarciSpeechBalloonFontItalic");
function LetteringSystem:GetWordWidth(word)
self.wordContainer:SetText(word);
local width = self.wordContainer:GetStringWidth();
return width
end
function LetteringSystem:EvaluateWidth(word, boundaryWidth)
return (self:GetWordWidth(word) <= boundaryWidth) ;
end
function LetteringSystem:GetLine(object, index)
if not object.lines then
object.lines = {};
end
local line = object.lines[index];
if not line then
local font, height = object.fontPath, object.fontHeight;
line = object:CreateFontString(nil, "OVERLAY");
line:SetFont(font, height, "");
local r, g, b = object.ShownText:GetTextColor();
line:SetTextColor(r, g, b);
line:SetMaxLines(1);
line:SetHeight(round(height) + 0.10);
tinsert(object.lines, line);
if index == 1 then
line:SetPoint("TOP", object, "TOP", 0, -object.padding);
else
line:SetPoint("TOP", object.lines[index - 1], "BOTTOM", 0, -2);
end
end
line:SetWidth( object:GetWidth() - object.padding*2 );
return line
end
function LetteringSystem:UpdateFont(object)
if object.lines then
local r, g, b = object.ShownText:GetTextColor();
local line;
local font, height = object.fontPath, object.fontHeight;
for i = 1, #object.lines do
line = object.lines[i];
line:SetFont(font, height, "");
line:SetTextColor(r, g, b);
line:SetHeight(round(height) + 0.10);
end
end
self:SetText(object);
end
function LetteringSystem:GetBoundaryWidth(object, lineIndex)
local w, h = object:GetSize();
local yMax = h/2;
local R = object.cornerRadius;
local a, b = -w/2 + R, yMax - R;
local padding = object.padding;
local x, y;
y = yMax - padding * lineIndex - 2*(lineIndex - 1)
if y > yMax - R then
y = yMax - padding * lineIndex - 2*(lineIndex - 1);
local z = (R - padding)^2 - (y - b)^2;
if z < 0 then z = 0; end
x = sqrt(z) - a;
elseif y < -yMax + R then
y = y - 14;
local z = (R - padding)^2 - (y + b)^2;
if z < 0 then z = 0; end
x = sqrt(z) - a;
else
x = w/2 - padding;
end
return 2*x
end
function LetteringSystem:ShowText(object, state)
if object.lines then
for i = 1, #object.lines do
object.lines[i]:SetShown(state);
end
end
end
function LetteringSystem:SetText(object, text)
if not object.textWrapping then
if object.lines then
for i = 1, #object.lines do
object.lines[i]:SetText("");
end
end
return
end
if text then
object.rawText = text;
else
text = object.rawText or "";
end
if object.isAllCaps then
text = upper(text);
end
local words = { strsplit(" ", text) };
if not words then return end;
local line, lineString, fineString;
local wordIndex = 0;
local numLines = 0;
local numWords = #words;
while( (numLines < 20) and (wordIndex < numWords) ) do
wordIndex = wordIndex + 1;
lineString = words[wordIndex];
numLines = numLines + 1;
local boundaryWidth = self:GetBoundaryWidth(object, numLines);
line = self:GetLine(object, numLines);
line:SetWidth(boundaryWidth);
line:SetText(lineString);
local nextWord;
for i = wordIndex + 1, numWords do
wordIndex = wordIndex + 1;
nextWord = " ".. words[i];
line:SetText(lineString..nextWord);
if not line:IsTruncated() then
lineString = lineString..nextWord;
else
wordIndex = wordIndex -1;
line:SetText(lineString);
break;
end
end
--print(numLines)
end
for i = numLines + 1, #object.lines do
object.lines[i]:SetText("");
end
end
local updateCorner = CreateUpdater()
updateCorner:SetScript("OnUpdate", function(self, elapsed)
local cursorX, cursorY = GetCursorPosition();
cursorX = cursorX - self.dx;
cursorY = cursorY - self.dy;
--local d = sqrt( (cursorX - self.x0)^2 + (cursorY - self.y0)^2 );
--d = d / 1.4142;
local r = min(cursorX - self.x0, self.y0- cursorY);
if r > self.maxRadius then
r = self.maxRadius;
elseif r <= 0 then
r = 0.1;
end
self.parent:SetCornerRadius(r);
self.t = self.t + elapsed;
if self.t >= self.duration then
self.t = 0;
LetteringSystem:SetText(self.parent);
end
end);
local function SetParentObject(object)
local updateCorner = updateCorner;
updateCorner.maxRadius = min(object:GetHeight(), object:GetWidth())/2
updateCorner.parent = object;
end
local updateWidth = CreateUpdater()
updateWidth.direction = 1;
updateWidth:SetScript("OnUpdate", function(self, elapsed)
local cursorX, cursorY = GetCursorPosition();
cursorX = cursorX - self.dx;
local x = self.direction * (cursorX - self.x0); --distance from cursor to center
self.parent:SetBoundaryWidth(2 * x);
self.t = self.t + elapsed;
if self.t >= self.duration then
self.t = 0;
LetteringSystem:SetText(self.parent);
end
end);
local updateHeight = CreateUpdater()
updateHeight:SetScript("OnUpdate", function(self, elapsed)
local cursorX, cursorY = GetCursorPosition();
cursorY = cursorY - self.dy;
local y = cursorY - self.y0; --distance from cursor to center
self.parent:SetBoundaryHeight(2 * y);
self.t = self.t + elapsed;
if self.t >= self.duration then
self.t = 0;
LetteringSystem:SetText(self.parent);
end
end);
local updateTailEnd = CreateUpdater()
updateTailEnd:SetScript("OnUpdate", function(self, elapsed)
local cursorX, cursorY = GetCursorPosition();
cursorX = cursorX - self.dx;
cursorY = cursorY - self.dy;
local x, y = cursorX - self.x0, cursorY - self.y0;
if y > 0 then
y = 0;
end
self.parent:SetTailEnd(x, y);
end);
local updateTailAttach = CreateUpdater()
updateTailAttach:SetScript("OnUpdate", function(self, elapsed)
local cursorX = GetCursorPosition();
cursorX = cursorX - self.dx;
local x = cursorX - self.x0;
local maxOffset = self.maxOffset;
if x > maxOffset then
x = maxOffset;
elseif x < -maxOffset then
x = -maxOffset;
end
self.parent:SetTailAttachOffset(x);
end);
local updateTailWidth = CreateUpdater()
updateTailWidth:SetScript("OnUpdate", function(self, elapsed)
local cursorX = GetCursorPosition();
cursorX = cursorX - self.dx;
local x = cursorX - self.x0;
if x < 10 then
x = 10;
elseif x > 40 then
x = 40;
end
self.parent:SetTailWidth(x);
end);
local updateTailArc = CreateUpdater()
updateTailArc:SetScript("OnUpdate", function(self, elapsed)
local cursorX = GetCursorPosition();
local x = cursorX - self.dx - self.x0;
if self.facingRight then
if x < 10 then
x = 10;
elseif x > 80 then
x = 80;
end
else
if x > -10 then
x = 10;
elseif x < -80 then
x = 80;
end
end
x = abs(x);
self.parent:SetTailArcRadius(4*x);
end);
local function Node_OnMouseUp()
simple_updateHeight:Hide();
simple_updateWidth:Hide();
simple_updateTailAttach:Hide();
updateWidth:Hide();
updateHeight:Hide();
updateCorner:Hide();
updateTailEnd:Hide();
updateTailAttach:Hide();
updateTailWidth:Hide();
updateTailArc:Hide();
end
local function ColorButton_OnClick(self)
self.parent:SetBackgroundColor(237/255, 28/255, 36/255);
end
NarciAdjustableSpeechBalloonMixin = CreateFromMixins(SharedSpeechBallonMixin);
function NarciAdjustableSpeechBalloonMixin:OnLoad()
self.rawText = "";
self.padding = 12;
self.corners = {self.CornerTopLeft, self.CornerTopRight, self.CornerBottomLeft, self.CornerBottomRight};
self.borders = {self.BorderMaskTopLeft, self.BorderMaskTopRight, self.BorderMaskBottomLeft, self.BorderMaskBottomRight};
self:RegisterForDrag("LeftButton");
self.CornerNode.isBoundaryButton = true;
self.CornerNode.transformFunc = function(dx, dy)
updateCorner.uiScale = UIParent:GetEffectiveScale();
updateCorner.x0 = self:GetLeft();
updateCorner.y0 = self:GetTop();
updateCorner.dx, updateCorner.dy = dx, dy;
SetParentObject(self);
updateCorner:Show();
end
self.WidthNode.isBoundaryButton = true;
self.WidthNode.transformFunc = function(dx, dy)
updateWidth.x0 = self:GetCenter();
updateWidth.direction = -1;
updateWidth.parent = self;
updateWidth.dx, updateWidth.dy = dx, dy;
updateWidth:Show();
end
self.WidthNodeRight.isBoundaryButton = true;
self.WidthNodeRight.transformFunc = function(dx, dy)
updateWidth.x0 = self:GetCenter();
updateWidth.direction = 1;
updateWidth.parent = self;
updateWidth.dx, updateWidth.dy = dx, dy;
updateWidth:Show();
end
self.HeightNode.isBoundaryButton = true;
self.HeightNode.transformFunc = function(dx, dy)
local _;
_, updateHeight.y0 = self:GetCenter();
updateHeight.dx, updateHeight.dy = dx, dy;
updateHeight.parent = self;
updateHeight:Show();
end
self.TailEndNode.transformFunc = function(dx, dy)
updateTailEnd.parent = self;
updateTailEnd.x0 = self:GetCenter();
updateTailEnd.y0 = self:GetBottom();
updateTailEnd.dx, updateTailEnd.dy = dx, dy;
updateTailEnd:Show();
end
self.TailAttachNode.transformFunc = function(dx, dy)
updateTailAttach.parent = self;
updateTailAttach.x0 = self:GetCenter();
updateTailAttach.dx = dx;
updateTailAttach.maxOffset = self:GetWidth()/2 - self.cornerRadius - self.tailWidth/2;
updateTailAttach:Show();
end
self.TailWidthNode.transformFunc = function(dx, dy)
updateTailWidth.parent = self;
updateTailWidth.x0 = self:GetCenter() + self.tailAttachOffset;
updateTailWidth.dx = dx;
updateTailWidth:Show();
end
self.TailArcNode.transformFunc = function(dx, dy)
updateTailArc.parent = self;
updateTailArc.x0 = self.TailEndNode:GetCenter();
updateTailArc.dx = dx;
updateTailArc.facingRight = self.facingRight;
updateTailArc:Show();
end
self.ColorButton.parent = self;
self.ColorButton:SetScript("OnClick", ColorButton_OnClick);
self:SetNodesTransparency(0);
self:SetBorderThickness(3);
self:SetCornerRadius(6);
self:SetBoundaryWidth(180);
self:SetBoundaryHeight(90);
self:SetPadding(12);
self:SetTail(-12, -20, 0, 20, 200);
self:SetBackgroundColor(1, 1, 1);
self:SetBorderColor(0, 0, 0);
self:SetTextWrapping(false);
self:UpdateText();
LetteringSystem:SetText(self, "");
end
function NarciAdjustableSpeechBalloonMixin:SetBorderThickness(value)
self.borderPixelSize = value;
local pixel = NarciAPI.GetPixelForWidget(self, value);
value = pixel; --Pixel Scale
self.thickness = value;
local cornerRadius = self.CornerTopRight:GetWidth();
self.BorderLeft:SetPoint("TOPLEFT", self, "TOPLEFT", -value, value);
self.BorderLeft:SetPoint("BOTTOMRIGHT", self, "BOTTOM", 0, -value);
self.BorderRight:SetPoint("TOPLEFT", self, "TOP", -0, value);
self.BorderRight:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", value, -value);
self.BorderMaskTopLeft:SetSize(cornerRadius + value, cornerRadius + value);
self.BorderMaskTopRight:SetSize(cornerRadius + value, cornerRadius + value);
self.BorderMaskBottomLeft:SetSize(cornerRadius + value, cornerRadius + value);
self.BorderMaskBottomRight:SetSize(cornerRadius + value, cornerRadius + value);
if self.tailEndOffsetX then
self:UpdateTail();
else
local tailD = 2 * (self.Circle1:GetWidth());
local tailBorderThickness = value * 1.832;
self.TailBorderCircle1:SetSize(tailD + tailBorderThickness, tailD + tailBorderThickness);
self.TailBorderCircle2:SetSize(tailD + tailBorderThickness, tailD + tailBorderThickness);
end
end
function NarciAdjustableSpeechBalloonMixin:SetCornerRadius(r)
local showMask = true;
local CornerNode = self.CornerNode;
if r <= 4 then
showMask = false;
CornerNode:SetPoint("CENTER", self, "TOPLEFT", 4, -4);
CornerNode.Mark:SetPoint("CENTER", CornerNode, "CENTER", -4/1.414 - 2, 4/1.414 + 2);
CornerNode.Mark:SetTexCoord(0, 1, 0, 1);
self.cornerRadius = 0;
else
CornerNode:SetPoint("CENTER", self, "TOPLEFT", r, -r);
self.cornerRadius = r;
if r < 25 then
CornerNode.Mark:SetTexCoord(0, 1, 0, 1);
CornerNode.Mark:SetPoint("CENTER", CornerNode, "CENTER", -r/1.414 - 2, r/1.414 + 2);
else
CornerNode.Mark:SetTexCoord(1, 0, 1, 0);
CornerNode.Mark:SetPoint("CENTER", CornerNode, "CENTER", -r/1.414 + 2, r/1.414 - 2);
end
end
for i = 1, #self.corners do
self.corners[i]:SetSize(r, r);
end
local r2 = r + (self.thickness or 2);
for i = 1, #self.borders do
self.borders[i]:SetSize(r2, r2);
end
for i = 1, #self.corners do
self.corners[i]:SetShown(showMask);
end
for i = 1, #self.borders do
self.borders[i]:SetShown(showMask);
end
self:UpdateTailPosition();
--Reposition EidtButton
if EditButton then
local offset = (0.4142 * r + 24)/1.4142;
EditButton:SetPoint("CENTER", self, "BOTTOMRIGHT", -offset, offset);
end
end
function NarciAdjustableSpeechBalloonMixin:SetBoundaryWidth(width)
local minWidth = max(2 * self.CornerTopLeft:GetWidth(), SPEECH_BALLOON_MIN_SIZE + 2*self.padding);
if width < minWidth then
width = minWidth;
end
self.WidthNode:SetPoint("CENTER", self, "CENTER", -width/2, 0);
self.WidthNodeRight:SetPoint("CENTER", self, "CENTER", width/2, 0);
self:SetWidth(width);
self:UpdateTailPosition();
end
function NarciAdjustableSpeechBalloonMixin:SetBoundaryHeight(height)
local cornerRadius = self.CornerTopLeft:GetWidth();
local minHeight = max(2 * cornerRadius, SPEECH_BALLOON_MIN_SIZE + 2*self.padding);
if height < minHeight then
height = minHeight;
end
self.HeightNode:SetPoint("CENTER", self, "CENTER", 0, height/2);
self:SetHeight(height);
end
function NarciAdjustableSpeechBalloonMixin:SetPadding(distance)
distance = distance or 0;
if distance < 0 then
distance = 0;
end
self.padding = distance;
self.ShownText:ClearAllPoints();
self.ShownText:SetPoint("TOPLEFT", self, "TOPLEFT", distance, -distance - 2);
self.ShownText:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -distance, distance);
end
function NarciAdjustableSpeechBalloonMixin:SetTail(m, n, o, d, r)
--local m, n = -20, -120;
local r1 = r;
local r2 = r;
if n >= -12 then --minimum offsetY
n = -12;
end
local facingRight = m > o;
local a1, b1 = GetCircleCenter(m, n, o - d/2, 0, r1, facingRight);
local a2, b2 = GetCircleCenter(m, n, o + d/2, 0, r2, facingRight);
if a1 and a2 then
self.tailEndOffsetX = m;
self.tailEndOffsetY = n;
self.tailAttachOffset = o;
self.tailWidth = d;
self.tailArcRadius = r;
local thickness = self.thickness * 1.832;
r1 = r1 * 2;
r2 = r2 * 2;
self.Circle1:SetSize(r1, r1);
self.Circle2:SetSize(r2, r2);
self.Circle1:SetPoint("CENTER", self, "BOTTOM", a1, b1);
self.Circle2:SetPoint("CENTER", self, "BOTTOM", a2, b2);
if facingRight then
self.TailBorderCircle1:SetSize(r1 + thickness, r1 + thickness);
self.TailBorderCircle2:SetSize(r2 - thickness, r2 - thickness);
if not self.facingRight then
self.facingRight = true;
self.Circle2:SetTexture(TEXTURE_PATH_PREFIX.. "CircleOuter-ShowLeft", "CLAMPTOWHITE", "CLAMPTOWHITE");
self.Circle1:SetTexture(TEXTURE_PATH_PREFIX.. "Circle512", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
self.TailBorderCircle2:SetTexture(TEXTURE_PATH_PREFIX.. "CircleOuter-ShowLeft", "CLAMPTOWHITE", "CLAMPTOWHITE");
self.TailBorderCircle1:SetTexture(TEXTURE_PATH_PREFIX.. "Circle512", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
end
else
self.TailBorderCircle1:SetSize(r1 - thickness, r1 - thickness);
self.TailBorderCircle2:SetSize(r2 + thickness, r2 + thickness);
if self.facingRight then
self.facingRight = false;
self.Circle1:SetTexture(TEXTURE_PATH_PREFIX.. "CircleOuter-ShowRight", "CLAMPTOWHITE", "CLAMPTOWHITE");
self.Circle2:SetTexture(TEXTURE_PATH_PREFIX.. "Circle512", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
self.TailBorderCircle1:SetTexture(TEXTURE_PATH_PREFIX.. "CircleOuter-ShowRight", "CLAMPTOWHITE", "CLAMPTOWHITE");
self.TailBorderCircle2:SetTexture(TEXTURE_PATH_PREFIX.. "Circle512", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
end
end
self.TailBorderCircle1:SetPoint("CENTER", self, "BOTTOM", a1, b1);
self.TailBorderCircle2:SetPoint("CENTER", self, "BOTTOM", a2, b2);
--Update button position
self.TailEndNode:SetPoint("CENTER", self, "BOTTOM", m, n);
self.TailAttachNode:SetPoint("CENTER", self, "BOTTOM", o, 0);
local TailArcNode = self.TailArcNode;
local markX, degrees;
local markY = n + 24;
if facingRight then
TailArcNode:SetPoint("CENTER", self.TailEndNode, "CENTER", r/4, 6);
markX = -sqrt(r^2 - (markY - b2)^2) + a2;
degrees = GetDegrees(markX, markY, a2, b2);
else
TailArcNode:SetPoint("CENTER", self.TailEndNode, "CENTER", -r/4, 6);
markX = sqrt(r^2 - (markY - b1)^2) + a1;
degrees = GetDegrees(markX, markY, a1, b1);
end
TailArcNode.Mark:SetPoint("CENTER", self, "BOTTOM", markX, markY);
TailArcNode.Mark:SetRotation(pi90 - degrees);
local TailWidthNode = self.TailWidthNode;
TailWidthNode.Mark1:ClearAllPoints();
TailWidthNode.Mark1:SetPoint("RIGHT", self.TailAttachNode, "CENTER", -d/2 + 2, 8);
TailWidthNode.Mark2:ClearAllPoints();
TailWidthNode.Mark2:SetPoint("LEFT", self.TailAttachNode, "CENTER", d/2 - 2, 8);
TailWidthNode:SetPoint("CENTER", self, "BOTTOM", o + d, 0);
end
end
function NarciAdjustableSpeechBalloonMixin:SetTailEnd(m, n)
self:SetTail(m, n, self.tailAttachOffset, self.tailWidth, self.tailArcRadius);
end
function NarciAdjustableSpeechBalloonMixin:SetTailWidth(d)
self:SetTail(self.tailEndOffsetX, self.tailEndOffsetY, self.tailAttachOffset, d, self.tailArcRadius);
end
function NarciAdjustableSpeechBalloonMixin:SetTailAttachOffset(o)
if o > -4 and o < 4 then
o = 0;
end
self:SetTail(self.tailEndOffsetX, self.tailEndOffsetY, o, self.tailWidth, self.tailArcRadius);
end
function NarciAdjustableSpeechBalloonMixin:SetTailArcRadius(r)
self:SetTail(self.tailEndOffsetX, self.tailEndOffsetY, self.tailAttachOffset, self.tailWidth, r);
end
function NarciAdjustableSpeechBalloonMixin:UpdateTail(r)
self:SetTail(self.tailEndOffsetX, self.tailEndOffsetY, self.tailAttachOffset, self.tailWidth, self.tailArcRadius);
end
function NarciAdjustableSpeechBalloonMixin:UpdateTailPosition()
local maxOffset = abs(self:GetWidth()/2 - self.cornerRadius - (self.tailWidth or 0) );
local tailAttachOffset = self.tailAttachOffset or 0;
local direction = false;
if tailAttachOffset < -maxOffset then
direction = -1;
tailAttachOffset = maxOffset;
elseif tailAttachOffset > maxOffset then
direction = 1;
tailAttachOffset = maxOffset;
end
if direction then
self:SetTailAttachOffset(direction * tailAttachOffset);
end
end
function NarciAdjustableSpeechBalloonMixin:SetBackgroundColor(r, g, b)
self.BackgroundLeft:SetColorTexture(r, g, b);
self.BackgroundRight:SetColorTexture(r, g, b);
self.TailBackground:SetColorTexture(r, g, b);
self.backgroundColor = {r, g, b};
end
function NarciAdjustableSpeechBalloonMixin:UpdateText()
local font, height = GetFontData(self.isBold, self.isItalic);
height = round(height);
self.fontPath = font;
if not self.fontHeight then
self.fontHeight = height;
else
height = self.fontHeight;
end
LetteringSystem:UpdateFont(self);
if self.textWrapping then
self.ShownText:SetText("");
else
if self.isAllCaps then
self.ShownText:SetText(upper(self.rawText));
else
self.ShownText:SetText(self.rawText);
end
end
self.ShownText:SetFont(font, height, "");
end
function NarciAdjustableSpeechBalloonMixin:SetBorderColor(r, g, b, a)
self.BorderLeft:SetColorTexture(r, g, b);
self.BorderRight:SetColorTexture(r, g, b);
self.TailBorder:SetColorTexture(r, g, b);
self.borderColor = {r, g, b};
end
function NarciAdjustableSpeechBalloonMixin:SetFontColor(r, g, b)
self.ShownText:SetTextColor(r, g, b);
LetteringSystem:UpdateFont(self);
end
function NarciAdjustableSpeechBalloonMixin:SetTextWrapping(state)
self.textWrapping = state;
self:UpdateText();
end
function NarciAdjustableSpeechBalloonMixin:OnDoubleClick()
--EditButton:Click();
end
-----------------------------------------------------------------
NarciSpeechBalloonControlNodeMixin = {};
function NarciSpeechBalloonControlNodeMixin:OnLoad()
self.Texture:SetTexture(TEXTURE_PATH_PREFIX.. "ControlNode", nil, nil, "TRILINEAR");
end
function NarciSpeechBalloonControlNodeMixin:OnClick()
self.Bling:Show();
self.Bling.animScale:Play();
end
function NarciSpeechBalloonControlNodeMixin:OnMouseDown()
self:LockHighlight();
local cx, cy = GetCursorPosition();
local x0, y0 = self:GetCenter();
if self.transformFunc then
self.transformFunc(cx - x0, cy- y0);
end
EditButton:FadeOut(0.12);
end
function NarciSpeechBalloonControlNodeMixin:OnMouseUp()
self:UnlockHighlight();
if self.isBoundaryButton then
Node_OnMouseUp();
LetteringSystem:SetText(self:GetParent());
else
Node_OnMouseUp();
end
if not self:IsMouseOver() then
if self.Mark then
self.Mark:Hide();
end
if self.Mark1 then
self.Mark1:Hide();
self.Mark2:Hide();
end
end
if self:GetParent():IsMouseOver() then
EditButton:FadeIn(0.5);
end
end
function NarciSpeechBalloonControlNodeMixin:OnEnter()
if (not IsMouseButtonDown()) then
if self.Mark then
self.Mark:Show();
end
if self.Mark1 then
self.Mark1:Show();
self.Mark2:Show();
end
end
end
function NarciSpeechBalloonControlNodeMixin:OnLeave()
if (not IsMouseButtonDown()) then
if self.Mark then
self.Mark:Hide();
end
if self.Mark1 then
self.Mark1:Hide();
self.Mark2:Hide();
end
end
EditButton:SmartFadeOut();
end
----------------------------------------------------------
NarciSpeechBalloonToolbarButtonMixin = {};
function NarciSpeechBalloonToolbarButtonMixin:OnEnter()
Tooltip:ShowDelayedTooltip(self);
if self.expandable then
if not self.isOn then
self.Arrow:Show();
self.Arrow.flyInUp:Play();
end
end
end
function NarciSpeechBalloonToolbarButtonMixin:OnLeave()
Tooltip:HideTooltip();
if self.expandable then
self.Arrow:Hide();
end
end
function NarciSpeechBalloonToolbarButtonMixin:SetBackgroundColor(r, g, b)
if self.Background then
self.Background:SetVertexColor(r, g, b);
elseif self.Middle then
self.Left:SetVertexColor(r, g, b);
self.Middle:SetVertexColor(r, g, b);
self.Right:SetVertexColor(r, g, b);
end
end
function NarciSpeechBalloonToolbarButtonMixin:Select()
self.Background:SetVertexColor(0.72, 0.72, 0.72);
self.Icon:SetVertexColor(0, 0, 0);
end
function NarciSpeechBalloonToolbarButtonMixin:Deselect()
self.Background:SetVertexColor(0.35, 0.35, 0.35);
self.Icon:SetVertexColor(1, 1, 1);
end
function NarciSpeechBalloonToolbarButtonMixin:UpdateSelection(isSelected)
if isSelected then
self:Select();
else
self:Deselect();
end
end
function NarciSpeechBalloonToolbarButtonMixin:OnEvent(event)
if self.options then
local isInbound = self:IsMouseOver();
for i = 1, #self.options do
isInbound = isInbound or self.options[i]:IsMouseOver();
if isInbound then
return
end
end
if not isInbound then
self:UnregisterEvent(event);
if self.closeFunc then
self.closeFunc(self);
end
end
end
end
NarciSpeechBalloonToolbarLongButtonMixin = CreateFromMixins(NarciSpeechBalloonToolbarButtonMixin)
function NarciSpeechBalloonToolbarLongButtonMixin:OnEnter()
Tooltip:ShowDelayedTooltip(self);
if self.expandable then
if not self.isOn then
self.Arrow:Show();
self.Arrow.flyInUp:Play();
end
end
self:SetBackgroundColor(0.92, 0.92, 0.92);
end
function NarciSpeechBalloonToolbarLongButtonMixin:OnLeave()
Tooltip:HideTooltip();
if self.expandable then
self.Arrow:Hide();
end
if not self.isActive then
self:SetBackgroundColor(0.72, 0.72, 0.72);
end
end
----------------------------------------------------------
local function ToggleBorderThicknessOptions(self, visible)
if visible ~= nil then
self.isOn = visible;
else
self.isOn = not self.isOn;
end
if not self.options then
self.options = {};
local widget;
for i = 1, #STROKE_SIZE do
widget = CreateFrame("Button", nil, self, "NarciSpeechBalloonLineThicknessButtonTemplate");
widget:SetFrameLevel(15);
tinsert(self.options, widget);
if i == 1 then
widget:SetPoint("TOP", self, "BOTTOM", 0, -8);
else
widget:SetPoint("TOP", self.options[i - 1], "BOTTOM", 0, 1.5);
end
widget:SetValue(STROKE_SIZE[i]);
end
end
if self.isOn then
self.isActive = true;
local widget;
local selectedValue = self.value;
for i = 1, #self.options do
widget = self.options[i];
widget:Show();
if widget.value == selectedValue then
widget.isActive = true;
widget:OnEnter();
else
widget.isActive = false;
widget:OnLeave();
end
end
self:RegisterEvent("GLOBAL_MOUSE_DOWN");
else
self.isActive = false;
for i = 1, #self.options do
self.options[i]:Hide();
end
self:UnregisterEvent("GLOBAL_MOUSE_DOWN");
self.isActive = false;
if not self:IsMouseOver() then
self:OnLeave();
end
end
end
local COLOR_PRESETS = {
{255, 255, 255},
{204, 204, 204},
{166, 166, 166},
{128, 128, 128},
{85, 85, 85},
{51, 51, 51},
{13, 13, 13},
{237, 28, 36},
{122, 0, 38},
{244, 154, 193},
{255, 232, 165},
{198, 156, 109},
{255, 210, 0},
{124, 197, 118},
{0, 191, 243},
{90, 163, 255},
{172, 115, 238},
};
for i = 2, #COLOR_PRESETS do
local r, g, b = unpack(COLOR_PRESETS[i]);
COLOR_PRESETS[i] = {r/255, g/255, b/255};
end
local function ToggleColorDropDown(colorSwitch, state, modeIndex)
if not ColorDropDown then
ColorDropDown = CreateFrame("Frame", nil, Toolbar);
ColorDropDown:Hide();
ColorDropDown:SetSize(16, 16);
ColorDropDown.buttons = {};
--[[Area Text]]--
--[[
local backdrop = ColorDropDown:CreateTexture(nil, "BACKGROUND");
backdrop:SetPoint("TOPLEFT", ColorDropDown, "TOPLEFT", 0, 0);
backdrop:SetPoint("BOTTOMRIGHT", ColorDropDown, "BOTTOMRIGHT", 0, 0);
backdrop:SetColorTexture(1, 0, 0, 0.5);
--]]
function ColorDropDown:SetMode(mode)
if mode ~= self.mode then
ColorDropDown.mode = mode;
self:UpdateButtons(mode);
end
end
function ColorDropDown:GetMode()
return self.mode
end
function ColorDropDown:UpdateButtons(mode)
local colors;
if mode == 1 then
colors = THEME_PRESETS;
else
colors = COLOR_PRESETS;
end
if colors == self.colors then
return
else
self.colors = colors;
end
if colors then
local col = 1;
local row = 1;
for i = 1, #colors do
if not self.buttons[i] then
self.buttons[i] = CreateFrame("Button", nil, self, "NarciSpeechBalloonColorButtonTemplate");
self.buttons[i]:SetPoint("TOPLEFT", self, "TOPLEFT", (col - 1) * 20, (1 - row) * 20);
self.buttons[i].id = i;
end
col = col + 1;
if col > 7 then
col = 1;
row = row + 1;
end
self.buttons[i]:SetColor( colors[i] );
self.buttons[i]:Show();
end
if row == 1 then
self:SetSize(20 * col, 20);
else
self:SetSize(20 * 7, 20 * row);
end
for i = #colors + 1, #self.buttons do
self.buttons[i]:Hide();
end
end
end
function ColorDropDown:IsFocused()
return self:IsShown() and ( self:IsMouseOver(4, -4, -4, 4) or (self.parentButton and self.parentButton:IsMouseOver() ));
end
ColorDropDown:SetScript("OnEvent", function(self, event)
if not self:IsFocused() then
self:Hide();
end
end);
ColorDropDown:SetScript("OnShow", function(self)
self:RegisterEvent("GLOBAL_MOUSE_DOWN");
end);
ColorDropDown:SetScript("OnHide", function(self)
self:UnregisterEvent("GLOBAL_MOUSE_DOWN");
end);
end
ColorDropDown:ClearAllPoints();
if state == nil then
state = not ColorDropDown:IsShown()
end
if state then
ColorDropDown.parentButton = colorSwitch;
ColorDropDown:SetPoint("TOPLEFT", colorSwitch, "BOTTOMLEFT", 0, -8);
ColorDropDown:SetMode(modeIndex);
ColorDropDown:Show();
else
ColorDropDown:Hide();
end
end
local function ToggleThemeOptions(self, visible)
ToggleColorDropDown(self, visible, 1);
end
local function ToggleBackgroundColorOptions(self, visible)
ToggleColorDropDown(self, visible, 2);
end
local function ToggleBorderColorOptions(self, visible)
ToggleColorDropDown(self, visible, 3);
end
local function ToggleFontColorOptions(self, visible)
ToggleColorDropDown(self, visible, 4);
end
local FONT_SIZES = {
12,
14,
16,
18,
24,
30,
36,
48,
};
local ToggleFontSizeOptions;
local function FontSizeOption_OnClick(self)
local switch = self:GetParent():GetParent();
local value = self.value;
switch.value = value;
switch.Label:SetText(value.." x");
ToggleFontSizeOptions(switch, false);
local parentObject = Toolbar.parentObject;
if parentObject then
parentObject.fontHeight = value;
parentObject:UpdateText();
end
end
function ToggleFontSizeOptions(self, visible)
if visible ~= nil then
self.isOn = visible;
else
self.isOn = not self.isOn;
end
if not FontSizeDropDown then
self.options = {};
FontSizeDropDown = CreateFrame("Frame", nil, self);
FontSizeDropDown:SetPoint("TOP", self, "BOTTOM", 0, -8);
local numButtons = #FONT_SIZES;
FontSizeDropDown:SetSize(48, 16 * numButtons);
local widget;
local value;
for i = 1, numButtons do
widget = CreateFrame("Button", nil, FontSizeDropDown, "NarciSpeechBalloonLongButtonTemplate");
widget:SetFrameLevel(15);
self.options[i] = widget;
if i == 1 then
widget:SetPoint("TOP", FontSizeDropDown, "TOP", 0, 0);
else
widget:SetPoint("TOP", self.options[i - 1], "BOTTOM", 0, 1.5);
end
widget:SetScript("OnClick", FontSizeOption_OnClick);
value = FONT_SIZES[i];
widget.Label:SetText(value.." x");
widget.value = value;
end
function FontSizeDropDown:IsFocused()
return FontSizeDropDown:IsShown() and FontSizeDropDown:IsMouseOver()
end
end
if self.isOn then
self.isActive = true;
local widget;
local selectedValue = self.value;
for i = 1, #self.options do
widget = self.options[i];
if widget.value == selectedValue then
widget.isActive = true;
widget:SetBackgroundColor(0.92, 0.92, 0.92);
else
widget.isActive = false;
widget:SetBackgroundColor(0.72, 0.72, 0.72);
end
end
FontSizeDropDown:Show();
self:RegisterEvent("GLOBAL_MOUSE_DOWN");
else
self.isActive = false;
FontSizeDropDown:Hide();
self:UnregisterEvent("GLOBAL_MOUSE_DOWN");
if not self:IsMouseOver() then
self:SetBackgroundColor(0.72, 0.72, 0.72);
end
end
end
---------------------------------------------------------------------------------------------------------
NarciSpeechBalloonLineThicknessButtonMixin = CreateFromMixins(NarciSpeechBalloonToolbarButtonMixin);
function NarciSpeechBalloonLineThicknessButtonMixin:SetValue(pixelSize)
local i;
pixelSize = round(pixelSize);
if pixelSize < 5 then
i = pixelSize - 1;
else
i = 4;
end
self.value = pixelSize;
self.Icon:SetTexCoord( (i - 1)*0.25, i*0.25, 0, 1);
self.Label:SetText(pixelSize.." x");
end
function NarciSpeechBalloonLineThicknessButtonMixin:OnClick()
local parentButton = self:GetParent();
local parentObject = parentButton:GetParent():GetParent().parentObject;
local thickness = self.value;
ToggleBorderThicknessOptions(parentButton, false);
if parentObject then
parentObject:SetBorderThickness(thickness);
parentButton:SetValue(self.value);
parentButton:OnLeave();
end
end
function NarciSpeechBalloonLineThicknessButtonMixin:OnEnter()
self:SetBackgroundColor(0.92, 0.92, 0.92);
Tooltip:ShowDelayedTooltip(self);
if self.expandable then
if not self.isOn then
self.Arrow:Show();
self.Arrow.flyInUp:Play();
end
end
end
function NarciSpeechBalloonLineThicknessButtonMixin:OnLeave()
Tooltip:HideTooltip();
if not self.isActive then
self:SetBackgroundColor(0.72, 0.72, 0.72);
end
if self.expandable then
self.Arrow:Hide()
end
end
NarciSpeechBalloonToolbarColorButtonMixin = CreateFromMixins(NarciSpeechBalloonToolbarButtonMixin);
function NarciSpeechBalloonToolbarColorButtonMixin:OnLoad()
self.Icon:SetTexture(TEXTURE_PATH_PREFIX.."TextButton-NoColor");
self.Icon:Hide();
end
function NarciSpeechBalloonToolbarColorButtonMixin:SetColor(colors)
if colors then
self.Icon:Hide();
self:SetBackgroundColor(unpack(colors));
else
self.Icon:Show();
end
self.value = colors;
end
function NarciSpeechBalloonToolbarColorButtonMixin:OnClick()
local parentButton = ColorDropDown.parentButton;
local parentObject = Toolbar.parentObject;
local mode = ColorDropDown:GetMode();
if mode == 2 then
if parentObject then
local colors = self.value;
parentObject:SetBackgroundColor(unpack(colors));
parentButton:SetBackgroundColor(unpack(colors));
end
elseif mode == 3 then
if parentObject then
local colors = self.value;
parentObject:SetBorderColor(unpack(colors));
parentButton:SetBackgroundColor(unpack(colors));
end
elseif mode == 4 then
if parentObject then
local colors = self.value;
parentObject:SetFontColor(unpack(colors));
parentButton:SetBackgroundColor(unpack(colors));
end
elseif mode == 1 then
if parentObject then
parentObject:SelectTheme(self.id);
parentButton:SetBackgroundColor(unpack(self.value));
local textColorButton = Toolbar.Bar2.buttons[4];
if self.id == 1 then
textColorButton:SetBackgroundColor(0, 0, 0);
else
textColorButton:SetBackgroundColor(1, 0.91, 0.647);
end
end
end
ColorDropDown:Hide();
end
NarciSpeechBalloonTextWrappingButtonMixin = {};
function NarciSpeechBalloonTextWrappingButtonMixin:OnEnter()
Tooltip:ShowDelayedTooltip(self);
end
function NarciSpeechBalloonTextWrappingButtonMixin:OnLeave()
Tooltip:HideTooltip();
end
function NarciSpeechBalloonTextWrappingButtonMixin:Select()
self.isOn = true;
self.Background:SetTexCoord(0.5, 1, 0, 0.5);
end
function NarciSpeechBalloonTextWrappingButtonMixin:Deselect()
self.isOn = false;
self.Background:SetTexCoord(0, 0.5, 0, 0.5);
end
function NarciSpeechBalloonTextWrappingButtonMixin:UpdateSelection(isSelected)
if isSelected then
self:Select();
else
self:Deselect();
end
end
function NarciSpeechBalloonTextWrappingButtonMixin:SetBackgroundColor()
end
function NarciSpeechBalloonTextWrappingButtonMixin:OnClick()
self.isOn = not self.isOn;
local state = self.isOn;
if state then
self:Select();
else
self:Deselect();
end
local parentObject = self:GetParent():GetParent().parentObject;
if parentObject then
parentObject:SetTextWrapping(state);
end
end
NarciTextOverlayEditButtonMixin = {};
function NarciTextOverlayEditButtonMixin:OnLoad()
EditButton = self;
local filter = "TRILINEAR";
self.Texture:SetTexture(TEXTURE_PATH_PREFIX.. "EditButton", nil, nil, filter);
self.Highlight:SetTexture(TEXTURE_PATH_PREFIX.. "EditButton", nil, nil, filter);
self.Icon:SetTexture(TEXTURE_PATH_PREFIX.. "EditButton");
self:SetAlpha(0);
self:SetColor(1);
NarciAPI_CreateFadingFrame(self);
end
function NarciTextOverlayEditButtonMixin:OnEnter()
FadeFrame(self.Highlight, 0.15, 0.66);
if not IsMouseButtonDown() then
self:FadeIn(0.25);
end
end
function NarciTextOverlayEditButtonMixin:SmartFadeOut()
if not self:IsMouseOver() then
if (self.parentObject) and (not self.parentObject:IsMouseOver()) then
self:FadeOut(0.25);
end
end
end
function NarciTextOverlayEditButtonMixin:OnLeave()
FadeFrame(self.Highlight, 0.2, 0);
self:SmartFadeOut();
end
function NarciTextOverlayEditButtonMixin:OnHide()
self.parentObject = nil;
self:SetAlpha(0);
end
function NarciTextOverlayEditButtonMixin:OnMouseDown()
self.animPushed:Stop();
self.animPushed.hold:SetDuration(20);
self.animPushed:Play();
end
function NarciTextOverlayEditButtonMixin:OnMouseUp()
self.animPushed.hold:SetDuration(0);
end
function NarciTextOverlayEditButtonMixin:SetColor(index)
if index == 2 then
self.Texture:SetVertexColor(0.37, 0.74, 0.42);
else
--self.Texture:SetVertexColor(1, 0.91, 0.65);
self.Texture:SetVertexColor(0.25, 0.78, 0.92);
end
end
function NarciTextOverlayEditButtonMixin:SetParentObject(object)
if object == self.parentObject then return end;
self.parentObject = object;
local r = object.cornerRadius or 0;
local offset = (0.4142 * r + 24)/1.4142;
self:SetPoint("CENTER", object, "BOTTOMRIGHT", -offset, offset);
end
function NarciTextOverlayEditButtonMixin:OnClick()
self.isOn = not self.isOn;
local Icon = self.Icon;
Icon:StopAnimating();
if self.isOn then
self:SetColor(2);
Icon:SetTexCoord(0.5, 1, 0.5, 1);
Icon.rotateClockwise:Play();
if self.parentObject then
PrimaryEditBox:SetParentObject(self.parentObject);
end
else
self:SetColor(1);
Icon:SetTexCoord(0, 0.5, 0.5, 1);
Icon.rotateCounterClockwise:Play();
if self.parentObject then
PrimaryEditBox:SetEnabled(false);
end
end
end
function NarciTextOverlayEditButtonMixin:OnDoubleClick()
end
function NarciTextOverlayEditButtonMixin:ResetState()
self:StopAnimating();
self.Icon:SetTexCoord(0, 0.5, 0.5, 1);
self:SetColor(1);
self.isOn = nil;
end
---------------------------------------------------------------------------------------
NarciSpeechBalloonEditBoxMixin = {};
function NarciSpeechBalloonEditBoxMixin:OnLoad()
PrimaryEditBox = self;
self:Disable();
self:SetEnabled(false);
end
function NarciSpeechBalloonEditBoxMixin:SetParentObject(object)
self.parentObject = object;
self:ClearAllPoints();
local padding = object.padding;
self:SetParent(object);
self:SetPoint("TOPLEFT", object, "TOPLEFT", padding, -padding);
self:SetPoint("BOTTOMRIGHT", object, "BOTTOMRIGHT", -padding, padding);
self:SetText(object.rawText);
self:SetEnabled(true);
local font, height = object.ShownText:GetFont();
self:SetFont(font, height, "");
self:SetTextColor(object.ShownText:GetTextColor());
object.ShownText:Hide();
LetteringSystem:ShowText(object, false);
object:SetNodesTransparency(0.5);
end
function NarciSpeechBalloonEditBoxMixin:OnDisable()
self.Highlight:Hide();
self:EnableMouse(false);
self:HighlightText(0, 0);
self:Hide();
if self.parentObject then
self.parentObject:SetNodesTransparency(1);
self:ConfirmChanges();
end
end
function NarciSpeechBalloonEditBoxMixin:ConfirmChanges()
if self.parentObject then
local text = self:GetText() or "";
self.parentObject.rawText = text;
self.parentObject:UpdateText();
self.parentObject.ShownText:Show();
LetteringSystem:ShowText(self.parentObject, true);
self.parentObject = nil;
end
end
function NarciSpeechBalloonEditBoxMixin:OnEnable()
self:Show();
self.Highlight:Show();
self:EnableMouse(true);
self:SetFocus();
self:SetCursorPosition(999);
end
function NarciSpeechBalloonEditBoxMixin:OnEditFocusLost()
end
function NarciSpeechBalloonEditBoxMixin:OnEscapePressed()
if self:IsEnabled() then
if EditButton.parentObject == self:GetParent() then
EditButton:Click();
end
end
end
---------------------------------------------------------------------------------------
NarciTextOverlayTooltipMixin = {};
function NarciTextOverlayTooltipMixin:OnLoad()
self.backgrounds = { self.Left, self.Right, self.Middle};
self:SetBackgroundColor();
local timer = NarciAPI_CreateAnimationFrame(0.6);
self.timer = timer;
timer:SetScript("OnUpdate", function(frame, elapsed)
frame.total = frame.total + elapsed;
if frame.total >= frame.duration then
frame:Hide();
if self.widget and self.widget == GetMouseFocus() then
self:ShowTooltip(self.widget);
end
end
end)
timer:SetScript("OnShow", function(frame)
frame:RegisterEvent("GLOBAL_MOUSE_DOWN");
end);
timer:SetScript("OnEvent", function(frame)
frame:UnregisterEvent("GLOBAL_MOUSE_DOWN");
frame.total = 0;
frame:Hide();
end);
self:SetAlpha(0);
NarciAPI_CreateFadingFrame(self);
end
function NarciTextOverlayTooltipMixin:OnShow()
self:RegisterEvent("GLOBAL_MOUSE_DOWN");
end
function NarciTextOverlayTooltipMixin:OnHide()
self:UnregisterEvent("GLOBAL_MOUSE_DOWN");
self:Hide();
self:SetAlpha(0);
end
function NarciTextOverlayTooltipMixin:OnEvent(event)
self.animFade:Hide();
self.timer:Hide();
self:Hide();
end
function NarciTextOverlayTooltipMixin:SetBackgroundColor(r, g, b)
if not r then
r, g, b = 175, 233, 254;
end
if r > 1 then
r, g, b = r/255, g/255, b/255;
end
for i = 1, #self.backgrounds do
self.backgrounds[i]:SetVertexColor(r, g, b);
end
end
function NarciTextOverlayTooltipMixin:SetText(str)
self.Description:SetText(str);
local textWidth = self.Description:GetWidth();
self:SetWidth(round(textWidth) + 8);
end
function NarciTextOverlayTooltipMixin:ShowTooltip(widget)
if not widget then return end;
if widget.tooltip then
if widget.tooltipColor then
self:SetBackgroundColor(unpack(widget.tooltipColor))
else
self:SetBackgroundColor();
end
self:SetText(widget.tooltip);
self:ClearAllPoints();
--self:SetPoint("BOTTOMLEFT", widget, "TOPLEFT", 0, 8);
self:SetPoint("TOPLEFT", widget, "BOTTOMLEFT", 0, -8);
self:Show();
self:FadeIn(0.15);
end
end
function NarciTextOverlayTooltipMixin:HideTooltip()
self.widget = nil;
self:Hide();
self.timer:Hide();
end
function NarciTextOverlayTooltipMixin:ShowDelayedTooltip(widget)
self.timer:Hide();
if widget.tooltip then
self.widget = widget;
self.timer:Show();
end
end
---------------------------------------------------------------------------------------
--Toolbar
NarciSpeechBalloonToolbarMixin = {};
function NarciSpeechBalloonToolbarMixin:OnLoad()
local timer = NarciAPI_CreateAnimationFrame(1);
timer:SetScript("OnUpdate", function(frame, elapsed)
frame.total = frame.total + elapsed;
if frame.total >= frame.duration then
frame:Hide();
if not (self:IsMouseOver() or (self.parentObject and self.parentObject:IsMouseOver()) ) then
self:FadeOut(0.5);
end
end
end);
end
local function ToolbarFade_OnUpdate(self, elapsed)
self.t = self.t + elapsed;
end
local function Toolbar_OnEvent(self, event, ...)
if not ( self:IsMouseOver(10, -10, -10, 10) or IsWidgetFocused(self.parentObject) or IsWidgetFocused(ColorDropDown) or IsWidgetFocused(FontSizeDropDown) ) then
HideEditor();
end
end
function NarciSpeechBalloonToolbarMixin:FadeIn(duration)
self:Show();
end
function NarciSpeechBalloonToolbarMixin:FadeOut(duration)
self:Hide();
end
function NarciSpeechBalloonToolbarMixin:SetParentObject(object)
self.parentObject = object;
self:ClearAllPoints();
self:SetPoint("BOTTOM", object, "TOP", 0, 8);
self:FadeIn(0.25);
--Load Settings
local buttons = self.buttons;
if object.balloonTpye == 1 then
self:SetWidth(self.barWidth0);
self.Bar0:Show();
self.Bar1:Hide();
self.Bar2:SetPoint("LEFT", self.Bar0, "RIGHT", 4, 0);
self.Bar2:SetWidth(140);
buttons[0]:SetBackgroundColor( unpack(object.backgroundColor) );
buttons[1]:SetBackgroundColor( unpack(object.backgroundColor) );
buttons[9]:Hide();
else
self:SetWidth(self.barWidth1);
self.Bar0:Hide();
self.Bar1:Show();
self.Bar2:SetPoint("LEFT", self.Bar1, "RIGHT", 4, 0);
self.Bar2:SetWidth(164);
buttons[1]:SetBackgroundColor( unpack(object.backgroundColor) );
buttons[2]:SetBackgroundColor( unpack(object.borderColor) );
buttons[3]:SetValue( object.borderPixelSize );
buttons[9]:UpdateSelection( object.textWrapping );
buttons[9]:Show();
end
buttons[4]:UpdateSelection( object.isBold );
buttons[5]:UpdateSelection( object.isItalic );
buttons[6]:UpdateSelection( object.isAllCaps );
buttons[7]:SetBackgroundColor( object.ShownText:GetTextColor() );
local value = object.fontHeight;
buttons[8].Label:SetText(value.." x");
buttons[8].value = value;
end
function NarciSpeechBalloonToolbarMixin:OnShow()
self:RegisterEvent("GLOBAL_MOUSE_DOWN");
self:SetScript("OnEvent", Toolbar_OnEvent);
end
function NarciSpeechBalloonToolbarMixin:OnHide()
self:UnregisterEvent("GLOBAL_MOUSE_DOWN");
self:SetScript("OnEvent", nil);
end
local BACKGROUND_SETTINGS = {
[1] = {
name = "Background Color",
expandable = true,
--template = "NarciSpeechBalloonColorButtonTemplate",
func = function(self) ToggleBackgroundColorOptions(self) end,
defaultValue = {1, 1, 1};
},
[2] = {
name = "Stroke Color",
icon = "SquareInner",
expandable = true,
--template = "NarciSpeechBalloonColorButtonTemplate",
func = function(self) ToggleBorderColorOptions(self) end,
defaultValue = {0.1, 0.1, 0.1};
},
[3] = {
name = "Stroke Width",
isLongButton = true,
expandable = true,
template = "NarciSpeechBalloonLineThicknessButtonTemplate",
func = function(self) ToggleBorderThicknessOptions(self) end,
closeFunc = function(self) ToggleBorderThicknessOptions(self, false) end,
defaultValue = 2;
},
};
local TEXT_SETTINGS = {
[1] = {
name = "Bold",
icon = "TextIcon-Bold",
func = function(self)
local parentObject = self:GetParent():GetParent().parentObject;
self.isOn = not self.isOn;
local state = self.isOn;
if state then
self:Select();
else
self:Deselect();
end
if parentObject then
parentObject:SetBold(state);
end
end,
defaultValue = false;
},
[2] = {
name = "Italic",
icon = "TextIcon-Italic",
func = function(self)
local parentObject = self:GetParent():GetParent().parentObject;
self.isOn = not self.isOn;
local state = self.isOn;
if state then
self:Select();
else
self:Deselect();
end
if parentObject then
parentObject:SetItalic(state);
end
end,
defaultValue = false;
},
[3] = {
name = "All Caps",
icon = "TextButton-Caps",
func = function(self)
local parentObject = self:GetParent():GetParent().parentObject;
self.isOn = not self.isOn;
local state = self.isOn;
if state then
self:Select();
else
self:Deselect();
end
if parentObject then
parentObject:SetAllCaps(state);
end
end,
defaultValue = false;
},
[4] = {
name = "Text Color",
gap = 8,
expandable = true,
func = function(self) ToggleFontColorOptions(self) end,
defaultValue = {0.1, 0.1, 0.1};
},
[5] = {
name = "Font Size",
isLongButton = true,
expandable = true,
func = function(self) ToggleFontSizeOptions(self) end,
closeFunc = function(self) ToggleFontSizeOptions(self, false) end,
defaultValue = 14;
},
[6] = {
name = "Wrap text around the corner",
template = "NarciSpeechBalloonTextWrappingButtonTemplate",
gap = 8,
defaultValue = false;
},
};
local REMOVE_SETTINGS = {
[1] = {
name = "Remove",
icon = "TextButton-Remove",
template = "NarciSpeechBalloonRemoveButtonTemplate",
tooltipColor = {215, 31, 38},
},
};
local SIMPLE_BALLOON_SETTINGS = {
[1] = {
name = "Theme",
expandable = true,
func = function(self) ToggleThemeOptions(self) end,
},
};
local function CreateSubBar(toolbarData)
--Toolbar
local parent = nil;
local Bar = CreateFrame("Frame", nil, parent, "NarciSpeechBalloonToolbarTemplate");
Bar:ClearAllPoints();
Bar:SetPoint("CENTER", UIParent, "CENTER", 0, 0);
--Button
local GAP = 4;
local numButtons = #toolbarData;
local buttonWidth = 0;
local button;
local buttons = {};
local template;
for i = 1, numButtons do
local data = toolbarData[i];
if data.template then
template = data.template;
else
if data.isLongButton then
template = "NarciSpeechBalloonLongButtonTemplate";
else
template = "NarciSpeechBalloonSquareButtonTemplate";
end
end
button = CreateFrame("Button", nil, Bar, template);
tinsert(buttons, button);
buttonWidth = buttonWidth + round(button:GetWidth());
local gap;
if i == 1 then
gap = 0;
button:SetPoint("LEFT", Bar, "LEFT", GAP, 0);
else
gap = data.gap or GAP;
button:SetPoint("LEFT", buttons[i - 1], "RIGHT", gap, 0);
if i == numButtons then
--gap = 0;
end
end
buttonWidth = buttonWidth + gap;
if data.icon then
button.Icon:SetTexture(TEXTURE_PATH_PREFIX.. data.icon);
end
if data.isLongButton then
button:OnLeave();
else
button:SetBackgroundColor(0.35, 0.35, 0.35);
end
if data.defaultValue ~= nil then
button.value = data.defaultValue;
end
if data.func then
button:SetScript("OnClick", data.func);
end
if data.closeFunc then
button.closeFunc = data.closeFunc;
end
if data.expandable then
button.expandable = true;
end
if data.tooltipColor then
button.tooltipColor = data.tooltipColor;
end
button.tooltip = data.name;
end
Bar.buttons = buttons;
Bar:SetWidth(round(buttonWidth + 2 * GAP));
return Bar
end
local function CreateBallonToolbar()
local GAP = 4;
Toolbar:SetPoint("CENTER", UIParent, "CENTER", 0, 0);
local Bar0 = CreateSubBar(SIMPLE_BALLOON_SETTINGS);
local Bar1 = CreateSubBar(BACKGROUND_SETTINGS);
local Bar2 = CreateSubBar(TEXT_SETTINGS);
local Bar3 = CreateSubBar(REMOVE_SETTINGS);
Toolbar.Bar0 = Bar0;
Toolbar.Bar1 = Bar1;
Toolbar.Bar2 = Bar2;
Toolbar.Bar3 = Bar3;
local barWidth0 = round(Bar0:GetWidth() + Bar2:GetWidth() + Bar3:GetWidth() + 2*GAP - 24);
local barWidth1 = round(Bar1:GetWidth() + Bar2:GetWidth() + Bar3:GetWidth() + 2*GAP);
Toolbar.barWidth0 = barWidth0;
Toolbar.barWidth1 = barWidth1;
Toolbar:SetSize(barWidth1, 24);
Bar0:Hide();
Bar0:ClearAllPoints();
Bar1:ClearAllPoints();
Bar2:ClearAllPoints();
Bar3:ClearAllPoints();
Bar0:SetParent(Toolbar);
Bar1:SetParent(Toolbar);
Bar2:SetParent(Toolbar);
Bar3:SetParent(Toolbar);
Bar0:SetPoint("LEFT", Toolbar, "LEFT", 0, 0);
Bar1:SetPoint("LEFT", Toolbar, "LEFT", 0, 0);
Bar2:SetPoint("LEFT", Bar1, "RIGHT", GAP, 0);
Bar3:SetPoint("LEFT", Bar2, "RIGHT", GAP, 0);
Toolbar:Hide();
--
local RemoveButton = Bar3.buttons[1];
RemoveButton.HighlightTexture:SetVertexColor(0.8, 0, 0);
RemoveButton:SetScript("OnMouseDown", function(self)
self.Fill.Timer:Play();
end)
RemoveButton:SetScript("OnMouseUp", function(self)
self.Fill.Timer:Stop();
end)
RemoveButton.Fill.Timer:SetScript("OnFinished", function()
RemoveButton:Disable();
After(0.35, function()
RemoveButton:Enable();
end)
local parentObject = Toolbar.parentObject;
Toolbar:FadeOut(0.25);
EditButton:Hide();
if parentObject then
parentObject:Hide();
end
end)
local FontSizeButton = Bar2.buttons[5];
FontSizeButton.Label:SetText("14 x");
local buttons = { [0] = Bar0.buttons[1] };
for i = 1, #Bar1.buttons do
tinsert(buttons, Bar1.buttons[i]);
end
for i = 1, #Bar2.buttons do
tinsert(buttons, Bar2.buttons[i]);
end
Toolbar.buttons = buttons;
end
local function CreateModelDropDown()
--
ModelDropDownMenu = Container.ModelDropDownMenu;
local menu = ModelDropDownMenu;
function ModelDropDownMenu:SetParentObject(object)
menu:ClearAllPoints();
menu:SetPoint("TOPLEFT", object.Model, "TOPRIGHT", 2, 5);
menu:Show();
menu.parentObject = object;
end
--Buttons
local selfButton = CreateFrame("Button", nil, menu, "NarciTalkingHeadModelDropDownButtonTemplate");
local targetButton = CreateFrame("Button", nil, menu, "NarciTalkingHeadModelDropDownButtonTemplate");
local buttons = {selfButton, targetButton};
local actorButtons = {};
local MAX_ACTORS = 8;
local button;
selfButton.Label:SetText(L["Self"]);
selfButton:SetScript("OnClick", function()
ModelDropDownMenu.parentObject:SetUnit("player");
menu:Hide();
end)
targetButton:SetScript("OnClick", function()
ModelDropDownMenu.parentObject:SetUnit("target");
menu:Hide();
end)
for i = 1, MAX_ACTORS do
button = CreateFrame("Button", nil, menu, "NarciTalkingHeadModelDropDownButtonTemplate");
tinsert(actorButtons, button);
tinsert(buttons, button);
button.index = i;
button:SetScript("OnClick", function(self)
ModelDropDownMenu.parentObject:SetCreature(self.creatureID, self.Label:GetText());
menu:Hide();
end)
end
local numButtons = #buttons;
for i = 1, numButtons do
button = buttons[i];
if i < 3 then
button:SetPoint("TOP", menu, "TOP", 0, -16 * i);
else
button:SetPoint("TOP", menu, "TOP", 0, -16 * (i + 1));
end
end
menu:SetHeight( 16*(numButtons + 1) );
local function UpdateTargetName()
if UnitExists("target") then
local name = UnitName("target");
local _, className = UnitClass("target");
local r, g, b = GetClassColor(className);
targetButton.Label:SetTextColor(r, g, b);
SmartFontType(targetButton.Label, name);
targetButton:Enable();
else
targetButton.Label:SetTextColor(1, 0.3137, 0.3137); --Pastel Red
targetButton.Label:SetText(ERR_GENERIC_NO_TARGET);
targetButton:Disable();
end
end
local function UpdateNPCName()
local numModels = 0;
local model;
for i = 1, MAX_ACTORS do
model = _G["NarciNPCModelFrame"..i];
if model then
numModels = numModels + 1;
actorButtons[numModels].Label:SetText(model.creatureName or ("NPC #"..i));
actorButtons[numModels].creatureID = model.creatureID;
actorButtons[numModels]:Show();
end
end
for i = numModels + 1, MAX_ACTORS do
actorButtons[i]:Hide();
end
if numModels == 0 then
menu.None:Show();
numModels = 1;
else
menu.None:Hide();
end
menu:SetHeight(16*(numModels + 4));
end
menu.None:SetTextColor(1, 0.3137, 0.3137); --Pastel Red
menu:SetScript("OnShow", function(self)
self:RegisterEvent("GLOBAL_MOUSE_DOWN");
self:RegisterEvent("PLAYER_TARGET_CHANGED");
UpdateTargetName();
UpdateNPCName();
end)
menu:SetScript("OnHide", function(self)
self:UnregisterEvent("GLOBAL_MOUSE_DOWN");
self:UnregisterEvent("PLAYER_TARGET_CHANGED");
self:Hide();
end)
menu:SetScript("OnEvent", function(self, event)
if event == "GLOBAL_MOUSE_DOWN" then
if not self:IsMouseOver() then
self:Hide();
end
elseif event == "PLAYER_TARGET_CHANGED" then
UpdateTargetName();
end
end)
end
local function SetUpOverlayFrame()
local frame = Narci_TextOverlay;
frame:SetFrameLevel(60);
local VisibilityButton = frame.VisibilityButton;
VisibilityButton.Icon:SetTexture(TEXTURE_PATH_PREFIX.."Icons", nil, nil, "TRILINEAR");
VisibilityButton.Icon:SetTexCoord(0, 0.125, 0, 0.5);
VisibilityButton.tooltipDescription = L["Visibility"];
VisibilityButton:SetScript("OnClick", function(self)
local visible = not Container:IsShown();
Container:SetShown(visible);
if visible then
self.Icon:SetTexCoord(0.125, 0.25, 0, 0.5);
else
self.Icon:SetTexCoord(0, 0.125, 0, 0.5);
end
end)
VisibilityButton:SetScript("OnShow", function(self)
if Container:IsShown() then
self.Icon:SetTexCoord(0.125, 0.25, 0, 0.5);
else
self.Icon:SetTexCoord(0, 0.125, 0, 0.5);
end
end);
VisibilityButton:SetScript("OnHide", function(self)
if Container:IsShown() then
self:Click();
end
end)
VisibilityButton:SetScript("OnMouseDown", function(self)
self.Icon:SetSize(20, 20);
end)
VisibilityButton:SetScript("OnMouseUp", function(self)
self.Icon:SetSize(22, 22);
end)
VisibilityButton:SetScript("OnEnter", function(self)
self.Highlight:Show();
NarciTooltip:ShowButtonTooltip(self);
end)
VisibilityButton:SetScript("OnLeave", function(self)
self.Highlight:Hide();
NarciTooltip:HideTooltip();
end)
--
frame.Tooltip:SetGradientExtraWidth(10);
--Create Type Button
local button;
local numButtons = 5;
for i = numButtons, 1, -1 do
button = CreateFrame("Button", nil, frame, "NarciNewTextOverlayButtonTemplate");
button:SetPoint("RIGHT", frame, "RIGHT", -5 + (i - numButtons) * 24, 0);
button:SetID(i);
button:OnLoad();
end
end
local function Init()
Tooltip = Container.Tooltip;
Toolbar = Container.Toolbar;
SetUpOverlayFrame();
CreateBallonToolbar();
CreateModelDropDown();
end
NarciTextOverlayFrameMixin = {};
function NarciTextOverlayFrameMixin:OnLoad()
Narci_ModelSettings:AddSubFrame(self, "TextOverlayMenu");
self.Label:SetText(Narci.L["Text Overlay"]);
end
function NarciTextOverlayFrameMixin:OnShow()
if Init then
Init();
Init = nil;
end
self:SetScript("OnShow", nil);
end
function NarciTextOverlayFrameMixin:IsFocused()
return self:IsShown() and self:IsMouseOver();
end
----------------------------------------------------------------------------------
local positionFrame = CreateFrame("Frame");
positionFrame.screenMidPoint = WorldFrame:GetWidth()/2;
positionFrame:Hide();
positionFrame:SetScript("OnUpdate", function(self)
local cursorX, cursorY = GetCursorPosition();
local uiScale = self.uiScale or 1;
cursorX, cursorY = cursorX/uiScale, cursorY/uiScale;
local compensatedX = cursorX - self.offsetX;
local midPoint = self.screenMidPoint/uiScale;
if (compensatedX > midPoint - 40) and (compensatedX < midPoint + 40) then
compensatedX = midPoint;
end
if self.object then
self.object:SetPoint("CENTER", Container, "BOTTOMLEFT", compensatedX, cursorY - self.offsetY);
else
self:Hide();
end
end);
----------------------------------------------------------------------------------
local function InteractableLineBorder_OnDragStart(self)
if self:GetParent().OnDragStart then
self:GetParent():OnDragStart();
end
end
local function InteractableLineBorder_OnDragStop(self)
if self:GetParent().OnDragStop then
self:GetParent():OnDragStop();
end
end
local function InteractableLineBorder_OnClick(self)
Container.GenericEditBox:SetParentObject(self.parentObject);
self:Hide();
end
local function InitializeInteractableLineBorder(frame, parentObject)
frame:RegisterForDrag("LeftButton");
frame:SetScript("OnDragStart", InteractableLineBorder_OnDragStart);
frame:SetScript("OnDragStop", InteractableLineBorder_OnDragStop);
frame:SetScript("OnClick", InteractableLineBorder_OnClick);
frame.parentObject = parentObject;
parentObject.area = frame;
end
NarciCustomTalkingHeadMixin = {};
function NarciCustomTalkingHeadMixin:OnLoad()
self:RegisterForDrag("LeftButton");
self:SetScale(0.8);
self.Name:SetFixedColor(true)
self.Text:SetFontObjectsToTry(SystemFont_Shadow_Large, SystemFont_Shadow_Med2, SystemFont_Shadow_Med1);
self.Text:SetText("");
local area1 = CreateFrame("Button", nil, self, "NarciInteractableAreaIndicatorTemplate");
InitializeInteractableLineBorder(area1, self.Name);
area1:SetPoint("TOPLEFT", self.Name, "TOPLEFT", 0, 0);
area1:SetPoint("RIGHT", self, "TOPRIGHT", -42, 0);
area1:SetHeight(24);
local area2 = CreateFrame("Button", nil, self, "NarciInteractableAreaIndicatorTemplate");
InitializeInteractableLineBorder(area2, self.Text);
area2:SetPoint("TOPLEFT", self.Text, "TOPLEFT", 0, 0);
area2:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -42, 12);
local Model = self.Model;
Model:EnableMouse(true);
Model:SetKeepModelOnHide(true);
Model:SetScript("OnModelLoaded", function()
self:OnModelLoaded();
end)
local button = self.ModelOptionButton;
button:RegisterForDrag("LeftButton");
button:SetScript("OnClick", function()
ModelDropDownMenu:SetParentObject(self);
end)
button:SetScript("OnDragStart", function()
self:OnDragStart();
end)
button:SetScript("OnDragStop", function()
self:OnDragStop();
end)
button:SetScript("OnEnter", function(frame)
frame:SetAlpha(1);
end)
button:SetScript("OnLeave", function(frame)
frame:SetAlpha(0);
end)
end
function NarciCustomTalkingHeadMixin:OnShow()
if not self.hasUnitSet then
self.hasUnitSet = true;
After(0, function()
self:SetUnit("target");
end)
end
end
function NarciCustomTalkingHeadMixin:OnDragStart()
positionFrame:Hide();
self:ClearAllPoints();
local uiScale = self:GetScale();
positionFrame.object = self;
positionFrame.uiScale = uiScale;
local cursorX, cursorY = GetCursorPosition();
cursorX, cursorY = cursorX/uiScale, cursorY/uiScale;
local x0, y0 = self:GetCenter();
positionFrame.offsetX = cursorX - x0;
positionFrame.offsetY = cursorY - y0;
positionFrame:Show();
end
function NarciCustomTalkingHeadMixin:OnDragStop()
positionFrame:Hide();
end
function NarciCustomTalkingHeadMixin:SetUnit(unit)
local Model = self.Model;
unit = unit or "player";
if not UnitExists(unit) then
unit = "player";
end
Model:SetUnit(unit);
self.Name:SetText(UnitName(unit));
end
function NarciCustomTalkingHeadMixin:SetCreature(creatureID, creatureName)
if not creatureID then return end;
self.Model:SetCreature(creatureID);
self.Name:SetText(creatureName);
end
function NarciCustomTalkingHeadMixin:OnModelLoaded()
SetModelLight(self.Model, true, false, -0.5124, -0.4872, -0.7071, 1, 204/255, 204/255, 204/255, 1, 0.8, 0.8, 0.8);
self.Model:SetCamera(0);
self.Model:SetPortraitZoom(1);
self.Model:SetPortraitZoom(0.975);
self.Model:SetAnimation(0, 0);
end
function NarciCustomTalkingHeadMixin:OnClick()
--self:SetUnit();
end
----------------------------------------------
NarciTextOverlayGenericEditBoxMixin = {};
function NarciTextOverlayGenericEditBoxMixin:SetParentObject(fontString)
self:ClearAllPoints();
self:SetPoint("TOPLEFT", fontString.area, "TOPLEFT", 0, 0);
self:SetPoint("BOTTOMRIGHT", fontString.area, "BOTTOMRIGHT", 0, 0);
local font, height = fontString:GetFont();
self:SetFont(font, height, "");
self:SetTextColor( fontString:GetTextColor() );
self:Show();
self:SetText(fontString:GetText() or "");
self:SetFocus();
self:SetCursorPosition(999);
self:SetScale(fontString:GetEffectiveScale());
if self.parentObject then
self.parentObject:Show();
self.parentObject.area:Show();
end
self.parentObject = fontString;
fontString:Hide();
end
function NarciTextOverlayGenericEditBoxMixin:OnTextChanged()
end
function NarciTextOverlayGenericEditBoxMixin:OnEscapePressed()
self:ClearFocus();
end
function NarciTextOverlayGenericEditBoxMixin:OnEditFocusLost()
self:HighlightText(0, 0);
self:Hide();
if self.parentObject then
self.parentObject:SetText(self:GetText() or "");
self.parentObject:Show();
self.parentObject.area:Show();
self.parentObject = nil;
end
end
----Overlay Container----
NarciTextOverlayContainerMixin = {};
function NarciTextOverlayContainerMixin:OnLoad()
Container = self;
self.simpleBalloons = {};
self.advancedBalloons = {};
self.talkingHeads = {};
--Only one subtile
self.SubtitleFrame.Subtitle:SetFontObjectsToTry(GameFontHighlightLarge, GameFontHighlightMedium, GameFontHighlight, GameFontHighlightSmall);
local area1 = CreateFrame("Button", nil, self.SubtitleFrame, "NarciInteractableAreaIndicatorTemplate");
InitializeInteractableLineBorder(area1, self.SubtitleFrame.Subtitle);
area1:SetPoint("TOPLEFT", self.SubtitleFrame.BlackBarBottom, "TOPLEFT", 100, -8);
area1:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -100, 8);
self.SubtitleFrame.Subtitle:SetText("Subtitle");
local MovieSubtitle = self.MovieSubtitleFrame.MovieSubtitle;
MovieSubtitle:SetText("Subtitle");
local area2 = CreateFrame("Button", nil, self.MovieSubtitleFrame, "NarciInteractableAreaIndicatorTemplate");
InitializeInteractableLineBorder(area2, MovieSubtitle);
area2:SetPoint("TOPLEFT", MovieSubtitle, "TOPLEFT", 0, 0);
area2:SetPoint("BOTTOMRIGHT", MovieSubtitle, "BOTTOMRIGHT", 0, 0);
end
function NarciTextOverlayContainerMixin:ToggleBlackBar()
if not self.SubtitleFrame:IsShown() then
local width = CinematicFrame:GetWidth();
local height = CinematicFrame:GetHeight();
local viewableHeight = width * 9 / 16;
local worldFrameHeight = WorldFrame:GetHeight();
local halfDiff = math.max(math.floor((worldFrameHeight - viewableHeight) / 2), 0);
local barHeight = max(halfDiff, 48); --40
self.SubtitleFrame.BlackBarTop:SetHeight(barHeight);
self.SubtitleFrame.BlackBarBottom:SetHeight(barHeight);
self.SubtitleFrame:Show();
return true
else
--WorldFrame:SetAllPoints(nil);
self.SubtitleFrame:Hide();
return false
end
end
function NarciTextOverlayContainerMixin:ToggleMovieSubtitle()
local visible = not self.MovieSubtitleFrame:IsShown();
self.MovieSubtitleFrame:SetShown(visible);
return visible
end
function NarciTextOverlayContainerMixin:OnHide()
end
function NarciTextOverlayContainerMixin:CreateSimpleBalloon()
local Balloon;
local numBalloons = #self.simpleBalloons
for i = 1, numBalloons do
if not self.simpleBalloons[i]:IsShown() then
Balloon = self.simpleBalloons[i];
break;
end
end
if not Balloon then
Balloon = CreateFrame("Button", nil, self, "NarciSimpleSpeechBalloonTemplate");
tinsert(self.simpleBalloons, Balloon);
end
Balloon:Show();
Balloon:Click();
return Balloon
end
function NarciTextOverlayContainerMixin:CreateAdvancedBalloon()
local Balloon;
local numBalloons = #self.advancedBalloons
for i = 1, numBalloons do
if not self.advancedBalloons[i]:IsShown() then
Balloon = self.advancedBalloons[i];
break;
end
end
if not Balloon then
Balloon = CreateFrame("Button", nil, self, "NarciAdjustableSpeechBalloonTemplate");
tinsert(self.advancedBalloons, Balloon);
end
Balloon:Show();
Balloon:Click();
return Balloon
end
function NarciTextOverlayContainerMixin:HideAllControlNodes(exemption)
local ballons;
for i = 1, #self.simpleBalloons do
ballons = self.simpleBalloons[i];
if ballons ~= exemption then
ballons:SetNodesTransparency(0);
else
if PrimaryEditBox:IsEnabled() then
ballons:SetNodesTransparency(0.5);
else
ballons:SetNodesTransparency(1);
end
end
end
for i = 1, #self.advancedBalloons do
ballons = self.advancedBalloons[i];
if ballons ~= exemption then
ballons:SetNodesTransparency(0);
else
if PrimaryEditBox:IsEnabled() then
ballons:SetNodesTransparency(0.5);
else
ballons:SetNodesTransparency(1);
end
end
end
end
function NarciTextOverlayContainerMixin:CreateTalkingHead()
local Head;
local numHeads = #self.talkingHeads
for i = 1, numHeads do
if not self.talkingHeads[i]:IsShown() then
Head = self.talkingHeads[i];
break;
end
end
if not Head then
Head = CreateFrame("Button", nil, self, "NarciTalkingHeadTemplate");
tinsert(self.talkingHeads, Head);
end
Head:Show();
return Head
end
function NarciTextOverlayContainerMixin:CreateOverlay(typeID)
local visible;
if typeID == 1 then
self:CreateSimpleBalloon();
elseif typeID == 2 then
self:CreateAdvancedBalloon();
elseif typeID == 3 then
self:CreateTalkingHead();
elseif typeID == 4 then
visible = self:ToggleMovieSubtitle();
elseif typeID == 5 then
visible = self:ToggleBlackBar();
end
return visible
end
function NarciTextOverlayContainerMixin:HideAllWidgets()
local tables = {self.simpleBalloons, self.advancedBalloons, self.talkingHeads};
for i = 1, #tables do
for j = 1, #tables[i] do
tables[i][j]:Hide();
end
end
self.SubtitleFrame:Hide();
end
--Click to create a text overlay--
NarciNewTextOverlayButtonMixin = {};
function NarciNewTextOverlayButtonMixin:OnLoad()
self.Icon:SetTexture(TEXTURE_PATH_PREFIX.."Icons", nil, nil, "LINEAR");--"TRILINEAR" "LINEAR"
local id = self:GetID();
self.id = id;
self.Icon:SetTexCoord((id - 1)*0.125, id * 0.125, 0.5, 1);
self.Icon:SetAlpha(0.6);
if id == 5 then
self.Icon:SetPoint("CENTER", self, "CENTER", -2, 0);
elseif id == 1 then
self.Icon:SetPoint("CENTER", self, "CENTER", 2, 0);
end
self.Icon:SetSize(22, 22);
self.tooltip = L["Text Overlay Button Tooltip"..id] or " ";
end
function NarciNewTextOverlayButtonMixin:OnEnter()
self.Icon:SetAlpha(1);
local Tooltip = self:GetParent().Tooltip;
Tooltip:ClearAllPoints();
Tooltip.Label:SetText(self.tooltip);
Tooltip:SetWidth( Tooltip.Label:GetWidth() + 8)
Tooltip:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, 0);
Tooltip:Show();
end
function NarciNewTextOverlayButtonMixin:OnLeave()
self.Icon:SetAlpha(0.6);
self:GetParent().Tooltip:Hide();
end
function NarciNewTextOverlayButtonMixin:OnMouseDown()
self.Icon:SetSize(20, 20);
end
function NarciNewTextOverlayButtonMixin:OnMouseUp()
self.Icon:SetSize(22, 22);
end
function NarciNewTextOverlayButtonMixin:OnClick()
if not Container:IsShown() then
self:GetParent().VisibilityButton:Click();
end
local visible = Container:CreateOverlay(self.id);
if visible then
self.Icon:SetVertexColor(0.33, 1, 0.8);
else
self.Icon:SetVertexColor(1, 1, 1);
end
end
function NarciNewTextOverlayButtonMixin:OnDoubleClick()
end
function NarciNewTextOverlayButtonMixin:OnHide()
self.Icon:SetVertexColor(1, 1, 1);
end
--[[
/run NarciAdjustableSpeechBalloonTemplate:SetTail();
/run LetteringSystem:SetText(NarciAdjustableSpeechBalloonTemplate);
/run NarciTextOverlayContainer:CreateAdvancedBalloon();
/run NarciTextOverlayContainer:CreateTalkingHead();
/run NarciTextOverlayContainer.TalkingHead:SetUnit();
--]]