comparison State.lua @ 79:a45255f5d0c2

- Converted State options to use static options table + metatable handler - Fixed bug with FixAll() - Added external property handler registration - Removed pages handler (to be migrated to ReAction_Action)
author Flick <flickerstreak@gmail.com>
date Tue, 24 Jun 2008 23:39:43 +0000
parents da8ba8783924
children 42ec2938d65a
comparison
equal deleted inserted replaced
78:502cdb5666e2 79:a45255f5d0c2
51 end) 51 end)
52 return r 52 return r
53 end 53 end
54 54
55 55
56 local InitRules, ApplyStates, SetProperty, GetProperty 56 local InitRules, ApplyStates, SetProperty, GetProperty, RegisterProperty
57 57
58 -- PRIVATE -- 58 -- PRIVATE --
59 do 59 do
60 -- As far as I can tell the macro clauses are NOT locale-specific. 60 -- As far as I can tell the macro clauses are NOT locale-specific.
61 local ruleformats = { 61 local ruleformats = {
140 local ofskey = ofskeys[ckey] 140 local ofskey = ofskeys[ckey]
141 local default = select(barofsidx[ckey], bar:GetAnchor()) 141 local default = select(barofsidx[ckey], bar:GetAnchor())
142 bar:SetStateAttribute(format("headofs%s",ofskeys[ckey]), map, default) 142 bar:SetStateAttribute(format("headofs%s",ofskeys[ckey]), map, default)
143 end 143 end
144 144
145 -- the name of the function maps to the name of the config element 145 -- the table key name for each function maps to the name of the config element
146 local propertyFuncs = { 146 local propertyFuncs = {
147 hide = function( bar, states ) 147 hide = function( bar, states )
148 local hs = { } 148 local hs = { }
149 for state, config in pairs(states) do 149 for state, config in pairs(states) do
150 if config.hide then 150 if config.hide then
151 table.insert(hs, state) 151 table.insert(hs, state)
152 end 152 end
153 end 153 end
154 bar:SetStateAttribute("hidestates", nil, table.concat(hs,","), true) -- pass to buttons 154 bar:SetStateAttribute("hidestates", nil, table.concat(hs,","), true) -- pass to buttons
155 end,
156
157 page = function( bar, states )
158 local map = { }
159 for state, config in pairs(states) do
160 if config.page then
161 map[state] = format("page%d",config.page)
162 end
163 end
164 bar:SetStateAttribute("statebutton", map)
165 end, 155 end,
166 156
167 keybindstate = function( bar, states ) 157 keybindstate = function( bar, states )
168 local map = { } 158 local map = { }
169 for state, config in pairs(states) do 159 for state, config in pairs(states) do
211 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") 201 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
212 tbuild(states, state)[propname] = value 202 tbuild(states, state)[propname] = value
213 local f = propertyFuncs[propname] 203 local f = propertyFuncs[propname]
214 if f then 204 if f then
215 f(bar, states) 205 f(bar, states)
206 end
207 end
208
209 function RegisterProperty( propname, f )
210 propertyFuncs[propname] = f
211 for bar in ReAction:IterateBars() do
212 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
213 if states then
214 f(bar,states)
215 end
216 end 216 end
217 end 217 end
218 218
219 219
220 220
377 local bars = self.db.profile.bars 377 local bars = self.db.profile.bars
378 bars[newname], bars[oldname] = bars[oldname], nil 378 bars[newname], bars[oldname] = bars[oldname], nil
379 end 379 end
380 380
381 function module:OnConfigModeChanged(event, mode) 381 function module:OnConfigModeChanged(event, mode)
382 -- TODO: unregister all state drivers (temporarily) and hidestates 382 -- nothing to do (yet)
383 end 383 end
384 384
385 385
386 386
387 -- Options -- 387 -- Options --
388 388
389 local CreateBarOptions 389 local CreateBarOptions, RegisterPropertyOptions
390 do 390 do
391 local playerClass = select(2, UnitClass("player"))
391 local function ClassCheck(...) 392 local function ClassCheck(...)
392 for i = 1, select('#',...) do 393 for i = 1, select('#',...) do
393 local _, c = UnitClass("player") 394 if playerClass == select(i,...) then
394 if c == select(i,...) then
395 return false 395 return false
396 end 396 end
397 end 397 end
398 return true 398 return true
399 end 399 end
440 table.insert(ruleMap, key) 440 table.insert(ruleMap, key)
441 end 441 end
442 end 442 end
443 end 443 end
444 444
445 local stateOptions = {
446 ordering = {
447 name = L["Info"],
448 order = 1,
449 type = "group",
450 args = {
451 delete = {
452 name = L["Delete this State"],
453 order = -1,
454 type = "execute",
455 func = "DeleteState",
456 },
457 rename = {
458 name = L["Name"],
459 order = 1,
460 type = "input",
461 get = "GetName",
462 set = "SetStateName",
463 pattern = "^%w*$",
464 usage = L["State names must be alphanumeric without spaces"],
465 },
466 ordering = {
467 name = L["Evaluation Order"],
468 desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
469 order = 2,
470 type = "group",
471 inline = true,
472 args = {
473 up = {
474 name = L["Up"],
475 order = 1,
476 type = "execute",
477 width = "half",
478 func = "MoveStateUp",
479 },
480 down = {
481 name = L["Down"],
482 order = 2,
483 type = "execute",
484 width = "half",
485 func = "MoveStateDown",
486 }
487 }
488 }
489 }
490 },
491 properties = {
492 name = L["Properties"],
493 order = 2,
494 type = "group",
495 args = {
496 desc = {
497 name = L["Set the properties for the bar when in this state"],
498 order = 1,
499 type = "description"
500 },
501 hide = {
502 name = L["Hide Bar"],
503 order = 91,
504 type = "toggle",
505 set = "SetProp",
506 get = "GetProp",
507 },
508 keybindstate = {
509 name = L["Override Keybinds"],
510 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
511 order = 92,
512 type = "toggle",
513 set = "SetProp",
514 get = "GetProp",
515 },
516 position = {
517 name = L["Position"],
518 order = 93,
519 type = "group",
520 inline = true,
521 args = {
522 enableAnchor = {
523 name = L["Set New Position"],
524 order = 1,
525 type = "toggle",
526 set = "SetProp",
527 get = "GetProp",
528 },
529 anchorPoint = {
530 name = L["Point"],
531 order = 2,
532 type = "select",
533 values = pointTable,
534 set = "SetAnchorPointProp",
535 get = "GetAnchorPointProp",
536 disabled = "GetAnchorDisabled",
537 hidden = "GetAnchorDisabled",
538 },
539 anchorRelPoint = {
540 name = L["Relative Point"],
541 order = 3,
542 type = "select",
543 values = pointTable,
544 set = "SetAnchorPointProp",
545 get = "GetAnchorPointProp",
546 disabled = "GetAnchorDisabled",
547 hidden = "GetAnchorDisabled",
548 },
549 anchorX = {
550 name = L["X Offset"],
551 order = 4,
552 type = "range",
553 min = -100,
554 max = 100,
555 step = 1,
556 set = "SetProp",
557 get = "GetProp",
558 disabled = "GetAnchorDisabled",
559 hidden = "GetAnchorDisabled",
560 },
561 anchorY = {
562 name = L["Y Offset"],
563 order = 5,
564 type = "range",
565 min = -100,
566 max = 100,
567 step = 1,
568 set = "SetProp",
569 get = "GetProp",
570 disabled = "GetAnchorDisabled",
571 hidden = "GetAnchorDisabled",
572 },
573 },
574 },
575 scale = {
576 name = L["Scale"],
577 order = 94,
578 type = "group",
579 inline = true,
580 args = {
581 enableScale = {
582 name = L["Set New Scale"],
583 order = 1,
584 type = "toggle",
585 set = "SetProp",
586 get = "GetProp",
587 },
588 scale = {
589 name = L["Scale"],
590 order = 2,
591 type = "range",
592 min = 0.1,
593 max = 2.5,
594 step = 0.05,
595 isPercent = true,
596 set = "SetProp",
597 get = "GetProp",
598 disabled = "GetScaleDisabled",
599 hidden = "GetScaleDisabled",
600 },
601 },
602 },
603 },
604 plugins = { }
605 },
606 rules = {
607 name = L["Rule"],
608 order = 3,
609 type = "group",
610 args = {
611 mode = {
612 name = L["Select this state"],
613 order = 2,
614 type = "select",
615 style = "radio",
616 values = {
617 default = L["by default"],
618 any = L["when ANY of these"],
619 all = L["when ALL of these"],
620 custom = L["via custom rule"],
621 keybind = L["via keybinding"],
622 },
623 set = "SetType",
624 get = "GetType",
625 },
626 clear = {
627 name = L["Clear All"],
628 order = 3,
629 type = "execute",
630 hidden = "GetClearAllDisabled",
631 disabled = "GetClearAllDisabled",
632 func = "ClearAllConditions",
633 },
634 inputs = {
635 name = L["Conditions"],
636 order = 4,
637 type = "multiselect",
638 hidden = "GetConditionsDisabled",
639 disabled = "GetConditionsDisabled",
640 values = ruleSelect,
641 set = "SetCondition",
642 get = "GetCondition",
643 },
644 custom = {
645 name = L["Custom Rule"],
646 order = 5,
647 type = "input",
648 multiline = true,
649 hidden = "GetCustomDisabled",
650 disabled = "GetCustomDisabled",
651 desc = L["Syntax like macro rules: see preset rules for examples"],
652 set = "SetCustomRule",
653 get = "GetCustomRule",
654 validate = "ValidateCustomRule",
655 },
656 keybind = {
657 name = L["Keybinding"],
658 order = 6,
659 inline = true,
660 hidden = "GetKeybindDisabled",
661 disabled = "GetKeybindDisabled",
662 type = "group",
663 args = {
664 desc = {
665 name = L["Invoking a state keybind toggles an override of all other transition rules."],
666 order = 1,
667 type = "description",
668 },
669 keybind = {
670 name = L["State Hotkey"],
671 desc = L["Define an override toggle keybind"],
672 order = 2,
673 type = "keybinding",
674 set = "SetKeybind",
675 get = "GetKeybind",
676 },
677 },
678 },
679 },
680 },
681 }
682
683 local StateHandler = { }
684
685 function StateHandler:New( bar, opts )
686 local self = setmetatable({ bar = bar }, { __index = StateHandler })
687
688 function self:GetName()
689 return opts.name
690 end
691
692 function self:SetName(name)
693 opts.name = name
694 end
695
696 function self:GetOrder()
697 return opts.order
698 end
699
700 -- get reference to states table: even if the bar
701 -- name changes the states table ref won't
702 self.states = tbuild(module.db.profile.bars, bar:GetName(), "states")
703
704 tbuild(self.states, opts.name)
705
706 opts.order = self:GetRule("order")
707 if opts.order == nil then
708 -- add after the highest
709 opts.order = 100
710 for _, state in pairs(self.states) do
711 local x = tonumber(tfetch(state, "rule", "order"))
712 if x and x >= opts.order then
713 opts.order = x + 1
714 end
715 end
716 self:SetRule("order",opts.order)
717 end
718
719 return self
720 end
721
722 -- helper methods
723
724 function StateHandler:SetRule( key, value, ... )
725 tbuild(self.states, self:GetName(), "rule", ...)[key] = value
726 end
727
728 function StateHandler:GetRule( ... )
729 return tfetch(self.states, self:GetName(), "rule", ...)
730 end
731
732 function StateHandler:FixAll( setkey )
733 -- if multiple selections in the same group are chosen when 'all' is selected,
734 -- keep only one of them. If changing the mode, the first in the fields list will
735 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
736 -- it will be retained.
737 local notified = false
738 if self:GetRule("type") == "all" then
739 for _, c in ipairs(rules) do
740 local rule, hidden, fields = unpack(c)
741 local once = false
742 if setkey then
743 for idx, field in ipairs(fields) do
744 if next(field) == setkey then
745 once = true
746 end
747 end
748 end
749 for idx, field in ipairs(fields) do
750 local key = next(field)
751 if self:GetRule("values",key) then
752 if once and key ~= setkey then
753 self:SetRule(key,false,"values")
754 if not setkey and not notified then
755 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
756 notified = true
757 end
758 end
759 once = true
760 end
761 end
762 end
763 end
764 end
765
766 function StateHandler:GetNeighbors()
767 local before, after
768 for k, v in pairs(self.states) do
769 local o = tonumber(tfetch(v, "rule", "order"))
770 if o and k ~= self:GetName() then
771 local obefore = tfetch(self.states,before,"rule","order")
772 local oafter = tfetch(self.states,after,"rule","order")
773 if o < self:GetOrder() and (not obefore or obefore < o) then
774 before = k
775 end
776 if o > self:GetOrder() and (not oafter or oafter > o) then
777 after = k
778 end
779 end
780 end
781 return before, after
782 end
783
784 function StateHandler:SwapOrder( a, b )
785 -- do options table
786 local args = optionMap[self.bar].args
787 args[a].order, args[b].order = args[b].order, args[a].order
788 -- do profile
789 a = tbuild(self.states, a, "rule")
790 b = tbuild(self.states, b, "rule")
791 a.order, b.order = b.order, a.order
792 end
793
794 -- handler methods
795
796 function StateHandler:GetProp( info )
797 -- gets property of the same name as the options arg
798 return GetProperty(self.bar, self:GetName(), info[#info])
799 end
800
801 function StateHandler:SetProp( info, value )
802 -- sets property of the same name as the options arg
803 SetProperty(self.bar, self:GetName(), info[#info], value)
804 end
805
806 function StateHandler:DeleteState()
807 if self.states[self:GetName()] then
808 self.states[self:GetName()] = nil
809 ApplyStates(self.bar)
810 end
811 optionMap[self.bar].args[self:GetName()] = nil
812 end
813
814 function StateHandler:SetStateName(info, value)
815 -- check for existing state name
816 if self.states[value] then
817 ReAction:UserError(format(L["State named '%s' already exists"],value))
818 return
819 end
820 local args = optionMap[self.bar].args
821 local name = self:GetName()
822 self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil
823 self:SetName(value)
824 ApplyStates(self.bar)
825 end
826
827 function StateHandler:MoveStateUp()
828 local before, after = self:GetNeighbors()
829 if before then
830 self:SwapOrder(before, self:GetName())
831 ApplyStates(self.bar)
832 end
833 end
834
835 function StateHandler:MoveStateDown()
836 local before, after = self:GetNeighbors()
837 if after then
838 self:SwapOrder(self:GetName(), after)
839 ApplyStates(self.bar)
840 end
841 end
842
843 function StateHandler:GetAnchorDisabled()
844 return not GetProperty(self.bar, self:GetName(), "enableAnchor")
845 end
846
847 function StateHandler:SetAnchorPointProp(info, value)
848 self:SetProp(info, value ~= "NONE" and value or nil)
849 end
850
851 function StateHandler:GetAnchorPointProp(info)
852 return self:GetProp(info) or "NONE"
853 end
854
855 function StateHandler:GetScaleDisabled()
856 return not GetProperty(self.bar, self:GetName(), "enableScale")
857 end
858
859 function StateHandler:SetType(info, value)
860 self:SetRule("type", value)
861 self:FixAll()
862 ApplyStates(self.bar)
863 end
864
865 function StateHandler:GetType()
866 return self:GetRule("type")
867 end
868
869 function StateHandler:GetClearAllDisabled()
870 local t = self:GetRule("type")
871 return not( t == "any" or t == "all" or t == "custom")
872 end
873
874 function StateHandler:ClearAllConditions()
875 local t = self:GetRule("type")
876 if t == "custom" then
877 self:SetRule("custom","")
878 elseif t == "any" or t == "all" then
879 self:SetRule("values", {})
880 end
881 ApplyStates(self.bar)
882 end
883
884 function StateHandler:GetConditionsDisabled()
885 local t = self:GetRule("type")
886 return not( t == "any" or t == "all")
887 end
888
889 function StateHandler:SetCondition(info, key, value)
890 self:SetRule(ruleMap[key], value or nil, "values")
891 if value then
892 self:FixAll(ruleMap[key])
893 end
894 ApplyStates(self.bar)
895 end
896
897 function StateHandler:GetCondition(info, key)
898 return self:GetRule("values", ruleMap[key]) or false
899 end
900
901 function StateHandler:GetCustomDisabled()
902 return self:GetRule("type") ~= "custom"
903 end
904
905 function StateHandler:SetCustomRule(info, value)
906 self:SetRule("custom",value)
907 ApplyStates(self.bar)
908 end
909
910 function StateHandler:GetCustomRule()
911 return self:GetRule("custom") or ""
912 end
913
914 function StateHandler:ValidateCustomRule(info, value)
915 local s = value:gsub("%s","") -- remove all spaces
916 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
917 repeat
918 if s == "" then
919 return true
920 end
921 local c, r = s:match("(%b[])(.*)")
922 if c == nil and s and #s > 0 then
923 return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "")
924 end
925 s = r
926 until c == nil
927 return true
928 end
929
930 function StateHandler:GetKeybindDisabled()
931 return self:GetRule("type") ~= "keybind"
932 end
933
934 function StateHandler:GetKeybind()
935 return self:GetRule("keybind")
936 end
937
938 function StateHandler:SetKeybind(info, value)
939 if value and #value == 0 then
940 value = nil
941 end
942 self:SetRule("keybind",value)
943 ApplyStates(self.bar)
944 end
945
445 local function CreateStateOptions(bar, name) 946 local function CreateStateOptions(bar, name)
446 local opts = { 947 local opts = {
447 type = "group", 948 type = "group",
448 name = name, 949 name = name,
449 childGroups = "tab", 950 childGroups = "tab",
951 args = stateOptions
450 } 952 }
451 953
452 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") 954 opts.handler = StateHandler:New(bar,opts)
453 955
454 local function update()
455 ApplyStates(bar)
456 end
457
458 local function setrule( key, value, ... )
459 tbuild(states, opts.name, "rule", ...)[key] = value
460 end
461
462 local function getrule( ... )
463 return tfetch(states, opts.name, "rule", ...)
464 end
465
466 local function setprop(info, value)
467 SetProperty(bar, opts.name, info[#info], value)
468 end
469
470 local function getprop(info)
471 return GetProperty(bar, opts.name, info[#info])
472 end
473
474 local function fixall(setkey)
475 -- if multiple selections in the same group are chosen when 'all' is selected,
476 -- keep only one of them. If changing the mode, the first in the fields list will
477 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
478 -- it will be retained.
479 local notified = false
480 for _, c in ipairs(rules) do
481 local rule, hidden, fields = unpack(c)
482 local found = false
483 for key in ipairs(fields) do
484 if getrule("values",key) then
485 if (found or setkey) and key ~= setkey then
486 setrule(key,false,"values")
487 if not setkey and not notified then
488 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
489 notified = true
490 end
491 end
492 found = true
493 end
494 end
495 end
496 end
497
498 local function getNeighbors()
499 local before, after
500 for k, v in pairs(states) do
501 local o = tonumber(tfetch(v, "rule", "order"))
502 if o and k ~= opts.name then
503 local obefore = tfetch(states,before,"rule","order")
504 local oafter = tfetch(states,after,"rule","order")
505 if o < opts.order and (not obefore or obefore < o) then
506 before = k
507 end
508 if o > opts.order and (not oafter or oafter > o) then
509 after = k
510 end
511 end
512 end
513 return before, after
514 end
515
516 local function swapOrder( a, b )
517 -- do options table
518 local args = optionMap[bar].args
519 args[a].order, args[b].order = args[b].order, args[a].order
520 -- do profile
521 a = tbuild(states, a, "rule")
522 b = tbuild(states, b, "rule")
523 a.order, b.order = b.order, a.order
524 end
525
526 local function anchordisable()
527 return not GetProperty(bar, opts.name, "enableAnchor")
528 end
529
530 tbuild(states, name)
531
532 opts.order = getrule("order")
533 if opts.order == nil then
534 -- add after the highest
535 opts.order = 100
536 for _, state in pairs(states) do
537 local x = tonumber(tfetch(state, "rule", "order"))
538 if x and x >= opts.order then
539 opts.order = x + 1
540 end
541 end
542 setrule("order",opts.order)
543 end
544
545 opts.args = {
546 ordering = {
547 name = L["Info"],
548 order = 1,
549 type = "group",
550 args = {
551 delete = {
552 name = L["Delete this State"],
553 order = -1,
554 type = "execute",
555 func = function(info)
556 if states[opts.name] then
557 states[opts.name] = nil
558 ApplyStates(bar)
559 end
560 optionMap[bar].args[opts.name] = nil
561 end,
562 },
563 rename = {
564 name = L["Name"],
565 order = 1,
566 type = "input",
567 get = function() return opts.name end,
568 set = function(info, value)
569 -- check for existing state name
570 if states[value] then
571 format(L["State named '%s' already exists"],value)
572 end
573 local args = optionMap[bar].args
574 states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil
575 opts.name = value
576 update()
577 end,
578 pattern = "^%w*$",
579 usage = L["State names must be alphanumeric without spaces"],
580 },
581 ordering = {
582 name = L["Evaluation Order"],
583 desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
584 order = 2,
585 type = "group",
586 inline = true,
587 args = {
588 up = {
589 name = L["Up"],
590 order = 1,
591 type = "execute",
592 width = "half",
593 func = function()
594 local before, after = getNeighbors()
595 if before then
596 swapOrder(before, opts.name)
597 update()
598 end
599 end,
600 },
601 down = {
602 name = L["Down"],
603 order = 2,
604 type = "execute",
605 width = "half",
606 func = function()
607 local before, after = getNeighbors()
608 if after then
609 swapOrder(opts.name, after)
610 update()
611 end
612 end,
613 }
614 }
615 }
616 }
617 },
618 properties = {
619 name = L["Properties"],
620 order = 2,
621 type = "group",
622 args = {
623 desc = {
624 name = L["Set the properties for the bar when in this state"],
625 order = 1,
626 type = "description"
627 },
628 hide = {
629 name = L["Hide Bar"],
630 order = 2,
631 type = "toggle",
632 set = setprop,
633 get = getprop,
634 },
635 page = {
636 name = L["Show Page #"],
637 order = 3,
638 type = "select",
639 disabled = function()
640 --return bar:GetNumPages() < 2
641 return true
642 end,
643 hidden = function()
644 --return bar:GetNumPages() < 2
645 return true
646 end,
647 values = function()
648 -- use off-by-one ordering to put (none) first in the list
649 local pages = { [1] = L["(none)"] }
650 --for i = 1, bar:GetNumPages() do
651 -- pages[i+1] = i
652 --end
653 return pages
654 end,
655 set = function(info, value)
656 value = value - 1
657 setprop(info, value > 0 and value or nil)
658 end,
659 get = function(info)
660 return getprop(info) or L["(none)"]
661 end,
662 },
663 keybindstate = {
664 name = L["Override Keybinds"],
665 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
666 order = 4,
667 type = "toggle",
668 set = setprop,
669 get = getprop,
670 },
671 position = {
672 name = L["Position"],
673 order = 5,
674 type = "group",
675 inline = true,
676 args = {
677 enableAnchor = {
678 name = L["Set New Position"],
679 order = 1,
680 type = "toggle",
681 set = setprop,
682 get = getprop,
683 },
684 anchorPoint = {
685 name = L["Point"],
686 order = 2,
687 type = "select",
688 values = pointTable,
689 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end,
690 get = function(info) return getprop(info) or "NONE" end,
691 disabled = anchordisable,
692 hidden = anchordisable,
693 },
694 anchorRelPoint = {
695 name = L["Relative Point"],
696 order = 3,
697 type = "select",
698 values = pointTable,
699 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end,
700 get = function(info) return getprop(info) or "NONE" end,
701 disabled = anchordisable,
702 hidden = anchordisable,
703 },
704 anchorX = {
705 name = L["X Offset"],
706 order = 4,
707 type = "range",
708 min = -100,
709 max = 100,
710 step = 1,
711 set = setprop,
712 get = getprop,
713 disabled = anchordisable,
714 hidden = anchordisable,
715 },
716 anchorY = {
717 name = L["Y Offset"],
718 order = 5,
719 type = "range",
720 min = -100,
721 max = 100,
722 step = 1,
723 set = setprop,
724 get = getprop,
725 disabled = anchordisable,
726 hidden = anchordisable,
727 },
728 },
729 },
730 scale = {
731 name = L["Scale"],
732 order = 6,
733 type = "group",
734 inline = true,
735 args = {
736 enableScale = {
737 name = L["Set New Scale"],
738 order = 1,
739 type = "toggle",
740 set = setprop,
741 get = getprop,
742 },
743 scale = {
744 name = L["Scale"],
745 order = 2,
746 type = "range",
747 min = 0.1,
748 max = 2.5,
749 step = 0.05,
750 isPercent = true,
751 set = setprop,
752 get = function(info) return getprop(info) or 1 end,
753 disabled = function() return not GetProperty(bar, opts.name, "enableScale") end,
754 hidden = function() return not GetProperty(bar, opts.name, "enableScale") end,
755 },
756 },
757 },
758 },
759 },
760 rules = {
761 name = L["Rule"],
762 order = 3,
763 type = "group",
764 args = {
765 mode = {
766 name = L["Select this state"],
767 order = 2,
768 type = "select",
769 style = "radio",
770 values = {
771 default = L["by default"],
772 any = L["when ANY of these"],
773 all = L["when ALL of these"],
774 custom = L["via custom rule"],
775 keybind = L["via keybinding"],
776 },
777 set = function( info, value )
778 setrule("type", value)
779 fixall()
780 update()
781 end,
782 get = function( info )
783 return getrule("type")
784 end,
785 },
786 clear = {
787 name = L["Clear All"],
788 order = 3,
789 type = "execute",
790 hidden = function()
791 local t = getrule("type")
792 return t ~= "any" and t ~= "all"
793 end,
794 disabled = function()
795 local t = getrule("type")
796 return t ~= "any" and t ~= "all"
797 end,
798 func = function()
799 local type = getrule("type")
800 if type == "custom" then
801 setrule("custom","")
802 elseif type == "any" or type == "all" then
803 setrule("values", {})
804 end
805 update()
806 end,
807 },
808 inputs = {
809 name = L["Conditions"],
810 order = 4,
811 type = "multiselect",
812 hidden = function()
813 local t = getrule("type")
814 return t ~= "any" and t ~= "all"
815 end,
816 disabled = function()
817 local t = getrule("type")
818 return t ~= "any" and t ~= "all"
819 end,
820 values = ruleSelect,
821 set = function(info, key, value )
822 setrule(ruleMap[key], value or nil, "values")
823 if value then
824 fixall(ruleMap[key])
825 end
826 update()
827 end,
828 get = function(info, key)
829 return getrule("values", ruleMap[key]) or false
830 end,
831 },
832 custom = {
833 name = L["Custom Rule"],
834 order = 5,
835 type = "input",
836 multiline = true,
837 hidden = function()
838 return getrule("type") ~= "custom"
839 end,
840 disabled = function()
841 return getrule("type") ~= "custom"
842 end,
843 desc = L["Syntax like macro rules: see preset rules for examples"],
844 set = function(info, value)
845 setrule("custom",value)
846 update()
847 end,
848 get = function(info)
849 return getrule("custom") or ""
850 end,
851 validate = function (info, rule)
852 local s = rule:gsub("%s","") -- remove all spaces
853 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
854 repeat
855 if s == "" then
856 return true
857 end
858 local c, r = s:match("(%b[])(.*)")
859 if c == nil and s and #s > 0 then
860 return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],rule)
861 end
862 s = r
863 until c == nil
864 return true
865 end,
866 },
867 keybind = {
868 name = L["Keybinding"],
869 order = 6,
870 inline = true,
871 hidden = function() return getrule("type") ~= "keybind" end,
872 disabled = function() return getrule("type") ~= "keybind" end,
873 type = "group",
874 args = {
875 desc = {
876 name = L["Invoking a state keybind toggles an override of all other transition rules."],
877 order = 1,
878 type = "description",
879 },
880 keybind = {
881 name = L["State Hotkey"],
882 desc = L["Define an override toggle keybind"],
883 order = 2,
884 type = "keybinding",
885 set = function(info, value)
886 if value and #value == 0 then
887 value = nil
888 end
889 setrule("keybind",value)
890 update()
891 end,
892 get = function() return getrule("keybind") end,
893 },
894 },
895 },
896 },
897 },
898 }
899 return opts 956 return opts
900 end 957 end
901 958
902 959
903 CreateBarOptions = function(bar) 960 function RegisterPropertyOptions( field, options, handler )
961 stateOptions.properties.plugins[field] = options
962 if handler then
963 for k,v in pairs(handler) do
964 StateHandler[k] = v
965 end
966 end
967 end
968
969
970 function module:GetBarOptions(bar)
904 local private = { } 971 local private = { }
905 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") 972 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
906 local options = { 973 local options = {
907 name = L["Dynamic State"], 974 name = L["Dynamic State"],
908 type = "group", 975 type = "group",
952 } 1019 }
953 } 1020 }
954 } 1021 }
955 } 1022 }
956 } 1023 }
957 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") 1024 for name, config in pairs(states) do
958 if states then 1025 options.args[name] = CreateStateOptions(bar,name)
959 for name, config in pairs(states) do
960 options.args[name] = CreateStateOptions(bar,name)
961 end
962 end 1026 end
963 optionMap[bar] = options 1027 optionMap[bar] = options
964 return options 1028 return options
965 end 1029 end
966 end 1030 end
967 1031
968 function module:GetBarOptions(bar) 1032 -- Module API --
969 return CreateBarOptions(bar) 1033
970 end 1034 -- Pass in a property field-name, an implementation function, a static options table, and an
1035 -- optional options handler method-table
1036 --
1037 -- propertyImplFunc prototype:
1038 -- propertyImplFunc( bar, stateTable )
1039 -- where stateTable is a { ["statename"] = { state config } } table.
1040 --
1041 -- The options table is static, i.e. not bar-specific and should only reference handler method
1042 -- strings (either existing ones or those added via optHandler). The existing options are ordered
1043 -- 91-100. Order #1 is reserved for the heading.
1044 --
1045 -- The contents of optHandler, if provided, will be added to the existing StateHandler metatable.
1046 -- See above, for existing API. In particular see the properties set up in the New method: self.bar,
1047 -- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp().
1048 --
1049 function module:RegisterStateProperty( field, propertyImplFunc, options, optHandler )
1050 RegisterProperty(field, propertyImplFunc)
1051 RegisterPropertyOptions(field, options, optHandler)
1052 end
1053