Scripting:InfoBar Links

From STNE Wiki

Jump to: navigation, search

Understanding InfoBar Links

IMPORTANT NOTICE: The CHtmlControl object, as of this time, is broke, and will display extra text most times that it is used (e.g you get < class="" title="" id=""> in front of the desired output). For this reason, if at all possible use CHtmlDiv or CHtmlSpan objects instead. These objects can still be passed in and out as CHtmlControl, and there are no serious drawbacks to using specific types in most circumstances. In the case that you get a CHtmlControl object from the system, it looks like it terminates correctly, so you can use the assignment shown in this guide without any adverse affects.

The STNE engine allows for extensions accessible for the 'InfoBar', an area at the top of the UI (user interface) primary pane. The intent it seems is to allow extensibility to the UI, and allow a certain level of customizations. These could include notes, coordinates, a list alliance members, etc. The InfoBar itself you have probably seen, it is the area where [You have a new message] appears.

To understand how to create an InfoBar application, one must first understand how it actually manages to get to the UI. This is done by CallBack methods. A callback is similar to system signal handlers in concept. When you tell the script to 'hook' a callback, it is added to a list of code to process every time that callback happens (with the exception that unlike a system signal, it can be pre filtered). A script can only hook a specific callback once, so if you need to have 3 individual methods happen for a particular callback event, you add them to a single function and call them all from there (In a normal driver or signal handler, you would not call the handler from a class, and this is good methodology to follow even with scripts) The following is an example of callback handling

Function RegisterHooks() {
  ScriptContext.EnableExtension();
  ScriptContext.RegisterEvent(EGuiEventType.InfoBarAfterCreate, AddressOf CB_InfoBarAfterCreate);
  ScriptContext.ActivateEvents();
}

Function CB_InfoBarAfterCreate(CBEvent As CGuiEventOnInfoBarAfterCreate) {
  // processing for event goes here, create the link, the output, etc.
  Var LinkItem As String = "my_infobar_app";
  Var Div As CHtmlDiv;
  InfoBarLink(CBEvent, LinkItem);
  Div = InfoBarDiv(CBEvent, LinkItem);
  Div.Style.Add('display', 'none'); 
  Div.Add("Hello, World!");
}

RegisterHooks();

RegisterHooks is a function called from the main body of the script, and the function itself handles adding the entry point onto the callback stack. Firstly, it sets up for being an extension via EnableExtensions. Next, it adds a handler to the callback stack, giving the code reference via AddressOf. Lastly, it lets the script engine know it's ready to handle events. Normally you will have any validation (checking data storage, initializing variables, etc) happening between EnableExtension and RegisterEvent. This function is called only at script initialization, which should be when you click on “Execute Script”, but experience tells me it gets called occasionally in other circumstances (the script seems to like to run from the main body after an STNE restart).

Once you have the callback hooked, you need the InfoBar link. For an InfoBar application to work, it needs two more components, other than the output. The first is a clickable link item, which usually contains the name of the script. The other is an area upon which to place your actual output. While the output can be appended from a different callback method, this is not the usual case. Firstly, set up the link:

Function InfoBarLink(CBEvent As CGuiEventOnInfoBarAfterCreate, LinkItem As String) {
  Var InfoBarAlignRight As Boolean = True;
  Var UrlCon As CHtmlControl;
  Var LinkText As String = "My InfoBar App";
  // Create the app bar link
  Var Url As CJsAction = CUrlBuilder.JsToggleElement(LinkItem);
  UrlCon = CControlBuilder.BracketLink(Url, LinkText);
  Var UrlSmall As New CHtmlSmall();
  UrlSmall.Add(UrlCon)
  CBEvent.CreateItem(InfoBarAlignRight).Add(UrlSmall.GuiControl);
}

Alternatively, you can use an HTML div to do the same job, as it can hold a CHtmlControl:

Function InfoBarLink(CBEvent As CGuiEventOnInfoBarAfterCreate, LinkItem As String) {
  Var InfoBarAlignRight As Boolean = True;
  Var UrlCon As New CHtmlDiv(); // Div instead of control, and instiated
  Var LinkText As String = "My InfoBar App";
  // Create the app bar link
  Var Url As CJsAction = CUrlBuilder.JsToggleElement(LinkItem);
  UrlCon.Add(CControlBuilder.BracketLink(Url, LinkText)); // Added to div rather than assigned to a control
  Var UrlSmall As New CHtmlSmall();
  UrlSmall.Add(UrlCon)
  CBEvent.CreateItem(InfoBarAlignRight).Add(UrlSmall.GuiControl);
}


This function simply sets up a toggle-able link on the InfoBar... It does not actually display anything other than the link, and clicking on it will do nothing, because no data is attached to it. The first line declares the function, and expects the first passed parameter to be of type CGuiEventOnInfoBarAfterCreate, which is itself a child of the class CGuiEvent. CGuiEvent could be used instead, if this handler was to process multiple callbacks, but you loose accessibility to anything specific to the child (unless you cast it back to it's particular type). The declaration of InfoBarAlignRight is for clarity. If it was false, it would align on the left. LinkText is literally the text the link will have. JsToggleElement is how the UI adds the javascript code to toggle the output (a div tag) between visible and hidden. BracketLink adds [ and ] around the text. CHtmlSmall applies the small HTML tag to the link. The CreateItem is how the script adds the newly created link into the UI, on the InfoBar.

Now that we have the link, we need to add the output area for the link. This is done by creating a layered div tag. Why layered? So the output stays precisely where you want it, and it actually hides when necessary.

Function InfoBarDiv(CBEvent As CGuiEventOnInfoBarAfterCreate, LinkItem As String) As CHtmlDiv {
  Var BorderColor As String = "#16344E";
  Var OuterDiv As New CHtmlDiv();
  Var Div As New CHtmlDiv();
  
  CBEvent.Page.Body.Controls.Insert(0, OuterDiv.GuiControl);
  OuterDiv.Style.Add('position', 'absolute');
  OuterDiv.Width = '99%';
  OuterDiv.Style.Add('z-index', '255'); 
  
  OuterDiv.Add(Div);
  Div.Style.Add('position', 'absolute');
  Div.Style.Add('top', '0');
  Div.Style.Add('right', '0');
  Div.Style.Add('z-index', '255');
  Div.Style.Add('background-color', 'black');
  Div.Style.Add('border-style', 'solid');
  Div.Style.Add('border-color', BorderColor);
  Div.Style.Add('border-width', '1px');
  Div.ID = LinkItem; 
  Return Div;
}

Usually the InfoBar creation and the div creation are in the same function or method, but for purposes of example I have split them into two functions. The first part is the setup and assignment. LinkItem is the name of the inner div, and it is usually similar to the link name. Keep in mind that if you create two scripts and name the div the same, they will both appear and hide when either script link is toggled, so you want the inner div name to be unique. The middle of the function sets up the inner div output area. The width is 1% short of maximum, because some browsers (notably Opera) tend to cut the right border off if it is 100%. The Z-Index ensures it's appears above normal output. The last part of the function sets up the inner div, with a black background and the standard STNE blue border 1 pixel wide. The alignment for the output is top right of the outer div, which is created just below the InfoBar. The output is adjusted to the smallest possible area that still displays the output. The following is the entire script:

Now, you may ask yourself, why the z-index assignment? On the inner div, it is for completeness more than anything else, since there is only one inner div. If there were multiple divs, you would be telling the parser who lays over whom, or who to stack next to who. For the outer div, it's a little more particular, since any div with the same z index will not overlay another of the same index (it displays as well as it can next to or around), and will stack over/under other levels of z index.

#UseInterface Web, Gui;

Function InfoBarLink(CBEvent As CGuiEventOnInfoBarAfterCreate, LinkItem As String) {
  Var InfoBarAlignRight As Boolean = True;
  Var UrlCon As CHtmlControl;
  Var LinkText As String = "My InfoBar App";
  // Create the app bar link
  Var Url As CJsAction = CUrlBuilder.JsToggleElement(LinkItem);
  UrlCon = CControlBuilder.BracketLink(Url, LinkText);
  Var UrlSmall As New CHtmlSmall();
  UrlSmall.Add(UrlCon)
  CBEvent.CreateItem(InfoBarAlignRight).Add(UrlSmall.GuiControl);
}

Function InfoBarDiv(CBEvent As CGuiEventOnInfoBarAfterCreate, LinkItem As String) As CHtmlDiv {
  Var BorderColor As String = "#16344E";
  Var OuterDiv As New CHtmlDiv();
  Var Div As New CHtmlDiv();
  
  CBEvent.Page.Body.Controls.Insert(0, OuterDiv.GuiControl);
  OuterDiv.Style.Add('position', 'absolute');
  OuterDiv.Width = '99%';
  OuterDiv.Style.Add('z-index', '255'); 
  
  OuterDiv.Add(Div);
  Div.Style.Add('position', 'absolute');
  Div.Style.Add('top', '0');
  Div.Style.Add('right', '0');
  Div.Style.Add('z-index', '255');
  Div.Style.Add('background-color', 'black');
  Div.Style.Add('border-style', 'solid');
  Div.Style.Add('border-color', BorderColor);
  Div.Style.Add('border-width', '1px');
  Div.ID = LinkItem; 
  Return Div;
}


Function RegisterHooks() {
  ScriptContext.EnableExtension();
  ScriptContext.RegisterEvent(EGuiEventType.InfoBarAfterCreate, AddressOf CB_InfoBarAfterCreate);
  ScriptContext.ActivateEvents();
}

Function CB_InfoBarAfterCreate(CBEvent As CGuiEventOnInfoBarAfterCreate) {
  // processing for event goes here, create the link, the output, etc.
  Var LinkItem As String = "my_infobar_app";
  Var Div As CHtmlDiv;
  InfoBarLink(CBEvent, LinkItem);
  Div = InfoBarDiv(CBEvent, LinkItem);
  Div.Style.Add('display', 'none'); // See notes below
  Div.Add("Hello, World!");
}

RegisterHooks();

Normally, at the Div.Add in CB_ InfoBarAfterCreate, you would add your output table or div instead of a text string. Also, you would toggle the output with a URL Parameter, with something like this in CB_ InfoBarAfterCreate:

  Var ShowDiv As Boolean = False;
  // option processing here, usually returning the status of ShowDiv, like Showdiv = ProcessOptions()
  // … table/div output creation, like Div.Add(function());
  If (ShowDiv) {
    Div.Style.Add('display', 'block');
  } Else {
    Div.Style.Add('display', 'none');
  }

But URL/Form parameter passing and processing is for another tutorial
--Miltiades (En-1:56599) 06:19, 9 October 2011 (CEST)

Personal tools