Difference between revisions of "Tool Integration Development"
m (→Getting Started) |
(Replaced origo.ethz.ch by eiffel.com in SVN URL) |
||
(23 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:EiffelStudio]] | [[Category:EiffelStudio]] | ||
− | + | [[Category:Extending EiffelStudio]] | |
{{UnderConstruction}} | {{UnderConstruction}} | ||
− | + | Welcome to the the EiffelStudio tool integration development page. The content held within this page will show you how to develop and integrate a new tool into the EiffelStudio IDE. | |
− | Welcome to the the EiffelStudio tool integration development page. The content held within this page will show you how to develop and integrate | + | |
== Getting Started == | == Getting Started == | ||
− | + | Creating a new EiffelStudio tools is two fold; a tool descriptor/shim and a the dockable tool panel. | |
+ | |||
+ | The tool descriptor is used by the IDE to create command objects, menu items, tool bar buttons, connecting shortcut preferences and a number of other functions. The descriptor is used in place of the actual tool for these functions as to defer the creation of the actual tool until it is shown in then EiffelStudio IDE. | ||
+ | |||
+ | A tool descriptor does not just describe the tool, its type is used to through out parts of the EiffelStudio IDE to query and instantiate editions the tool is a dynamic fashion. The internals of this dynamic mechanism are explained in a later tutorial. | ||
+ | |||
+ | The dockable tool panel represents the actual dockable tool user interface. | ||
+ | |||
+ | This tutorial will only cover the basics of tool development, for more advanced tutorials see [[Tool Integration Development 2]] and [[Tool Integration Service Development]] and [[Services]]. | ||
== Requirements == | == Requirements == | ||
− | The content of this tutorial relates to the working version of EiffelStudio 6.1, current attainable from the open source repository https:// | + | The content of this tutorial relates to the working version of EiffelStudio 6.1, current attainable from the open source repository https://svn.eiffel.com/eiffelstudio/trunk/Src/Eiffel. Please see the pages on attain the EiffelStudio source code from the [[Repository]], as well as information on [[Compiling EiffelStudio]] and [[Debugging EiffelStudio]]. |
Once you have EiffelStudio compiled and in a state where it can be debugged then continue reading. | Once you have EiffelStudio compiled and in a state where it can be debugged then continue reading. | ||
+ | |||
+ | == Creating a Tool Descriptor == | ||
+ | Once the tool user interface panel has been created it needs to be represented by a descriptor. Tool descriptors are simple to implement and support more advanced features such as [[User Interface Memory Managment#Automatic Recycling|automatic recycling]] on closing and multiple-editions, which are explained in a later tutorial. | ||
+ | |||
+ | All tool descriptors are implement [[ES_TOOL]] taking a generic parameter representing the tool's user interface panel. Below shows the minimal implementation of <code>ES_CUSTOM_TOOL</code> to describe <code>ES_CUSTOM_TOOL_PANEL</code>. | ||
+ | |||
+ | <eiffel> | ||
+ | class | ||
+ | ES_CUSTOM_TOOL | ||
+ | |||
+ | inherit | ||
+ | ES_TOOL [ES_CUSTOM_TOOL_PANEL] | ||
+ | |||
+ | create {NONE} | ||
+ | default_create | ||
+ | |||
+ | feature -- Access | ||
+ | |||
+ | icon: EV_PIXEL_BUFFER | ||
+ | -- Tool icon. | ||
+ | do | ||
+ | Result := stock_pixmaps.tool_output_icon_buffer | ||
+ | end | ||
+ | |||
+ | icon_pixmap: EV_PIXMAP | ||
+ | -- Tool icon pixmap. | ||
+ | do | ||
+ | Result := stock_pixmaps.tool_output_icon | ||
+ | end | ||
+ | |||
+ | title: STRING_32 | ||
+ | -- Tool title. | ||
+ | do | ||
+ | Result := "Custom Tool" | ||
+ | end | ||
+ | |||
+ | shortcut_preference_name: STRING_32 | ||
+ | -- An optional shortcut preference name, for automatic preference binding. | ||
+ | do | ||
+ | end | ||
+ | |||
+ | feature {NONE} -- Factory | ||
+ | |||
+ | create_tool: ES_CUSTOM_TOOL_PANEL | ||
+ | -- Creates the tool for first use on the development `window' | ||
+ | do | ||
+ | create Result.make (window, Current) | ||
+ | end | ||
+ | |||
+ | end | ||
+ | </eiffel> | ||
+ | |||
+ | One of the most important function of the descriptor is <code>create_tool</code>. It's used to instantiate the tool's user interface panel, which will be requested when the tool is shown, either in full (that actual tool is visible) or in part (tab-docked but hidden beneath another tool) in place in the EiffelStudio IDE. | ||
+ | |||
+ | Both <code>icon</code> and <code>icon_pixmap</code> must both return a valid pixmap. In the example the icon for the '''Output Tool''' was reused for the purpose of demonstration. Creating a using custom pixmaps is explained [[Creating and Using Custom Pixmap Matrices|here]]. | ||
+ | |||
+ | The <code>title</code> found should return the tool's title as displayed in the tool's user interface when docked and tabbed or when the tool is opened as a undocked floating window. | ||
+ | |||
+ | Finally <code>shortcut_preference_name</code> is used to associate the tool with a shortcut preference, which is expanded upon in a later tutorial. | ||
+ | |||
+ | {{Outdated|This article will be outdated in the release of EiffelStudio 6.1}} | ||
+ | |||
== An Implemented Tool Reference == | == An Implemented Tool Reference == | ||
− | If at any time you need to reference an actually implementation of a tool using the information in these pages, see the implementation of <eiffel>ES_ERRORS_AND_WARNINGS_TOOL</eiffel> in the EiffelStudio code. | + | If at any time you need to reference an actually implementation of a tool using the information in these pages, see the implementation of <eiffel>ES_ERRORS_AND_WARNINGS_TOOL</eiffel> in the EiffelStudio code. It was one of the first tools developed to use the new tool development base and helper classes you'll used to develop EiffelStudio tools. |
== Creating a Tool == | == Creating a Tool == | ||
− | Before anything else can be done you need to create a tool. Most tools inside EiffelStudio utilized a class call <eiffel>EB_TOOL</eiffel> (as of 6.1.) <eiffel>EB_TOOL</eiffel> is | + | Before anything else can be done you need to create a new tool class. Most tools inside EiffelStudio utilized a base class call <eiffel>EB_TOOL</eiffel> (as of 6.1.) <eiffel>EB_TOOL</eiffel> is currently the most basic of tool classes and has become a little outdated. In 6.1 a new abstract base class call <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> was created for the purpose of creating EiffelStudio tools with a greater amount of ease. <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> still derives from <eiffel>EB_TOOL</eiffel> but this may change in the future if all tools are converted to use <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> instead. With that said you should not use <eiffel>EB_TOOL</eiffel> directly, it was only mentioned for those curious about the class hierarchy. |
− | So to be clear, only use <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> as your base for tool development. | + | {{Note|So to be clear, only use <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> as your base for tool development.}} |
− | <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> requires a generic parameter constrained to <eiffel>EV_WIDGET</eiffel>. The generic parameter, <eiffel>G</eiffel>, represents the "user widget". The user widget | + | The use of <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> requires a generic parameter constrained to <eiffel>EV_WIDGET</eiffel>. The generic parameter, <eiffel>G</eiffel>, represents the tool's "user widget". The user widget defined by the feature <eiffel>user_widget</eiffel> is the top level widget the tool should interact with. For instance, if the tool consists of just a grid, list or tree widget then the user control will be of the respective type. For more complex scenarios the generic parameter could just as easily be a composite widget or a simple container like <eiffel>EV_HORIZONTAL_BOX</eiffel> or <eiffel>EV_VERTICAL_BOX</eiffel>. |
− | To start out create a new Eiffel class and inherit <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel>, specifying the generic parameter for | + | To start out, create a new Eiffel class and inherit <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel>, specifying the generic parameter for the tool's user widget. |
− | <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> has many advantages to being a base class for all | + | {{Note|<eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> has many advantages to being a base class for all tools; simplified implementation, a small number of required to implement features and delayed initialization of the tool itself. This last aspect is important because the more tools put into EiffelStudio the more time it takes the IDE to start up. Using delayed initialization comes with a few gotchas that you may possibly run into but it's recommended that it is used, so not to slow down EiffelStudio.}} |
== Declaring the Tool's Creation Routine == | == Declaring the Tool's Creation Routine == | ||
− | <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> even comes complete with a basic creation routine, <eiffel>make</eiffel>, which should be passed the development window object the tool is created | + | <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> even comes complete with a basic creation routine, <eiffel>make</eiffel>, which should be passed the active development window object the tool is created with. EiffelStudio supports the display of mutli IDE windows and the passed object represents the window the tool is active within. If the tool has no further actions required to be performed on creation then simply specify your tool's creation routine as the one from <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> (which is actually from <eiffel>EB_TOOL</eiffel>.) On the other hand, if the tool has other creation initialization to perform either redefine <eiffel>make</eiffel> or specify an alternative creation routine. It is important that <eiffel>develop_window</eiffel> be set correctly for <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> to function correctly. |
− | + | Alternatively if simplicity is what you are after, simply redefine <eiffel>on_before_initialize</eiffel> to initialize any class attributes or other data. Please be aware that this routine is called during creation of the tool and affects the overall EiffelStudio start up time. | |
− | + | ||
− | Alternatively if simplicity is what you are after | + | |
== Implementing the Requirements == | == Implementing the Requirements == | ||
− | There | + | There are less than a hand full of <eiffel>deferred</eiffel> features from <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel> to implement for a tool to compile and execute in EiffelStudio. The following are the list of deferred features requiring implementation (as of 6.1): |
+ | |||
* <eiffel>build_tool_interface</eiffel> | * <eiffel>build_tool_interface</eiffel> | ||
* <eiffel>create_tool_bar_items</eiffel> | * <eiffel>create_tool_bar_items</eiffel> | ||
Line 42: | Line 110: | ||
* <eiffel>tool_title</eiffel> | * <eiffel>tool_title</eiffel> | ||
− | + | Each of these routines will be discussed, first the rudimentary features and then optional implementation for which empty routines can simply be defined. | |
=== <eiffel>tool_title</eiffel> === | === <eiffel>tool_title</eiffel> === | ||
− | Every tool requires | + | Every tool requires a title. The title is the text that is used when the tool is first created and will appear on the tool's tab button or tool's window title when undocked. It is also used when building a menu entry under ''View'' > ''Tools'' in the EiffelStudio IDE. Generally the title should be internationalized but internationalization details are discussed in a further tutorial. For now, it is good enough to simple return a attached non-empty Eiffel string. |
− | It should be noted that <eiffel>tool_title</eiffel> and <eiffel>title</eiffel> can | + | It should be noted that <eiffel>tool_title</eiffel> and <eiffel>title</eiffel> can yield different values. <eiffel>tool_title</eiffel> should remain constant, <eiffel>title</eiffel> is a mutable title of the tool and is settable via <eiffel>set_title</eiffel>. <eiffel>tool_title</eiffel> is used in the initialization of <eiffel>title</eiffel> and nothing more. Of course you are free to base a new title using <eiffel>tool_title</eiffel> as part of an aggregated string when setting <eiffel>title</eiffel>. |
=== <eiffel>tool_icon_buffer</eiffel> === | === <eiffel>tool_icon_buffer</eiffel> === | ||
− | As well as title, all tools require an icon to | + | As well as title, all tools require an icon to distinguish them graphically from other tools. Instead of an <eiffel>EV_PIXMAP</eiffel>, which [[EiffelVision2]] developers may be used to, a <eiffel>EV_PIXEL_BUFFER</eiffel> is required. A <eiffel>EV_PIXEL_BUFFER</eiffel> retains information related to the alpha channel of a image when loaded so the docking library is able to utilize the extra information for graphical effects. |
− | For now, simply use one of the stock EiffelStudio icons available from <eiffel>stock_pixmaps</eiffel>. | + | For now, simply use one of the stock EiffelStudio icons available from <eiffel>stock_pixmaps</eiffel>. A recommend default is to use <eiffel>stock_pixmaps.tool_output_icon_buffer</eiffel> as it is generic enough for all tools. Of course, if the tool is to truly be integrated into EiffelStudio it will require it's own unique icon. |
− | If you wish to add your own pixmap matrix and use the [[Eiffel Matrix Code Generator]] tool then feel free. The project source root folder contains a file call ''readme.txt'' that explains the format of a configuration file. | + | {{Note|If you wish to add your own pixmap matrix and use the [[Eiffel Matrix Code Generator]] tool then feel free. The project source root folder contains a file call ''readme.txt'' that explains the format of a configuration file.}} |
− | It should be noted; just like <eiffel>tool_title</eiffel>, <eiffel>tool_icon_buffer</eiffel> should remain unmodified as it is used only during initialization. <eiffel>icon</eiffel> actually represents the icon pixel buffer used by the tool, which may be changed using <eiffel>set_icon</eiffel>. Of course if | + | It should be noted; just like <eiffel>tool_title</eiffel>, <eiffel>tool_icon_buffer</eiffel> should remain unmodified as it is used only during initialization. <eiffel>icon</eiffel> actually represents the icon pixel buffer used by the tool, which may be changed using <eiffel>set_icon</eiffel>. Of course if there is a need to reset the tool, calling <eiffel>set_icon</eiffel> with <eiffel>tool_icon_buffer</eiffel> as an argument as the icon will ensure the tools appear as it did on first show. |
=== <eiffel>create_widget</eiffel> === | === <eiffel>create_widget</eiffel> === | ||
− | <eiffel>create_widget</eiffel> is a factory function | + | <eiffel>create_widget</eiffel> is a factory function which must return a widget of the same type as defined for the inherited <eiffel>ES_DOCKABLE_TOOL_WINDOW</eiffel>'s actual generic parameter. Here the tool should '''only''' create the widget and do any '''must'''-needed initialization. <eiffel>create_widget</eiffel> will be called the first time the tool requires the user widget and it will only be called once per tool object. Any other initialization can be done in <eiffel>build_tool_interface</eiffel>, but more on this later. |
=== <eiffel>build_tool_interface</eiffel> === | === <eiffel>build_tool_interface</eiffel> === | ||
− | Briefly mentioned in the description of <eiffel>create_widget</eiffel>, <eiffel>build_tool_interface</eiffel> is implemented to perform the brunt of all tool initialization. Initialization of the tool covers the creation of additional widget objects, setting parameters and extending them to <eiffel>user_widget</eiffel>. When called, <eiffel>build_tool_interface</eiffel> receives the created user widget as an argument <eiffel>a_widget</eiffel>. | + | Briefly mentioned in the description of <eiffel>create_widget</eiffel>, <eiffel>build_tool_interface</eiffel> is implemented to perform the brunt of all tool initialization. Initialization of the tool covers the creation of additional widget objects, setting parameters and extending them to <eiffel>user_widget</eiffel>. When called, <eiffel>build_tool_interface</eiffel> receives the created user widget as an argument <eiffel>a_widget</eiffel>. It is encouraged to use <eiffel>a_widget</eiffel> instead of accessing <eiffel>user_widget</eiffel> directly to avoid any possible, unavoidable future initialization recursion issues. |
− | Tool bars and tool bar buttons need not be created in <eiffel>build_tool_interface</eiffel> because tool bars are created using <eiffel>create_tool_bar_items</eiffel> and other such like factory functions | + | Tool bars and tool bar buttons need not be created in <eiffel>build_tool_interface</eiffel> because tool bars are created using <eiffel>create_tool_bar_items</eiffel> and other such like factory functions. The details for creating tool bars will be discussed in the next section. |
− | {{Note|<eiffel>build_tool_interface</eiffel> is called only when the window is shown for the first time, which can lead to calls being made on <eiffel>Void</eiffel> targets | + | {{Note|<eiffel>build_tool_interface</eiffel> is called only when the window is shown for the first time, which can lead to calls being made on <eiffel>Void</eiffel> targets if the tool is accessed from other parts of EiffelStudio. If the tool acts stand-alone then this is not a concern. See the "Gotcha" information below.}} |
=== <eiffel>create_tool_bar_items</eiffel> === | === <eiffel>create_tool_bar_items</eiffel> === | ||
− | Most tools require some form of | + | Most tools require some form of proprietary tool bar so it's only natural that the base implementation provide a means to add one. By default <eiffel>create_tool_bar_items</eiffel> is <eiffel>deferred</eiffel> because it's more a case to have a tool bar than it is not to have one. |
− | <eiffel>create_tool_bar_items</eiffel> is called once upon initialization and only requires any implementation to return a list of tool bar items, which can be any item that derives <eiffel>SD_TOOL_BAR_ITEM</eiffel>. | + | <eiffel>create_tool_bar_items</eiffel> is called once upon initialization and only requires any implementation to return a list of tool bar items, which can be any item that derives <eiffel>SD_TOOL_BAR_ITEM</eiffel>. Tool initialization takes care of constructing the tool bar and placing the buttons on it, determined by the order the buttons appear in the <eiffel>Result</eiffel> list. If <eiffel>Void</eiffel> or and empty list is returned then no tool bar will be created. |
− | For the more | + | For the more adventurous, take a look at and even experiment with the following features; <eiffel>is_tool_bar_bottom_aligned</eiffel>, <eiffel>create_right_tool_bar_items</eiffel> and <eiffel>create_mini_tool_bar_items</eiffel>. |
== A Delayed Initialization Gotcha == | == A Delayed Initialization Gotcha == | ||
− | <eiffel>build_tool_interface</eiffel> is called only when the window is shown for the first time, which can lead to calls being made on <eiffel>Void</eiffel> targets | + | <eiffel>build_tool_interface</eiffel> is called only when the window is shown for the first time, which can lead to calls being made on <eiffel>Void</eiffel> targets if the tool is accessed from other parts of EiffelStudio. If the tool acts stand-alone then this is not a concern and the reading of this section may be skipped. |
− | In the example of the Errors and Warnings tool, the event list service pushes error event list items to the tool. When an error item is pushed the UI must respond by enabling tool bar buttons and other commands. The Errors and Warnings tool may still be hidden and so <eiffel>build_tool_interface</eiffel> not yet called. This creates a situation where the UI widgets are | + | In the example of the Errors and Warnings tool, the event list service pushes error event list items to the tool. When an error item is pushed the UI must respond by enabling tool bar buttons and other commands. The Errors and Warnings tool may still be hidden and so <eiffel>build_tool_interface</eiffel> not yet called. This creates a situation where the UI widgets are present in an unattached state and making calls upon them illegal. There are two solutions: |
* Protect calls to the widget by testing if it is attached to an object. | * Protect calls to the widget by testing if it is attached to an object. | ||
− | * Where access to a widget object | + | * Where access to a widget object created in <eiffel>build_tool_interface</eiffel> is required, call <eiffel>initialize</eiffel>, respecting it's preconditions. |
− | The Errors and Warnings tool actually uses the latter of these solutions as seen in <eiffel>on_event_added</eiffel> and <eiffel>on_event_removed</eiffel>. | + | The Errors and Warnings tool actually uses the latter of these solutions as can be seen in <eiffel>on_event_added</eiffel> and <eiffel>on_event_removed</eiffel>. |
− | Both of the solution respect the recommended approach | + | if not is_initialized then |
+ | initialize | ||
+ | end | ||
+ | |||
+ | Both of the solution respect the recommended approach for delaying the initialization of the tool until requested. In the first solution the tool simply ignores any UI manipulation requested, which could be deferred until the tool is show if so applicable. The latter option forces initialization because it is deemed time to initialize. The tool will most likely have already been created and EiffelStudio made visible before any interaction with the tool takes place thereby, in effect, delaying the initialization the tool. | ||
There is another solution, but it is '''not''' recommended because it will initialize the tool upon creation, and penalizes start up performance; it is mention for the purpose of an absolute requirement! You can initialize the tool upon creation, when EiffelStudio is starting up, by redefining <eiffel>on_before_initialize</eiffel> and making a call to <eiffel>initialize</eiffel>. | There is another solution, but it is '''not''' recommended because it will initialize the tool upon creation, and penalizes start up performance; it is mention for the purpose of an absolute requirement! You can initialize the tool upon creation, when EiffelStudio is starting up, by redefining <eiffel>on_before_initialize</eiffel> and making a call to <eiffel>initialize</eiffel>. | ||
== Tool Completed == | == Tool Completed == | ||
− | As far as developing a tool, that is pretty much it. All that is left to do now is to | + | As far as developing a tool, that is pretty much it. All that is left to do now is to actually integrate the tool into the IDE. |
− | In EiffelStudio open the class <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel>. <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel> contains all the tools found in EiffelStudio and provides quick access inside EiffelStudio to any of those tools. | + | === Adding a Tool Accessor === |
+ | In EiffelStudio open the class <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel>. <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel> contains all the tools found in EiffelStudio and provides quick access inside EiffelStudio to any of those tools. It is here where an attribute, and an associated assigner, needs to be added. | ||
− | Locate <eiffel>errors_and_warnings_tool</eiffel> and underneath add a class attribute for | + | errors_and_warnings_tool: ES_ERRORS_AND_WARNINGS_TOOL assign set_errors_and_warnings_tool |
+ | -- Errors and warnings tool | ||
+ | |||
+ | Locate <eiffel>errors_and_warnings_tool</eiffel> and underneath add a class attribute for the tool, using the same class name type of your tool itself. Next locate the attribute assigner for <eiffel>errors_and_warnings_tool</eiffel>, <eiffel>set_errors_and_warnings_tool</eiffel>. Replicate <eiffel>set_errors_and_warnings_tool</eiffel> and adjust it to set the class attribute for the tool just added. | ||
+ | |||
+ | set_errors_and_warnings_tool (a_tool: like errors_and_warnings_tool) | ||
+ | do | ||
+ | errors_and_warnings_tool := a_tool | ||
+ | ensure | ||
+ | errors_and_warnings_tool_set: errors_and_warnings_tool = a_tool | ||
+ | end | ||
+ | |||
+ | Another registration detail to add is to make the tool available in the <eiffel>all_tools</eiffel> list. Locate <eiffel>all_tools</eiffel> in <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel> and add the new tool to the end of the list of tools. Replicate the entry for the Errors and Warnings tool and replace <eiffel>errors_and_warnings_tool</eiffel> with the attribute for the new tool. | ||
+ | |||
+ | if errors_and_warnings_tool /= Void then | ||
+ | Result.extend (errors_and_warnings_tool) | ||
+ | end | ||
=== Building and Registering === | === Building and Registering === | ||
− | Now | + | Now the accessor and an associated assigner exists it is time to finish up by building and registering the tool. Open <eiffel>EB_DEVELOPMENT_WINDOW_MAIN_BUILDER</eiffel> in EiffelStudio and locate <eiffel>build_errors_and_warnings_tool</eiffel>. Using <eiffel>build_errors_and_warnings_tool</eiffel> as a reference add the code required to build the tool. It should consist of the creation of the tool, calling the assigner just created in <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel> and a call to <eiffel>setup_tool</eiffel>. |
+ | |||
+ | build_errors_and_warnings_tool is | ||
+ | -- Build warnings tool. | ||
+ | local | ||
+ | l_tool: ES_ERRORS_AND_WARNINGS_TOOL | ||
+ | do | ||
+ | create l_tool.make (develop_window) | ||
+ | develop_window.tools.set_errors_and_warnings_tool (l_tool) | ||
+ | setup_tool (l_tool, "show_errors_and_warnings_tool") | ||
+ | end | ||
+ | |||
+ | In the call to <eiffel>setup_tool</eiffel> be sure to replace the shortcut string <eiffel>"show_errors_and_warnings_tool"</eiffel> with the name of the new tool, prefixed with "show_". Shortcut handling is not discussed here, instead that will be discussed in the next installment. | ||
+ | |||
+ | The next thing to do it to call the new build routine just added. This will need to added inside the routine <eiffel>build_tools</eiffel>. In <eiffel>build_tools</eiffel> locate the call to <eiffel>build_errors_and_warnings_tool</eiffel> and place the call to the new build routine directly after it. | ||
+ | |||
+ | === Adding a Tool Menu Entry === | ||
+ | One of the final tasks is to add the tool to the ''View'' > ''Tool'' menu in the EiffelStudio IDE. Open <eiffel>EB_DEVELOPMENT_WINDOW_MENU_BUILDER</eiffel> in EiffelStudio and locate <eiffe>tool_list_menu</eiffel>. A quick review of the code reveals how to add the new tool to the menu. As an example using the running Errors and Warnings tool find the following line of code: | ||
+ | |||
+ | if develop_window.tools.errors_and_warnings_tool /= Void then | ||
+ | fill_show_menu_for_tool (Result, develop_window.tools.errors_and_warnings_tool) | ||
+ | end | ||
− | + | Replicate this line of code at the correct placement in the menu, replacing the call to <eiffel>errors_and_warnings_tool</eiffel> with the attribute added in <eiffel>EB_DEVELOPMENT_WINDOW_TOOLS</eiffel>. | |
− | + | All there is left to do is to compile and debug. | |
That's it! | That's it! | ||
Line 107: | Line 217: | ||
There is still much more to be talked about when building an EiffelStudio tool. Hopefully this page has given you the ground work needed to start building your own tool. | There is still much more to be talked about when building an EiffelStudio tool. Hopefully this page has given you the ground work needed to start building your own tool. | ||
− | In the next installation [Tool Integration Development 2] tool shortcuts, preferences | + | In the next installation [Tool Integration Development 2] tool shortcuts, preferences, internationalization, helper implementation, commander modeling and how to add a tool to the default EiffelStudio layout will be explained. |
Latest revision as of 12:20, 4 June 2012
Welcome to the the EiffelStudio tool integration development page. The content held within this page will show you how to develop and integrate a new tool into the EiffelStudio IDE.
Contents
Getting Started
Creating a new EiffelStudio tools is two fold; a tool descriptor/shim and a the dockable tool panel.
The tool descriptor is used by the IDE to create command objects, menu items, tool bar buttons, connecting shortcut preferences and a number of other functions. The descriptor is used in place of the actual tool for these functions as to defer the creation of the actual tool until it is shown in then EiffelStudio IDE.
A tool descriptor does not just describe the tool, its type is used to through out parts of the EiffelStudio IDE to query and instantiate editions the tool is a dynamic fashion. The internals of this dynamic mechanism are explained in a later tutorial.
The dockable tool panel represents the actual dockable tool user interface.
This tutorial will only cover the basics of tool development, for more advanced tutorials see Tool Integration Development 2 and Tool Integration Service Development and Services.
Requirements
The content of this tutorial relates to the working version of EiffelStudio 6.1, current attainable from the open source repository https://svn.eiffel.com/eiffelstudio/trunk/Src/Eiffel. Please see the pages on attain the EiffelStudio source code from the Repository, as well as information on Compiling EiffelStudio and Debugging EiffelStudio.
Once you have EiffelStudio compiled and in a state where it can be debugged then continue reading.
Creating a Tool Descriptor
Once the tool user interface panel has been created it needs to be represented by a descriptor. Tool descriptors are simple to implement and support more advanced features such as automatic recycling on closing and multiple-editions, which are explained in a later tutorial.
All tool descriptors are implement ES_TOOL taking a generic parameter representing the tool's user interface panel. Below shows the minimal implementation of ES_CUSTOM_TOOL
to describe ES_CUSTOM_TOOL_PANEL
.
class ES_CUSTOM_TOOL inherit ES_TOOL [ES_CUSTOM_TOOL_PANEL] create {NONE} default_create feature -- Access icon: EV_PIXEL_BUFFER -- Tool icon. do Result := stock_pixmaps.tool_output_icon_buffer end icon_pixmap: EV_PIXMAP -- Tool icon pixmap. do Result := stock_pixmaps.tool_output_icon end title: STRING_32 -- Tool title. do Result := "Custom Tool" end shortcut_preference_name: STRING_32 -- An optional shortcut preference name, for automatic preference binding. do end feature {NONE} -- Factory create_tool: ES_CUSTOM_TOOL_PANEL -- Creates the tool for first use on the development `window' do create Result.make (window, Current) end end
One of the most important function of the descriptor is create_tool
. It's used to instantiate the tool's user interface panel, which will be requested when the tool is shown, either in full (that actual tool is visible) or in part (tab-docked but hidden beneath another tool) in place in the EiffelStudio IDE.
Both icon
and icon_pixmap
must both return a valid pixmap. In the example the icon for the Output Tool was reused for the purpose of demonstration. Creating a using custom pixmaps is explained here.
The title
found should return the tool's title as displayed in the tool's user interface when docked and tabbed or when the tool is opened as a undocked floating window.
Finally shortcut_preference_name
is used to associate the tool with a shortcut preference, which is expanded upon in a later tutorial.
Outdated: This article is to be considered outdated when using the latest version of EiffelStudio or it's tools. This article will be outdated in the release of EiffelStudio 6.1
An Implemented Tool Reference
If at any time you need to reference an actually implementation of a tool using the information in these pages, see the implementation of ES_ERRORS_AND_WARNINGS_TOOL
in the EiffelStudio code. It was one of the first tools developed to use the new tool development base and helper classes you'll used to develop EiffelStudio tools.
Creating a Tool
Before anything else can be done you need to create a new tool class. Most tools inside EiffelStudio utilized a base class call EB_TOOL
(as of 6.1.) EB_TOOL
is currently the most basic of tool classes and has become a little outdated. In 6.1 a new abstract base class call ES_DOCKABLE_TOOL_WINDOW
was created for the purpose of creating EiffelStudio tools with a greater amount of ease. ES_DOCKABLE_TOOL_WINDOW
still derives from EB_TOOL
but this may change in the future if all tools are converted to use ES_DOCKABLE_TOOL_WINDOW
instead. With that said you should not use EB_TOOL
directly, it was only mentioned for those curious about the class hierarchy.
Note: So to be clear, only use ES_DOCKABLE_TOOL_WINDOW
as your base for tool development.
The use of ES_DOCKABLE_TOOL_WINDOW
requires a generic parameter constrained to EV_WIDGET
. The generic parameter, G
, represents the tool's "user widget". The user widget defined by the feature user_widget
is the top level widget the tool should interact with. For instance, if the tool consists of just a grid, list or tree widget then the user control will be of the respective type. For more complex scenarios the generic parameter could just as easily be a composite widget or a simple container like EV_HORIZONTAL_BOX
or EV_VERTICAL_BOX
.
To start out, create a new Eiffel class and inherit ES_DOCKABLE_TOOL_WINDOW
, specifying the generic parameter for the tool's user widget.
Note: ES_DOCKABLE_TOOL_WINDOW
has many advantages to being a base class for all tools; simplified implementation, a small number of required to implement features and delayed initialization of the tool itself. This last aspect is important because the more tools put into EiffelStudio the more time it takes the IDE to start up. Using delayed initialization comes with a few gotchas that you may possibly run into but it's recommended that it is used, so not to slow down EiffelStudio.
Declaring the Tool's Creation Routine
ES_DOCKABLE_TOOL_WINDOW
even comes complete with a basic creation routine, make
, which should be passed the active development window object the tool is created with. EiffelStudio supports the display of mutli IDE windows and the passed object represents the window the tool is active within. If the tool has no further actions required to be performed on creation then simply specify your tool's creation routine as the one from ES_DOCKABLE_TOOL_WINDOW
(which is actually from EB_TOOL
.) On the other hand, if the tool has other creation initialization to perform either redefine make
or specify an alternative creation routine. It is important that develop_window
be set correctly for ES_DOCKABLE_TOOL_WINDOW
to function correctly.
Alternatively if simplicity is what you are after, simply redefine on_before_initialize
to initialize any class attributes or other data. Please be aware that this routine is called during creation of the tool and affects the overall EiffelStudio start up time.
Implementing the Requirements
There are less than a hand full of deferred
features from ES_DOCKABLE_TOOL_WINDOW
to implement for a tool to compile and execute in EiffelStudio. The following are the list of deferred features requiring implementation (as of 6.1):
-
build_tool_interface
-
create_tool_bar_items
-
create_widget
-
tool_icon_buffer
-
tool_title
Each of these routines will be discussed, first the rudimentary features and then optional implementation for which empty routines can simply be defined.
tool_title
Every tool requires a title. The title is the text that is used when the tool is first created and will appear on the tool's tab button or tool's window title when undocked. It is also used when building a menu entry under View > Tools in the EiffelStudio IDE. Generally the title should be internationalized but internationalization details are discussed in a further tutorial. For now, it is good enough to simple return a attached non-empty Eiffel string.
It should be noted that tool_title
and title
can yield different values. tool_title
should remain constant, title
is a mutable title of the tool and is settable via set_title
. tool_title
is used in the initialization of title
and nothing more. Of course you are free to base a new title using tool_title
as part of an aggregated string when setting title
.
tool_icon_buffer
As well as title, all tools require an icon to distinguish them graphically from other tools. Instead of an EV_PIXMAP
, which EiffelVision2 developers may be used to, a EV_PIXEL_BUFFER
is required. A EV_PIXEL_BUFFER
retains information related to the alpha channel of a image when loaded so the docking library is able to utilize the extra information for graphical effects.
For now, simply use one of the stock EiffelStudio icons available from stock_pixmaps
. A recommend default is to use stock_pixmaps.tool_output_icon_buffer
as it is generic enough for all tools. Of course, if the tool is to truly be integrated into EiffelStudio it will require it's own unique icon.
Note: If you wish to add your own pixmap matrix and use the Eiffel Matrix Code Generator tool then feel free. The project source root folder contains a file call readme.txt that explains the format of a configuration file.
It should be noted; just like tool_title
, tool_icon_buffer
should remain unmodified as it is used only during initialization. icon
actually represents the icon pixel buffer used by the tool, which may be changed using set_icon
. Of course if there is a need to reset the tool, calling set_icon
with tool_icon_buffer
as an argument as the icon will ensure the tools appear as it did on first show.
create_widget
create_widget
is a factory function which must return a widget of the same type as defined for the inherited ES_DOCKABLE_TOOL_WINDOW
's actual generic parameter. Here the tool should only create the widget and do any must-needed initialization. create_widget
will be called the first time the tool requires the user widget and it will only be called once per tool object. Any other initialization can be done in build_tool_interface
, but more on this later.
build_tool_interface
Briefly mentioned in the description of create_widget
, build_tool_interface
is implemented to perform the brunt of all tool initialization. Initialization of the tool covers the creation of additional widget objects, setting parameters and extending them to user_widget
. When called, build_tool_interface
receives the created user widget as an argument a_widget
. It is encouraged to use a_widget
instead of accessing user_widget
directly to avoid any possible, unavoidable future initialization recursion issues.
Tool bars and tool bar buttons need not be created in build_tool_interface
because tool bars are created using create_tool_bar_items
and other such like factory functions. The details for creating tool bars will be discussed in the next section.
Note: build_tool_interface
is called only when the window is shown for the first time, which can lead to calls being made on Void
targets if the tool is accessed from other parts of EiffelStudio. If the tool acts stand-alone then this is not a concern. See the "Gotcha" information below.
create_tool_bar_items
Most tools require some form of proprietary tool bar so it's only natural that the base implementation provide a means to add one. By default create_tool_bar_items
is deferred
because it's more a case to have a tool bar than it is not to have one.
create_tool_bar_items
is called once upon initialization and only requires any implementation to return a list of tool bar items, which can be any item that derives SD_TOOL_BAR_ITEM
. Tool initialization takes care of constructing the tool bar and placing the buttons on it, determined by the order the buttons appear in the Result
list. If Void
or and empty list is returned then no tool bar will be created.
For the more adventurous, take a look at and even experiment with the following features; is_tool_bar_bottom_aligned
, create_right_tool_bar_items
and create_mini_tool_bar_items
.
A Delayed Initialization Gotcha
build_tool_interface
is called only when the window is shown for the first time, which can lead to calls being made on Void
targets if the tool is accessed from other parts of EiffelStudio. If the tool acts stand-alone then this is not a concern and the reading of this section may be skipped.
In the example of the Errors and Warnings tool, the event list service pushes error event list items to the tool. When an error item is pushed the UI must respond by enabling tool bar buttons and other commands. The Errors and Warnings tool may still be hidden and so build_tool_interface
not yet called. This creates a situation where the UI widgets are present in an unattached state and making calls upon them illegal. There are two solutions:
- Protect calls to the widget by testing if it is attached to an object.
- Where access to a widget object created in
build_tool_interface
is required, callinitialize
, respecting it's preconditions.
The Errors and Warnings tool actually uses the latter of these solutions as can be seen in on_event_added
and on_event_removed
.
if not is_initialized then initialize end
Both of the solution respect the recommended approach for delaying the initialization of the tool until requested. In the first solution the tool simply ignores any UI manipulation requested, which could be deferred until the tool is show if so applicable. The latter option forces initialization because it is deemed time to initialize. The tool will most likely have already been created and EiffelStudio made visible before any interaction with the tool takes place thereby, in effect, delaying the initialization the tool.
There is another solution, but it is not recommended because it will initialize the tool upon creation, and penalizes start up performance; it is mention for the purpose of an absolute requirement! You can initialize the tool upon creation, when EiffelStudio is starting up, by redefining on_before_initialize
and making a call to initialize
.
Tool Completed
As far as developing a tool, that is pretty much it. All that is left to do now is to actually integrate the tool into the IDE.
Adding a Tool Accessor
In EiffelStudio open the class EB_DEVELOPMENT_WINDOW_TOOLS
. EB_DEVELOPMENT_WINDOW_TOOLS
contains all the tools found in EiffelStudio and provides quick access inside EiffelStudio to any of those tools. It is here where an attribute, and an associated assigner, needs to be added.
errors_and_warnings_tool: ES_ERRORS_AND_WARNINGS_TOOL assign set_errors_and_warnings_tool -- Errors and warnings tool
Locate errors_and_warnings_tool
and underneath add a class attribute for the tool, using the same class name type of your tool itself. Next locate the attribute assigner for errors_and_warnings_tool
, set_errors_and_warnings_tool
. Replicate set_errors_and_warnings_tool
and adjust it to set the class attribute for the tool just added.
set_errors_and_warnings_tool (a_tool: like errors_and_warnings_tool) do errors_and_warnings_tool := a_tool ensure errors_and_warnings_tool_set: errors_and_warnings_tool = a_tool end
Another registration detail to add is to make the tool available in the all_tools
list. Locate all_tools
in EB_DEVELOPMENT_WINDOW_TOOLS
and add the new tool to the end of the list of tools. Replicate the entry for the Errors and Warnings tool and replace errors_and_warnings_tool
with the attribute for the new tool.
if errors_and_warnings_tool /= Void then Result.extend (errors_and_warnings_tool) end
Building and Registering
Now the accessor and an associated assigner exists it is time to finish up by building and registering the tool. Open EB_DEVELOPMENT_WINDOW_MAIN_BUILDER
in EiffelStudio and locate build_errors_and_warnings_tool
. Using build_errors_and_warnings_tool
as a reference add the code required to build the tool. It should consist of the creation of the tool, calling the assigner just created in EB_DEVELOPMENT_WINDOW_TOOLS
and a call to setup_tool
.
build_errors_and_warnings_tool is -- Build warnings tool. local l_tool: ES_ERRORS_AND_WARNINGS_TOOL do create l_tool.make (develop_window) develop_window.tools.set_errors_and_warnings_tool (l_tool) setup_tool (l_tool, "show_errors_and_warnings_tool") end
In the call to setup_tool
be sure to replace the shortcut string "show_errors_and_warnings_tool"
with the name of the new tool, prefixed with "show_". Shortcut handling is not discussed here, instead that will be discussed in the next installment.
The next thing to do it to call the new build routine just added. This will need to added inside the routine build_tools
. In build_tools
locate the call to build_errors_and_warnings_tool
and place the call to the new build routine directly after it.
Adding a Tool Menu Entry
One of the final tasks is to add the tool to the View > Tool menu in the EiffelStudio IDE. Open EB_DEVELOPMENT_WINDOW_MENU_BUILDER
in EiffelStudio and locate <eiffe>tool_list_menu</eiffel>. A quick review of the code reveals how to add the new tool to the menu. As an example using the running Errors and Warnings tool find the following line of code:
if develop_window.tools.errors_and_warnings_tool /= Void then fill_show_menu_for_tool (Result, develop_window.tools.errors_and_warnings_tool) end
Replicate this line of code at the correct placement in the menu, replacing the call to errors_and_warnings_tool
with the attribute added in EB_DEVELOPMENT_WINDOW_TOOLS
.
All there is left to do is to compile and debug.
That's it!
Next Steps
There is still much more to be talked about when building an EiffelStudio tool. Hopefully this page has given you the ground work needed to start building your own tool.
In the next installation [Tool Integration Development 2] tool shortcuts, preferences, internationalization, helper implementation, commander modeling and how to add a tool to the default EiffelStudio layout will be explained.