
TjOptions v0.42
========================================

Author: Tuhljin

Library for easy creation and management of Interface Options panels. TjOptions provides built-in support for a
few variations on some basic item types (checkboxes, labels) and an extensible, modular system for supporting
other items.

Contents:

PANEL CREATION API

POST-CREATION API

PANEL CONTENTS TABLE SPECIFICATIONS
- Variables Table

REGISTERING ADDITIONAL PANEL ITEM TYPES
 - Item Functions Table
 - Item Type Inheritance
 - Iterator


=== PANEL CREATION API ===

panel, oldver = TjOptions.CreatePanel(name, parent, [isDefaultInGroup,] tab)
  Creates a new Interface Options panel. The contents of the panel will not be created immediately, instead
  being dynamically created if and when it is necessary.
  Arguments:
    name     String. The panel's name, being the text that is used to indicate this panel in the Interface
             Options frame.
    parent   String or nil. The name of the panel which will be the new panel's parent, or nil for no parent.
    isDefaultInGroup
             Any. If it evaluates to true and a parent created by TjOptions.CreatePanelGroup() is specified, this
             panel will be shown when the parent panel is selected.
    tab      Table. The panel's contents, using the specifications indicated in the "PANEL CONTENTS TABLE
             SPECIFICATIONS" section, below.
  Returns:
    panel    Table. The new panel (frame).
    oldver   Any. If the variables key is used in the panel contents table, this will be the value of the
             "Version" key from the variables table unless TjOptions created the table (see the "Variables Table"
             section, below), in which case it will be false. The use of this return is of course entirely
             optional: Your variables table is not required to include a Version key.

panel = TjOptions.CreatePanelGroup(name, parent[, isDefaultInGroup[, sound]])
  Creates a new Interface Options panel that acts solely as a group header for other panels. If this panel is
  selected, one of its children (if it has any) will be displayed.
  Arguments:
    name, parent, isDefaultInGroup
             As TjOptions.CreatePanel()'s arguments.
    sound    String or nil. A sound file to play when the panel is selected and one of its children is shown. Use
             nil to play no sound, or an empty string ("") to use the suggested sound ("igCharacterInfoTab").
  Returns:
    panel    Table. The new panel (frame).


=== POST-CREATION API ===

In the following, myPanelGroup is the table returned by a TjOptions.CreatePanelGroup() call while myPanel is that
returned by TjOptions.CreatePanel().

myPanelGroup:SetDefaultPanel("PanelName")  -OR-
myPanelGroup:SetDefaultPanel(PanelFrame)
  Change the panel which will be shown when myPanelGroup is selected. The argument is either a string giving the
  name of the panel or the panel frame itself.

myPanelGroup:SetSound("SoundFile")
  Change the sound file associated with this panel, as per the sound argument of TjOptions.CreatePanelGroup().

myPanel:LoadVariables()
  Cause all items on the panel to reflect the saved variables they represent (checking/unchecking appropriate
  checkboxes, etc.). This is called automatically when the panel is shown or set to default settings.

changesInProgress, canceling, defaulting, loading = myPanel:IsChangeInProgress()
  Returns true if the panel is currently in the process of adjusting its items' settings, or nil otherwise.
  Also returns 3 other values, true or nil, based on which changes are under way:
    canceling    True if panel is reverting to previous settings, as occurs when the player hits the Cancel
                 button to close the Interface Options frame (if myPanel was shown since the frame was opened).
    defaulting   True if panel is reverting to default settings.
    loading      True if panel is in the middle of a LoadVariables() call.

 Most addons will have no need to use this function, but it can be useful to check when your addon needs to react
 differently depending on what TjOptions is doing, perhaps in order to prevent a loop that might occur because,
 e.g., you are forcing your panel's items to change their current values in response to TjOptions telling your
 addon that a variable was changed.

 The place where such a loop is most likely to occur is in an OnChange function (see table specifications,
 below); however, those functions are generally better off utilizing the fourth argument that TjOptions passes to
 them to prevent loops, but if, for some reason, knowing exactly what sort of change is taking place is needed,
 this function is available.


=== PANEL CONTENTS TABLE SPECIFICATIONS ===

The table used as the fourth (or third, if isDefaultInGroup is omitted) argument of a TjOptions.CreatePanel()
call should use the following format. In the following, myPanel is the table returned by a
TjOptions.CreatePanel() call.

Baseline Fields:
  All fields are optional except "items."

  title         String. Text of the large label used at the top of the panel. (The resulting label can be
                accessed at myPanel.titleLabel.) No label is created if this is nil.

  titleCenter   Boolean. If true, then the top label will be centered.

  items         Table. The items, such as checkboxes, used on this panel. See "Item Fields," below.

  itemspacing   Integer. The size of the empty buffer space between items. A default value is used if nil.

  variables     Table or string. The variables that will be altered when the player manipulates the panel's items.
                See "Variables Table," below. If this key is used, you will most likely want to make sure that the
                call to TjOpions.CreatePanel() occurs after your addon's saved variables have been initialized by
                WoW.

  defaults      Table. Settings for the variables to be used when the player sets this panels' settings to the
                default. See "Variables Table," below.

  OnChange      Function. The given function will be called when TjOptions may have changed the value of any of
                the panel's items' saved variables. It is called once for each item that may have changed and is
                passed the item's frame as the first argument, the key of the associated variable (i.e.
                "DoSomething") as the second one, and the value of that variable as the third. The fourth
                argument will be true if the item was changed directly by the player or false if the change was
                the result of something else, like the Cancel button. Also see the OnChange item field, below.

  OnShow        Function. Called when the panel is shown, with the panel as its first argument. The function is
                called after TjOptions handles its standard OnShow functionality, including LoadVariables().

  OnBuild       Function. Called when the panel's contents are built, with the panel as its first argument. This
                is called only once, either when the panel is shown for the first time (before the given OnShow
                function is called) or when the player sets all interface options to the default. At the time of
                this call, the panel's contents (checkboxes, etc.) have been created but LoadVariables() hasn't
                been called so their settings may be wrong. LoadVariables() is called after the OnBuild function,
                so you could change the values of your variables at this time so the new values will be used
                (except they will still be set to their defaults, if any, if that's what triggered this).

  OnOkay        Function. Called when the player clicks the interface options frame's Okay button if this panel
                was shown or set to default settings at some point since the frame was last shown.

  OnCancel      Function. Called when the player clicks the interface options frame's Cancel button if this panel
                was shown or set to default settings at some point since the frame was last shown. It is called
                after the panel's items have already been reverted to their previous state.

  OnDefault     Function. Called when the panel's options are set to the default.

  scrolling     Any. If it evaluates to true, the panel's items will be placed inside a scolling frame. If this
                is a string, it will be the scroll frame's global variable name.

  column?Offset (where "?" is the column number)
    Number. The base x-position of items in that column. Also see "column" in "Item Fields," below.

  tooltipWrap   Boolean or number. This is checked when an item's tooltip field is a string and that item's
                tooltipWrap field is omitted. See tooltipWrap under "Item Fields," below.

Item Fields - general (for all items):
  All general item fields are optional.

  type          String. The identifier for the desired type of item. Can refer to any item type registered with
                TjOptions. Types included by default are:
                  "checkbox"        A checkbox. This is the default if type isn't specified.
                  "checkboxsmall"   A smaller checkbox.
                  "checkboxnolabel" A checkbox with no text label.
                  "label"           A text label.
                  "labelwhite"      A white text label.
                  "labelwrap"       As "label" but with default values for justifyH and width set to allow for
                                    wordwrapping. The width is safe for options panels where scrolling is used.
                  "labelwrapwhite"  As "labelwrap" but using a white text label.
                  "labelnorm"       Alias for "label" (for backward compatibility).
                  "dropdown"        A dropdown menu. Requires the TjDropDownMenu library. See note below.

                Also see "SUPPORTING ADDITIONAL PANEL ITEM TYPES."

                Note: The "dropdown" type is provided for backward compatibility purposes and may be removed
                from this list in the future. Versions 0.51 and later of TjDropDownMenu use the item type
                registration methods now provided by TjOptions, so registering it by default is unnecessary
                unless the player is using an older version of that library.

  variable      String. The key of the variable this item will manipulate. For example, if the type is "checkbox"
                and this field is "DoSomething," when the checkbox is clicked, the "DoSomething" entry in the
                "variables" table (from the baseline field) is set to true if checked or false otherwise.

  text          String. Text of the label displayed alongside the item, if applicable.

  name          String. The name of the global variable to give the new object. Use this if your addon needs
                access to the object, but remember that it won't actually be created until it is needed.

  tooltip       String or function. If a string, tooltip text to display. If a function, it is called after
                the tooltip's owner is set and is ready to have text added to it. The item's frame is passed as
                the first argument and the tooltip object is passed as the second argument. Your function doesn't
                need to show the tooltip ("GameTooltip:Show()") since that will be done for you by TjOptions.
                Some item types, such as labels, do not use this field or tooltip2.

  tooltip2      String. Additional tooltip text (uses a smaller font). Used only if "tooltip" field is a string.

  tooltipWrap   Boolean or number. If tooltip is a string, this determines whether the tooltip will automatically
                be wordwrapped. If this is omitted, then the baseline tooltipWrap field is checked. If both are
                omitted, wordwrapping will be applied.

  xOffset       Integer. Offset the x position of the item by this amount. Column-1 items following it will also
                be offset by this amount until another is specified. (Set to 0 to end previous offsets.) If the
                current item is in a column other than 1, this offset instead applies only to the current item.

  topBuffer     Integer. Additional space to add between this item and the previous one, or fewer if negative.

  btmBuffer     Integer. Additional space to add between this item and the next one, or fewer if negative.

  OnChange      Function. If this is defined, it will override the panel's OnChange field where this specific
                item is concerned. If desired, you can set this to something other than a function (except for
                nil or false) to prevent the panel's OnChange function from being called without calling a
                different function.

  column        Integer. The column this item will be placed in. Defaults to 1. If in a column other than 1, then
                its y-position will be based on that of the last column-1 item. Also see the "column?Offset"
                baseline field in the "PANEL CONTENTS TABLE" section, above.

Item Fields - label or labelwhite:
  Three additional optional fields are available for labels.

  font          String. The font to use, such as "GameFontNormalLeftRed". If omitted, "GameFontNormal" is used
                for normal "label" items and "GameFontHighlight" is used for "labelwhite" items.

  width         Integer. The width of the label. Use this to allow automatic wordwrapping.

  justifyH      String. The horizontal justification of the text (such as "LEFT").

Item Fields - checkbox, checkboxsmall, or checkboxnolabel:
  One additional optional field is available for checkboxes.

  width         Integer. Used to expand the checkbox's hit rectangle to the right. For example, using 100 means
                the hit rectangle will be expanded 100 pixels (toward its text label, if it has one). If omitted,
                the width of the its text label is used, if it has one.

Item Fields - dropdown:
  Three additional fields are available for dropdown menu items. The menu field is required.

  menu          Table. Used as the third argument in TjDropDownMenu.CreateDropDown(). See TjDropDownMenu.lua.
                If you use the tooltip item fields, they will be used instead of those found in this table when
                the cursor is over the dropdown frame. Set tooltip to 0 show no tooltip. (The menu table's fields
                will still be used for the dropped-down list itself.)

  width         Integer. The width of the primary portion of the dropdown frame.

  displayMode   String. The display mode of the dropdown menu. Usually left nil or set to "MENU".

Variables Table
---------------

This table is intended to be used by your addon to determine the addon's options/configuration. It will probably
be the same table you declare as a saved variable in the addon's TOC file. The keys used should correspond to
those indicated by the "variable" field of the entries in the "items" table.

A simple example function call using such a table follows:

  MyAddon_Settings_Default = { Enabled = true, VerboseMode = false };
  local panel = TjOptions.CreatePanel("My AddOn", nil, {
	title = "My Addon",
	items = {
		 { type = "checkbox", variable = "Enabled", text = "Enable Stuff" },
  		 { type = "checkboxsmall", variable = "VerboseMode", text = "Verbose Mode",
  		   tooltip = "Increase verbosity", tooltip2 = "Makes things excessively wordy."  }
	},
	variables = MyAddon_Settings,
	defaults = MyAddon_Settings_Default
  });

In this example, when the player clicks the checkbox with the label "Enable Stuff," the variable
MyAddon_Settings["Enabled"] will be set to true if it is checked or false if it is unchecked. If the player uses
the "Defaults" button of the Interface Options frame to set your addon's settings to the default, the "Enable
Stuff" checkbox would become checked and the "Verbose Mode" checkbox would become unchecked.

Using a String Instead of a Table:

The variables key of your panel contents table should be declared either as a table or a string, if it is used.
If a string, when TjOptions.CreatePanel() is called, it will check the global table (_G) to find a table with
that global name. If one is found, the variables key in the contents table will be changed to point to it.
Otherwise, an empty table with the given global name is created to take the place of the string. If the panel
contents table also includes a defaults key that points to a table, the keys and values of the defaults table are
copied to the new variables table. This is useful as a way to simplify common saved variables management tasks
for your addon.

Also see the oldver return value of TjOptions.CreatePanel(), which can be used to further manipulate the variables
table should changes be necessary based on the table's previous Version value. Note that TjOptions does not set
the Version key for you; the way it is used in your addon is entirely up to you.


=== REGISTERING ADDITIONAL PANEL ITEM TYPES ===

For an example of item type registration in use, see the built-in registration near the end of TjOptions.lua or
look at the TjDropDownMenu library (version 0.51 or later).

registered = TjOptions.RegisterItemType(itemType, revision, [inherit,] funcList[, updateFunc])
  Registers a new item type for TjOptions panels.
  Arguments:
    itemType    String. A unique identifier for this type of item. It is suggested that all-lowercase generic
                identifiers ("checkbox", "editbox", "slider" and the like) be reserved for the use of the
                author(s) of TjOptions to allow forward compatibility.
    revision    Number. The revision number of the code that runs this item type.
                Only the highest-numbered revision given to TjOptions will be used.
    inherit     String or nil. If a string, it specifies the previously-registered item type to use as a template
                for this new item type. See "Item Type Inheritance" below for details on what this means.
    funcList    Table or function. The functions used by TjOptions to interact with this item type. See "Item
                Functions Table," below. Using a function for this argument is essentially shorthand for
                "{ create = myFunc }" (without quotes), where myFunc is the given argument.
    updateFunc  Function or nil. If provided and an older revision of the item type was registered, the given
                function will be called once for each instance of the item that has actually been "built," if
                any. Note that an item listed in an items table is not necessary a built item: Built items are
                those whose associated frames have actually been created and (most often) displayed to the
                player. The intent is that this function can be used to make previously-created items compatible
                with the new revision if changes are necessary.

                The function is called with three arguments, in this order:
                  frame     Table. The item instance's frame.
                  oldrev    Number. The old revision number.
                  data      Table. This instance's "item table." This is the relevant portion of the table used
                            in the TjOptions.CreatePanel call, e.g.:
                            { type = "checkbox", variable = "Enabled", text = "Enable Stuff" }
  Returns:
    registered  Boolean. Returns true if the item type was successfully registered. Returns false if the same
                or a newer revision was already registered.

success = TjOptions.SetItemTypeAlias(alias, itemType)
  Sets the alias (string) to point to the given item type (string). For example, "labelnorm" is an alias for
  "label". Whenever methods involving the item type "labelnorm" are requested, the methods for "label" are run
  instead. Returns true if the alias was successfully set.

TjOptions.ItemChanged( [noUser,] itemFrame[, value] )
  Registered items are responsible for letting TjOptions know when the user has done something to change their
  current settings (e.g., unchecked a checkbox). To do so, they should call this function where appropriate (such
  as in their OnClick handlers).
  Arguments:
    noUser      Boolean. If true, then this change isn't considered to have been triggered directly by the player
                (meaning the item instance's OnChange function, if defined, will be passed false as the fourth
                argument). If this argument is used, it must be true or false; nil will not work.
    itemFrame   Table. The item instance in question (i.e., the checkbox frame).
    value       Any. The new value to which the proper variable should be set. If omitted, TjOptions will use the
                item type's getvalue function ("Item Functions Table" below) to get this value.
                IMPORTANT NOTE: Unlike many other functions with optional arguments, this one checks whether the
                argument was actually omitted or if it is simply nil, since nil is a valid value for its use.
                However, when given nil, the actual saved value becomes false.

revision = TjOptions.GetItemTypeRevision(itemType)
  Returns the revision number of a registered item type, or nil if none is registered by that identifier.

Item Functions Table
--------------------

The item functions table as provided by the funcList argument of TjOptions.RegisterItemType gives TjOptions the
functions needed to interact with the registered item type. The possible fields for use are as follows. All
fields except for create are optional.

create        Function. The function that creates a new instance of the item. It will be called like so:
                frame, handletip, xOffset, yOffset, btmBuffer, focusable = create(name, parent, data, arg)
                Arguments:
                name        String. The global name that should be used by the new frame (or the primary frame,
                            if more than one is created).
                parent      Table. The frame that should be the new frame's parent.
                data        Table. This instance's "item table." This is the relevant portion of the table used
                            in the TjOptions.CreatePanel call, e.g.:
                            { type = "checkbox", variable = "Enabled", text = "Enable Stuff" }
                arg         Any. See create_arg.
              The function should return values like so:
                frame       Table or false. The primary frame of the newly created item instance. This frame must
                            support the SetPoint method. Return false instead to indicate the item was not
                            created.
                handletip   Any. If it evaluates to true, then TjOptions sets the frame's OnEnter and OnHide
                            scripts to the standard used by certain built-in item types, like checkbox, in order
                            to handle tooltips. Otherwise, it is the responsibility of the new item to handle the
                            display of tooltips.
                xOffset, yOffset
                            Integer or nil. Adjustments used for the positioning of the frame.
                btmBuffer   Integer. Additional space to add between this item and the next one, or fewer if
                            negative.
                focusable   Any. If it evaluates to true, then it is added to the list of its parent panel's
                            focusable items. This allows the player to cycle between items on the list by hitting
                            tab and shift+tab. This also causes TjOptions to clear the focus on the item when Okay
                            or Cancel is pressed, which is a necessity for properly handling certain items that
                            react to the focus being lost (which would otherwise lose the focus when the options
                            panel closes, after Okay/Cancel handling, which can cause problems). The item must
                            support the <item>:SetFocus(), <item>:ClearFocus() and <item>:HasFocus() methods.

getvalue      Function. Required for any item that may have a variable associated with it. (Those that never have
              a variable associated with them include labels.) It is called like so:
                value = getvalue(self, data, arg)
                Arguments:
                self        Table. The item instance itself (e.g., the checkbox frame).
                data        Table. This instance's item table.
                arg         Any. See getvalue_arg.
              The function should return the instance's current value as it should be saved in the variables. For
              example, a checked checkbox returns true while an unchecked checkbox returns false.

setvalue      Function. Required for any item that may have a variable associated with it. It is called like so:
                setvalue(self, value, data, arg)
                Arguments:
                self        Table. The item instance itself.
                value       Any. The value of the variable associated with this item instance.
                data        Table. This instance's item table.
                arg         Any. See setvalue_arg.
              The function doesn't need to return anything. When called, it should do what is necessary to cause
              the item instance to reflect the value given. For example, a checkbox given the value true would
              become checked while one given the value false would become unchecked.

create_arg    Any. Whenever an item instance of this type is created, the fourth argument of the create function
              call will be whatever is in this field.

getvalue_arg  Any. Whenever an item instance of this type is issued a getvalue call, the third argument of the
              call will be whatever is in this field.

setvalue_arg  Any. Whenever an item instance of this type is issued a setvalue call, the fourth argument of the
              call will be whatever is in this field.

create_prehook, getvalue_prehook, setvalue_prehook
  Function. If provided, then this function will be called before the function it "hooks" (if that other function
  is also defined by this item type or is inherited). It is given the same arguments that will be passed to the
  original function. If the prehook function's first return value is false (not nil), then the original function
  will not be called. If the first return value is true, then whatever else it returns will be passed to the
  original function as arguments instead of the original arguments (though the same number of arguments are always
  used even if the prehook returns more).

create_posthook, getvalue_posthook, setvalue_posthook
  Function. If provided, then this function will be called after the function it "hooks" (if that other function
  is also defined by this item type or is inherited). It is passed whatever the original function returned (that
  is, return values that were expected: extraneous return values are ignored) followed by the same arguments that
  were passed to the original function, as noted below for reference:

    create_posthook(frame, handletip, xOffset, yOffset, btmBuffer, name, parent, data, arg)
    getvalue_posthook(value, self, data, arg)
    setvalue_posthook(self, value, data, arg)

  If the posthook's first return value is true, then whatever else it returns will be used as if the original
  function returned those values. Otherwise, the original function's returns are used.

  Tip: Since the posthook is passed the original function's return values first and TjOptions ignores extraneous
  return values, something like this is a valid way to utilize a create_posthook:

    local function MyItem_create_posthook(frame, handletip, ...)
      return true, frame, false, ...  -- force handletip return to become false
    end

Item Type Inheritance
---------------------

When being registered, an item type can be set to "inherit" properties from another, previously-registered item
type (the "template"). This means that whenever a create, getvalue, or setvalue method is to be called but that
entry is not present in the new type's table, if it is present in the template's table, the template's function
is called instead. If the template also doesn't have this method and it, too, inherited a template, the
template's template is checked, then the template's template's template if it has one, and so on.

TjOptions also uses the inheritance tree to determine what value to use for the *_arg argument passed to the
function (*_arg being the create_arg, getvalue_arg, or setvalue_arg, depending on the function in question).
If the base item type's *_arg property is nil, the first non-nil value found in the inheritance tree is used,
starting by looking at the base item type's template, then the template's template, etc. However, unlike the
function-seeking checks, this one will stop after checking the template which ultimately yielded the function
that is to be called.

For example, imagine you create an item type "MyCheckbox" that inherits from "checkboxsmall", which in turn
inherits from "checkbox". "checkbox" defines the create, getvalue, and setvalue properties (and no others).
"checkboxsmall" defines the create_arg property (and no others). When a "checkboxsmall" item is created, it uses
the create method defined by "checkbox" but it passes its own create_arg argument to it. If "MyCheckbox" doesn't
define a create method or a create_arg property, then when it is created, it will use "checkbox"'s method and
"checkboxsmall"'s create_arg. However, if "MyCheckbox" does define a create method, it will use its own function
and it will NOT use "checkboxsmall"'s create_arg because that is found further down the inheritance tree than
the function is. Likewise, in this situation, if another item type called "ColorfulCheckbox" inherits from
"MyCheckbox" and it defines neither create nor create_arg, it would use "MyCheckbox"'s create method and use
nil for create_arg.

The same principle as that used for finding the relevant *_arg argument applies to finding which *_prehook and
*_posthook functions apply to a method call. If, in the previous example, "MyCheckbox" did not define a create
method but instead defined a create_posthook function, then when "ColorfulCheckbox" is created, "checkbox"'s
create method would be used (since no create method is found in the inheritance tree until that point),
"checkboxsmall"'s create_arg would be passed to it, and "MyCheckbox"'s create_posthook would be used. However,
if "ColorfulCheckbox" defined its own create method, then it essentially doesn't inherit anything as far as
create is concerned (though it may inherit setvalue- and/or getvalue-related properties).

Iterator
--------

An ipairs()-like iterator to iterate over all built items of a given type is available:
TjOptions.BuiltItemsIterator(itemType).

Example usage:
  for i,obj in TjOptions.BuiltItemsIterator("label") do
    print(obj:GetText())
  end


Recent changes
================

v0.42
- Adjusted the size of the scrolling area to better utilize the space provided by the updated Interface Options UI.

v0.41
- TjOptions now supports the use of a single variables table for multiple options "pages" and other miscellaneous
  use. Previously, if other entries were contained in the table, undesired results could occur when one page's
  settings were set to the default or its settings were reverted (e.g., due to the Cancel button being clicked),
  etc., since the entire table would be examined instead of just those keys that correspond to items on the
  relevant page.

v0.40
- Now supports columns to align items to the right of a previous item, using the "column" key in item tables and
  "column?Offset" keys in the panel contents table (where "?" is the column number).
- Checkboxes' hit rectangles are now assigned based on the "width" item field or, if that is omitted, the width of
  their text labels at creation.
- Added the Focus/Tab system. Items can use the new "focusable" return from their create functions to be added to
  a list of items that the player can cycle between using tab and shift+tab. (See details in the description of
  the "focusable" return in TjOptions.txt)
- Added the "checkboxnolabel" item type.
- Added the "tooltipWrap" baseline field and standard item field.
- Added the "titleCenter", "OnOkay", and "OnCancel" baseline fields.
- Background added to scrolling frames' scroll bars.

v0.34
- Added functionality intended to simplify common saved variables management tasks, including support for using a
  string for the panel contents table's variables key.
- Added oldver return to TjOptions.CreatePanel().
