ColorPicker = class(Turbine.UI.Lotro.Window);
-- Default settings
ColorPicker.settings = {};
ColorPicker.settings.recentColors = {};
Turbine.PluginData.Load(Turbine.DataScope.Account, "ColorPicker", function(loadStr)
if (loadStr) then
-- Workaround for Turbine localization bug -- Thanks, Lynx3d!
ColorPicker.settings = ImportTable(loadStr);
if (not ColorPicker.settings) then
Turbine.Shell.WriteLine("Failed to parse ColorPicker.plugindata file!");
return;
end
end
end);
function ColorPicker:Constructor(color, dimension)
Turbine.UI.Lotro.Window.Constructor(self);
if (color) then
color = Thurallor.Utils.Color(color.R, color.G, color.B);
else
color = Thurallor.Utils.Color(0.5, 1.0, 1.0);
end
if (not dimension) then
dimension = "V";
end
self:SetSize(407, 316);
self.displayWidth = Turbine.UI.Display:GetWidth();
self.displayHeight = Turbine.UI.Display:GetHeight();
local left = math.floor(self.displayWidth / 2) - 203;
local top = math.floor(Turbine.UI.Display:GetHeight() / 2 ) - 158;
self:SetPosition(left, top);
self:SetVisible(true);
local prevContext = L:SetContext("/ColorPicker");
self:SetText(L:GetText("DefaultTitle"));
self:SetResizable(true);
self.color = Thurallor.Utils.Color(1, 0.5, 0.5, 0.5);
self.originalColor = Thurallor.Utils.Color(color.R, color.G, color.B);
self.topBorderOutside = Turbine.UI.Control();
self.topBorderOutside:SetParent(self);
self.topBorderOutside:SetBackColor(Turbine.UI.Color(1, 0.5, 0.5, 0.5));
self.topBorderInside = Turbine.UI.Control();
self.topBorderInside:SetParent(self);
self.topBorderInside:SetBackColor(Turbine.UI.Color.Black);
self.palette = Palette(self, "G", "B", self.color);
self.palette.ColorChanged = function()
self:PaletteChanged();
end
self.slider = Slider(self, "R", self.color);
self.slider.ColorChanged = function()
self:SliderChanged();
end
local radioButtonWidth, radioButtonHeight = 127, 20;
self.radioContainer = Turbine.UI.Control();
self.radioContainer:SetParent(self);
self.radioContainer:SetWidth(radioButtonWidth);
self.radioButtons = {};
self.radioButtons["R"] = Thurallor.UI.RadioButton(self.radioContainer, L:GetText("Red"), true);
self.radioButtons["R"]:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.radioButtons["R"]:SetSize(radioButtonWidth, radioButtonHeight);
self.radioButtons["R"].Clicked = function()
self:SelectDimension("R");
end
local top = radioButtonHeight;
self.radioButtons["G"] = Thurallor.UI.RadioButton(self.radioContainer, L:GetText("Green"), false);
self.radioButtons["G"]:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.radioButtons["G"]:SetTop(top);
self.radioButtons["G"]:SetSize(radioButtonWidth, radioButtonHeight);
self.radioButtons["G"].Clicked = function()
self:SelectDimension("G");
end
top = top + radioButtonHeight;
self.radioButtons["B"] = Thurallor.UI.RadioButton(self.radioContainer, L:GetText("Blue"), false);
self.radioButtons["B"]:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.radioButtons["B"]:SetTop(top);
self.radioButtons["B"]:SetSize(radioButtonWidth, radioButtonHeight);
self.radioButtons["B"].Clicked = function()
self:SelectDimension("B");
end
top = top + 30;
self.radioButtons["H"] = Thurallor.UI.RadioButton(self.radioContainer, L:GetText("Hue"), false);
self.radioButtons["H"]:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.radioButtons["H"]:SetTop(top);
self.radioButtons["H"]:SetSize(radioButtonWidth, radioButtonHeight);
self.radioButtons["H"].Clicked = function()
self:SelectDimension("H");
end
top = top + radioButtonHeight;
self.radioButtons["S"] = Thurallor.UI.RadioButton(self.radioContainer, L:GetText("Saturation"), false);
self.radioButtons["S"]:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.radioButtons["S"]:SetTop(top);
self.radioButtons["S"]:SetSize(radioButtonWidth, radioButtonHeight);
self.radioButtons["S"].Clicked = function()
self:SelectDimension("S");
end
top = top + radioButtonHeight;
self.radioButtons["V"] = Thurallor.UI.RadioButton(self.radioContainer, L:GetText("Value"), false);
self.radioButtons["V"]:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.radioButtons["V"]:SetTop(top);
self.radioButtons["V"]:SetSize(radioButtonWidth, radioButtonHeight);
self.radioButtons["V"].Clicked = function()
self:SelectDimension("V");
end
top = top + 34;
Thurallor.UI.RadioButton.LinkPeers({self.radioButtons["R"], self.radioButtons["G"], self.radioButtons["B"], self.radioButtons["H"], self.radioButtons["S"], self.radioButtons["V"]});
self.radioContainer:SetHeight(top);
self.hex = Turbine.UI.Lotro.TextBox();
self.hex:SetParent(self);
self.hex:SetSize(math.floor(radioButtonWidth * 2 / 3 + 0.5), radioButtonHeight);
self.hex:SetFont(Turbine.UI.Lotro.Font.Verdana14);
self.hex:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
self.hex:SetWantsUpdates(true);
self.hex.Update = function()
local hexText = self.hex:GetText();
if (self.hex.prevText ~= hexText) then
self.hex.prevText = hexText;
self:HexChanged();
end
end
self.okButton = Turbine.UI.Lotro.Button();
self.okButton:SetParent(self);
self.okButton:SetSize(math.floor(radioButtonWidth / 2 + 0.5), radioButtonHeight);
self.okButton:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.okButton:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
self.okButton:SetText(L:GetText("Ok"));
self.okButton.Click = function()
self.okButtonClicked = true;
if (self.Accepted) then
self:Accepted();
end
self:AddRecentColor(self.color);
self:Close();
end
self:SetWantsKeyEvents();
self.KeyDown = function(sender, args)
if (args.Action == Turbine.UI.Lotro.Action.EnterKey) then
self.okButton:Click();
end
end
self.presets = SwatchBar(self, L:GetText("StandardColors"));
self.presets.ColorChanged = function(ctl)
self:SwatchClicked(ctl:GetColor());
end
self.colorNames = {};
L:SetContext("/ColorPicker/Colors");
for colorName, color in pairs(Turbine.UI.Color) do
if (Turbine.UI.Color:IsA(color) and (color.A == 1)) then
color = Thurallor.Utils.Color(color.A, color.R, color.G, color.B);
self.colorNames[color:GetHex()] = L:GetText(colorName);
self.presets:AddSwatch(color);
end
end
L:SetContext("/ColorPicker");
local recentColors = ColorPicker.settings.recentColors;
if (#recentColors > 0) then
self.recent = SwatchBar(self, L:GetText("RecentColors"));
self.recent.ColorChanged = function(ctl)
self:SwatchClicked(ctl:GetColor());
end
for r = 1, #recentColors, 1 do
local color = recentColors[r];
color = Thurallor.Utils.Color(color.A, color.R, color.G, color.B);
self.recent:AddSwatch(color);
end
end
self.changingSize = false;
self:DoLayout();
self:SetColor(color);
self.radioButtons[dimension]:MouseClick();
self:SetZOrder(self:GetZOrder());
L:SetContext(prevContext);
-- If the display size changes, images will unstretch, so we need to redraw.
AddCallback(Turbine.UI.Display, "SizeChanged", function()
self:DoLayout();
end);
end
function ColorPicker:SaveSettings()
-- Workaround for Turbine localization bug -- Thanks, Lynx3d!
local saveData = ExportTable(ColorPicker.settings);
Turbine.PluginData.Save(Turbine.DataScope.Account, "ColorPicker", saveData, function()
--Puts("Save complete.");
end);
end
function ColorPicker:AddRecentColor(newColor)
local recentColors = ColorPicker.settings.recentColors;
-- Remove duplicates.
local oldColorIndex = nil;
for r = 1, #recentColors, 1 do
local oldColor = recentColors[r];
oldColor = Thurallor.Utils.Color(oldColor.A, oldColor.R, oldColor.G, oldColor.B);
if (oldColor:GetHex() == newColor:GetHex()) then
oldColorIndex = r;
break;
end
end
if (oldColorIndex) then
table.remove(recentColors, oldColorIndex);
end
-- Add the new color.
table.insert(recentColors, 1, newColor);
self:SaveSettings();
end
function ColorPicker:DoLayout()
local titleHeight = 16;
local marginSize = 23;
local spacing = 10;
local sliderWidth = 20;
local borderSize = 3;
local top = titleHeight + marginSize;
local left = marginSize;
local radioContainerWidth, radioContainerHeight = self.radioContainer:GetSize();
local minPaletteSize = radioContainerHeight + 50;
local paletteSize = self:GetWidth() - (marginSize * 2) - (spacing * 2) - self.radioContainer:GetWidth() - sliderWidth;
if (paletteSize < minPaletteSize) then
self:SetWidth(self:GetWidth() + (minPaletteSize - paletteSize));
paletteSize = minPaletteSize;
end
self.palette:SetPosition(left, top);
self.palette:SetSize(paletteSize, paletteSize);
self.palette:SetStretchMode(1);
left = left + paletteSize + spacing;
self.slider:SetPosition(left, top);
self.slider:SetSize(sliderWidth, paletteSize);
self.slider:SetStretchMode(1);
left = left + sliderWidth + spacing;
top = top + math.floor(((paletteSize - 50 - radioContainerHeight) / 2) + 0.5);
self.radioContainer:SetZOrder(self:GetZOrder() + 2);
self.radioContainer:SetPosition(left, top);
local right = left + self.radioContainer:GetWidth();
top = titleHeight + marginSize + paletteSize - self.okButton:GetHeight();
self.okButton:SetPosition(left + math.floor(radioContainerWidth / 4 + 0.5), top - 3);
local bottom = top + self.okButton:GetHeight();
self.topBorderOutside:SetPosition(marginSize - borderSize, titleHeight + marginSize - borderSize);
self.topBorderOutside:SetSize(right - marginSize + (2 * borderSize), bottom - (titleHeight + marginSize) + (2 * borderSize));
self.topBorderInside:SetPosition(self.topBorderOutside:GetLeft() + 1, self.topBorderOutside:GetTop() + 1);
self.topBorderInside:SetSize(self.topBorderOutside:GetWidth() - 2, self.topBorderOutside:GetHeight() - 2);
top = top - 8 - self.hex:GetHeight();
self.hex:SetPosition(left + math.floor(radioContainerWidth / 6 + 0.5), top);
spacing = spacing + 2;
left = marginSize;
top = titleHeight + marginSize + paletteSize + spacing;
self.presets:SetPosition(left, top);
local width = right - left;
if (#ColorPicker.settings.recentColors > 0) then
local halfWidth = math.floor(0.5 + (width - spacing) / 2)
self.presets:SetSize(halfWidth, 40);
left = left + halfWidth + spacing;
self.recent:SetPosition(left, top);
self.recent:SetSize(width - (halfWidth + spacing), 40);
else
self.presets:SetSize(width, 40);
end
top = top + 40;
self:SetHeight(top + marginSize + 2);
end
function ColorPicker:SetZOrder(z)
if (z > 2147483647 - 4) then
-- Need room for four layers of stuff
z = 2147483647 - 4;
end
Turbine.UI.Lotro.Window.SetZOrder(self, z);
self.radioContainer:SetZOrder(z + 2);
self.palette:SetZOrder(z + 1);
self.slider:SetZOrder(z + 1);
end
function ColorPicker:SelectDimension(sliderDimension)
local paletteXDimension, paletteYDimension = Thurallor.Utils.Color.GetOtherDimensions(sliderDimension);
self.slider:SetDimension(sliderDimension);
self.palette:SetDimensions(paletteXDimension, paletteYDimension);
-- Update display
self:SetColor(self.color);
end
function ColorPicker:HexChanged()
local R, G, B = string.match(self.hex:GetText(), "^(%x%x)(%x%x)(%x%x)$");
if (R and G and B) then
self.hex:SetBackColor(Thurallor.Utils.Color(1, 1, 1, 1));
if (not self.updatingHex) then
self.color:SetHex(self.hex:GetText());
self:SetColor(self.color);
self:_ColorChanged();
end
else
self.hex:SetBackColor(Thurallor.Utils.Color(1, 1, 0, 0));
end
end
function ColorPicker:SetColor(color)
self.suppressCallbacks = true;
self.slider:SetColor(color);
self.palette:SetColor(color);
self:PaletteChanged();
self:SliderChanged();
self.suppressCallbacks = false;
end
function ColorPicker:GetColor()
return self.color;
end
function ColorPicker:UpdateHex()
local R, G, B, H, S, V;
self.updatingHex = true;
self.hex:SetText(self.color:GetHex());
self.hex:Focus();
self:Activate();
self.hex.prevText = self.hex:GetText();
self.updatingHex = false;
R = math.floor(self.color.R * 100 + 0.5);
G = math.floor(self.color.G * 100 + 0.5);
B = math.floor(self.color.B * 100 + 0.5);
H, S, V = self.color:GetHSV();
H = math.floor(H * 360 + 0.5);
S = math.floor(S * 100 + 0.5);
V = math.floor(V * 100 + 0.5);
local prevContext = L:SetContext("/ColorPicker");
self.radioButtons["R"]:SetText(L:GetText("Red") .. ": " .. tostring(R) .. "%");
self.radioButtons["G"]:SetText(L:GetText("Green") .. ": " .. tostring(G) .. "%");
self.radioButtons["B"]:SetText(L:GetText("Blue") .. ": " .. tostring(B) .. "%");
self.radioButtons["H"]:SetText(L:GetText("Hue") .. ": " .. tostring(H) .. "°");
self.radioButtons["S"]:SetText(L:GetText("Saturation") .. ": " .. tostring(S) .. "%");
self.radioButtons["V"]:SetText(L:GetText("Value") .. ": " .. tostring(V) .. "%");
L:SetContext(prevContext);
local colorName = self.colorNames[self.color:GetHex()];
self.palette:ShowColorName(colorName);
end
function ColorPicker:SliderChanged()
self.slider:GetColor(self.color);
self:UpdateHex();
self.palette:SetGamut(self.color);
self.palette:SetSpotColor(self.color);
self:_ColorChanged();
end
function ColorPicker:PaletteChanged()
self.palette:GetColor(self.color);
self:UpdateHex();
self.palette:SetSpotColor(self.color);
self.slider:SetGamut(self.color);
self:_ColorChanged();
end
function ColorPicker:SwatchClicked(color)
self:SetColor(color);
self:_ColorChanged();
end
function ColorPicker:SizeChanged()
if (not self.changingSize) then
self.changingSize = true;
self:DoLayout();
self.changingSize = false;
end
end
function ColorPicker:PositionChanged()
self:DoLayout();
end
function ColorPicker:FreezeDimension(dim, value)
self.frozen = self.frozen or {};
self.frozen[dim] = value;
for _, d in pairs({ "R", "G", "B", "H", "S", "V" }) do
if (dim ~= d) then
self.radioButtons[d]:SetEnabled(false);
end
end
self:SelectDimension(dim);
self.slider:SetLocked(true);
self:_ColorChanged();
end
function ColorPicker:_ColorChanged()
local R, G, B = self.color.R, self.color.G, self.color.B;
if (self.frozen) then
for dim, value in pairs(self.frozen) do
self.color:Set(dim, value);
end
if ((self.color.R ~= R) or (self.color.G ~= G) or (self.color.B ~= B)) then
self:SetColor(self.color);
end
end
if (not self.suppressCallbacks) then
DoCallbacks(self, "ColorChanged");
end
end
function ColorPicker:Closing()
self:SaveSettings();
if (not self.okButtonClicked) then
-- User closed the window. Revert to original color.
self.color = self.originalColor;
self:_ColorChanged();
DoCallbacks(self, "Canceled");
end
self.palette:Close();
self.slider:Close();
self:SetWantsKeyEvents(false);
end