Friday, December 13, 2013

Split Editors: Anatomy of a new Feature


Finally, we're able to start moving the Eclipse IDE beyond what 3.x could do, showing some real pay-off for all the pain (for you and us). First up is the infamous Bug 8009, 11 years old and highly desired but never able to be implemented in the 3.x code base.

Lars has (again) beat me to the punch as far as the announcement goes so I'll take a different track; how did we go from the idea to the finished feature ?

Step 1: Design and Mockups


We started by making a variety of informal stabs at hacking the functionality, arriving in the end at the conclusion that in order to proceed we needed to allow a mechanism for  an MPart to contain a sub-structure (sashes and other parts...). This part is all about testing the scope of the feature and learning what we really need.

This eventually lead to the extension of the model with a new element MCompositePart, giving us a formal definition of a part that can contain other parts.

Step 2: Functional Prototype


Once the MCompositePart made it into the model it was time to hack up a prototype of the feature, making sure that it performs all the operations necessary to expose the new functionality. My favorite technique for this type of work (especially when it's eventually aimed at the IDE rather than pure e4) is to just make a new project based off the 'Hello World, Command' template. Then I work on the command's handler code until I have a 'command' that can do what I want. In this case I ended up with the following (Kodos to the Orion team; my code snippets use an Embedded Orion Editor):

public Object execute(ExecutionEvent event) throws ExecutionException {
 window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
 MApplication theApp = (MApplication) window.getService(MApplication.class);
 MTrimmedWindow win1 = (MTrimmedWindow) theApp.getChildren().get(0);

 // Get services
 ms =  win1.getContext().get(EModelService.class);
 ps = win1.getContext().get(EPartService.class);
 pe = win1.getContext().get(IPresentationEngine.class);
 
 MPart part = ps.getActivePart();
 if (part == null)
  return null;
  
 MPartStack stack = getStackFor(part);
 if (stack == null)
  return null;
  
 MStackElement stackSelElement = stack.getSelectedElement();
 if (stackSelElement instanceof MCompositePart) {
  unsplitPart((MCompositePart) stackSelElement);
 } else {
  horizontal = !horizontal ;
  splitPart(stackSelElement);
 }
  
 return null;
}

This gives me a command that will act as a toggle and change orientation each time you split (to ensure that we can do both) and accomplishes two objectives:
  1. Allows me to fully implement the desired functionality, creating methods to be used in the implementation of the handler.
  2. Test / refine the rendering capabilities to allow for the new requirements. In this case very few were needed as you can see by looking at the commits on Bug 378298. A lot of this code is only necessary because we have to support the new MCompositePart itself and it not directly related to the split editor feature.
BTW, not to worry about the implementations of the 'split' and 'unsplit' methods used in the code above; we'll be getting back to that code later :-).

Now we have a working implementation of the feature but it's not correctly packaged since all the code is in the handler class (and that handler uses the IDE's mechanisms but we will want this functionality in e4 as well)...what to do ? what to do ?

Step 3: Packaging the code for re-use


OK, the first thing to consider is what this feature would look like in pure e4. The pattern used by the MinMaxAddon is also applicable to this scenario so let's go with it:
  1. Define a new SplitPartAddon that reacts to two new IPresentationEngine constants "SPLIT_HORIZONTAL" and "SPLIT_VERTICAL".
  2. Move the methods required to do this out of the original handler into the new addon, calling them appropriately based on the new tags being added / removed from an existing MPart.
Here's SplitterAddon (note that the complete functional implementation is only 150 lines of code):

/**
 * Listens for the IPresentationEngine's SPLIT_HORIZONTAL and SPLIT_VERTICAL tags being applied to
 * an MPart and takes the appropriate steps to split / unsplit the part
 * 
 * @since 1.1
 */
public class SplitterAddon {
 @Inject
 EModelService ms;

 @Inject
 EPartService ps;

 /**
  * Handles changes in tags
  * 
  * @param event
  */
 @Inject
 @Optional
 private void subscribeTopicTagsChanged(
   @UIEventTopic(UIEvents.ApplicationElement.TOPIC_TAGS) Event event) {
  Object changedObj = event.getProperty(EventTags.ELEMENT);

  if (!(changedObj instanceof MPart))
   return;

  MPart part = (MPart) changedObj;

  if (UIEvents.isADD(event)) {
   if (UIEvents.contains(event, UIEvents.EventTags.NEW_VALUE,
     IPresentationEngine.SPLIT_HORIZONTAL)) {
    splitPart(part, true);
   } else if (UIEvents.contains(event, UIEvents.EventTags.NEW_VALUE,
     IPresentationEngine.SPLIT_VERTICAL)) {
    splitPart(part, false);
   }
  } else if (UIEvents.isREMOVE(event)) {
   MCompositePart compPart = SplitterAddon.findContainingCompositePart(part);
   if (UIEvents.contains(event, UIEvents.EventTags.OLD_VALUE,
     IPresentationEngine.SPLIT_HORIZONTAL)) {
    unsplitPart(compPart);
   } else if (UIEvents.contains(event, UIEvents.EventTags.OLD_VALUE,
     IPresentationEngine.SPLIT_VERTICAL)) {
    unsplitPart(compPart);
   }
  }
 }

 /**
  * Finds the CompositePart containing the given part (if any)
  * 
  * @param part
  * @return The MCompositePart or 'null' if none is found
  */
 public static MCompositePart findContainingCompositePart(MPart part) {
  if (part == null)
   return null;

  MUIElement curParent = part.getParent();
  while (curParent != null && !(curParent instanceof MCompositePart))
   curParent = curParent.getParent();

  return (MCompositePart) curParent;
 }

 private void unsplitPart(MCompositePart compositePart) {
  if (compositePart == null)
   return;

  List innerElements = ms.findElements(compositePart, null, MPart.class, null);
  if (innerElements.size() < 2)
   return;

  MPart originalEditor = innerElements.get(1); // '0' is the composite part

  MElementContainer compParent = compositePart.getParent();
  int index = compParent.getChildren().indexOf(compositePart);
  compParent.getChildren().remove(compositePart);
  originalEditor.getParent().getChildren().remove(originalEditor);
  compParent.getChildren().add(index, originalEditor);

  if (ps.getActivePart() == originalEditor)
   ps.activate(null);
  ps.activate(originalEditor);
 }

 private MCompositePart createCompositePart(MPart originalPart) {
  MCompositePart compPart = ms.createModelElement(MCompositePart.class);
  compPart.setElementId("Split Host(" + originalPart.getLabel() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
  compPart.setLabel(originalPart.getLabel());
  compPart.setTooltip(originalPart.getTooltip());
  compPart.setIconURI(originalPart.getIconURI());
  compPart.setCloseable(true);
  compPart.setContributionURI("bundleclass://org.eclipse.e4.ui.workbench.addons.swt/org.eclipse.e4.ui.workbench.addons.splitteraddon.SplitHost"); //$NON-NLS-1$

  // Always remove the composite part from the model
  compPart.getTags().add(EPartService.REMOVE_ON_HIDE_TAG);

  return compPart;
 }

 void splitPart(MPart partToSplit, boolean horizontal) {
  MElementContainer parent = partToSplit.getParent();
  int index = parent.getChildren().indexOf(partToSplit);

  MPart editorClone = (MPart) ms.cloneElement(partToSplit, null);

  MCompositePart compPart = createCompositePart(partToSplit);

  // Add the new composite part to the model
  compPart.getChildren().add(editorClone);
  compPart.setSelectedElement(editorClone);
  parent.getChildren().add(index, compPart);
  parent.setSelectedElement(compPart);

  // Now, add the original part into the composite
  int orientation = horizontal ? EModelService.ABOVE : EModelService.LEFT_OF;
  ms.insert(partToSplit, editorClone, orientation, 0.5f);

  ps.activate(partToSplit);
 }
}

Now we can take our original IDE command handler and replace the code that used to call the methods directly with code that adds or removes the appropriate tags on the MPart for the active editor. Here's the final code for the IDE handler:

public class SplitHandler extends AbstractHandler {
 private EModelService modelService;
 private IWorkbenchWindow window;

 /**
  * The constructor.
  */
 public SplitHandler() {
 }

 public Object execute(ExecutionEvent event) throws ExecutionException {
  // Only works for the active editor
  IEditorPart activeEditor = HandlerUtil.getActiveEditor(event);
  if (activeEditor == null)
   return null;
  
  MPart editorPart = (MPart) activeEditor.getSite().getService(MPart.class);
  if (editorPart == null)
   return null;
  
  window = HandlerUtil.getActiveWorkbenchWindowChecked(event);

  // Get services
  modelService =  editorPart.getContext().get(EModelService.class);
  
  MPartStack stack = getStackFor(editorPart);
  if (stack == null)
   return null;

  window.getShell().setRedraw(false);
  try {
   MStackElement stackSelElement = stack.getSelectedElement();
   if (stackSelElement instanceof MCompositePart) {
    List innerElements = modelService.findElements(stackSelElement, null, MPart.class, null);
    MPart originalEditor = innerElements.get(1); // '0' is the composite part
    
    originalEditor.getTags().remove(IPresentationEngine.SPLIT_HORIZONTAL);
    originalEditor.getTags().remove(IPresentationEngine.SPLIT_VERTICAL);
   } else {
    if ("false".equals(event.getParameter("Splitter.isHorizontal"))) { //$NON-NLS-1$ //$NON-NLS-2$
     editorPart.getTags().add(IPresentationEngine.SPLIT_VERTICAL);
    } else {
     editorPart.getTags().add(IPresentationEngine.SPLIT_HORIZONTAL);
    }
   }
  } finally {
   window.getShell().setRedraw(true);
  }
  
  return null;
 }
 
 private MPartStack getStackFor(MPart part) {
  MUIElement presentationElement = part.getCurSharedRef() == null ? part : part.getCurSharedRef();
  MUIElement parent = presentationElement.getParent();
  while (parent != null && !(parent instanceof MPartStack))
   parent = parent.getParent();
  
  return (MPartStack) parent;
 }
}

We also add a couple of key bindings for easy access...in this case Ctrl + '_' gives a horizontal split (one above the other) and Ctrl + '{' gives a side-by-side split.

Step 4: Release and Polish

 

Finally, we blog about it so that folks will know that it's available...;-).

Then we take the feedback and polish the feature (adding CSS to define the splitter's color...). The earlier we can release a new feature the more time we'll have to get the feedback and act on it before the actual release. If there's one thing as certain as death and taxes is that you won't  get it right the first time. I've learned that it's more productive to 'get it working, get it out',  then polish it rather than putting a ton of work into the polish up front just to trash it later based on feedback.

P.S. While the current implementation is specific to the Eclipse IDE and editors in particular we do expect to extend this feature to allow the proper splitting of any MPart. This will, however, require some more design work on allowing the MCompositePart to somehow 'host' the Toolbar and Menus from the currently active 'child' MPart so they appear in the stack and work correctly.

Alrighty then, on to the next one...different presentations based on how many monitors you have.


Sunday, April 7, 2013

Another Great EclipseCon

Well it's already been a week since I got back from Boston and I'd better get this out before the memories fade into the background. First off it was really nice for me to finally be able to meet Lars, Wim and \Sopot who've been so great at extending both e4's capabilities and exposure.After our excellent conversation on Sunday I'm sure that there will be more shared beers to come...;-). Unfortunately (for me) Brian De Alwis couldn't be there due to his wife being...well...due (congrats Brian !).

OK, on to the conference...this year I had more time to attend talks because I was only giving one myself. For those that have been asking the slides are here. All the ones I attended were interesting and quite informative. Wim and Lars'"Shake that FUD" talk on mixing e4 and IDE was great, demonstrating the advantages of e4's architecture and ability but presented in a hilarious Laurel and Hardy style. There was also an excellent talk on the real-world issues encountered while porting a large application to e4. This was a great indication that there is true enterprise adoption of Eclipse 4.

Tom Schindl's talk "Modern UIs with JavaFX" showing off his JavaFX work with cool animations was made even more interesting by his demonstration showing the adoption of parts of the Eclipse JDK, allowing his simple JavaFX IDE to provide code assisst...  This was a great example of the direction we want to go; moving code away from being tightly bound to the IDE in order to allow it to be re-used much more freely.

For me though the absolute tops was the talk from l33tlabs , "Bling: The GPU powered Game IDE" demo'd a re-write of SWT using OpenGL. These guys come from a gaming background so everything about the presentation was unique (like their business card). Slides ? Who needs 'em ? Their presentation was a room around which a camera roamed, zooming in on each 'slide' (a note on a wall...). To them using the static (dare I say 'uncool') Eclipise UI just wouldn't cut it artistically so they decided to do something about it...;-). The results were stunning, seeing stacks in a running Eclipse IDE jiggle, fade, zoom as 'simple' effects has given me hope that we can make the Eclipse UI palatable to today's tablet using crowd. Early days yet but stay tuned...

Finally, away from the presentation rooms I've got to give a shout out to Marcel Bruch from CodeTrails, the folks who've given us Code Recommders. They had a really fun and effective way to demonstrate what they're all about. The idea was a 'Family Feud' game where you'd have to come up with the most common uses for a new object of some type (such as the SaveFileDialog, System...). I finally understand what they are all about ( think), it's a case of 'big data' meets code assist. Marcel and I had a great talk after my (winning!) run at the game (sort of unfair since I've written a lot of code using 'vi'...;-). We're trying to see what can be done to provide some form of code assist for injection annotations; something that would really help mitigate a real issue for folks wanting to adopt DI (as everyone should...;-).

OK, back to getting Kepler together. It's always a pleasure for me to attend EclipseCon and see the energy in the community. Hope to see you all again soon.

Stay tuned !! I'll be blogging here much more regularly with examples on using the newly available API from within the Eclipse IDE.




Friday, November 11, 2011

Almost over the Jet Lag

I'd meant to post this somewhat earlier but between catching up on work and sleep this is the first chance I've had.

I was lucky enough to be able to go to EclipseCon Europe to give a presentation on the advantages of the Eclipse 4 API (as well as assisting Tom Schindl on his talk about 4.1 / 4.2). This is a great conference ! Kudos to Ralph and everybody involved for making this a wonderful experience.

First thing I did when I got back was to fix a couple of defects pointed out by folks while I was at ECE (see, I do listen...;-):

Bug 351230 - This one's for Ed. Here's the first part...tabs can now shrink to only showing 1 character as in 3.x. Be assured that the Tab ordering issue is under active...er...discussion...;-).

Kai, your CSS re-styling issue should be gone now as well...

Just finished doing my submissions for EclispeCon in Virginia, we've got cool coming !!

All in all it was an excellent trip. It was my first time to Europe and I'm definitely going back ! Great people and so much to see...

Finally,a side note just to keep things in perspective. While I was there my best friend and his wife had their first child...while I had a nice time it's got nothing on that ! He'll get my trip stories once he comes back down to earth...


Now I've gotta get back to it...do my expenses (and pay John for the 50 Euro I borrowed..;-)...

Thursday, October 27, 2011

Eclipse 4: Beyond Compatibility


The bulk of the work for the past two Eclipse 4 releases has been on getting the compatibility layer (which allows all API clean 3.x components to 'just work' in 4.x) complete, robust and scalable. This is, however, a 'one off' exercise (thank goodness...it's been a bear!!) so we can expect to be back to a more normal 'development' cycle in 4.3.

While we're not done yet by Juno's release we will be...so now is the time to start thinking about where we want to take the platform in 4.3 and on into the future. The underlying architecture (called e4) upon which Eclipse 4 is implemented is capable of doing far more than just what 3.x provides in its presentation, the question is how we can leverage these new abilities to enhance the Eclipse User Experience.

I've recently presented a webinar called "Leveraging Eclipse 4 API to Build Components for the Future" which can be found on Eclipse Live. It gives a fairly general description of the basic 'e4' architecture (Modeled UI and Dependency Injection), suitable for folks that are just getting their first look at Eclipse 4.

Here's a screen shot of the webinar's demo showing a real PackageExplorer view hosted within a WizardPage.


This is just one example of the flexibility inherent in Eclipse 4. This turns out to be easy to do, here's a ZIP file (WebinarProject.zip) containing the project used to implement this wizard. To run it though its paces:
  1. Install a recent version of Eclipse 4 and fire it up
  2. Unzip the file somewhere and Import the project into your workspace
  3. Make a new Run Configuration and fire up the inner
  4. You will see that there's a new entry in the 'New...' drop down called 'Local View', opening it will get you to the state captured in the image above.
  5. It'll look pretty boring since your inner won't have any projects...but it's the PackageExplorer, just right-click and create a new project...;-).


(Gratuitous Plug Warning...;-) I'm heading off next week to EclipseCon Europe where (along with helping Tom Schindl out with some of his presentations) I'll be giving a talk entitled "Eclipse 4: Where do we go from here ?" that will cover some of these same questions, albeit from a slightly different angle. Folks lucky enough to be going to the talk will still want to view the webinar which will fill in more background than I'll be able to cover in a 25 minute presentation.

Late Breaking News !! Here's a screen capture taken from work that Bogdan Gheorghe is doing on creating an XText based CSS editor. While folks writing new e4 RCP apps can use this as a regular editor to define their app's CSS file here it's presented under the Appearance Preference page, where it will modify the CSS of the currently running Eclipse 4 session (replacing a slew of formerly discreet preferences).



This demonstrates one of the new approaches we've already identified; the Eclipse 4 tooling will allow itself to be hosted in two places; as a regular editor to define content for projects within the workspace as well as in an appropriate preference page to facilitate changes to the currently running Eclipse session. That way users don't have to learn two different mechanisms (and we only have one piece of code to improve...;-).

Got any ideas ? Remember when you opened an enhancement request that was eventually closed as 'undo-able' ? Now is the time to dust them off again or open new ones. If you just want to discuss them you can reach us on IRC at "#eclipse-e4' or feel free to send me an email at emoffatt@ca.ibm.com, it's not only my pleasure to discuss cool ideas, it's my job !

Monday, June 27, 2011

Eclipse 4 ships with Indigo


How do we know ? Well for one thing I finally have time to start blogging...;-). This is just the first of many posts I expect to be making over the summer (and likely the least technical).

After almost 3 years of design and development the next version of Eclipse is ready for you to start using.

Is it perfect yet ? No, see the Known Issues page here but we are sure that none of these issues cannot be addressed during the 4.2 cycle. It's now time to pass the ball over to the community; we need your input to make this version of Eclipse even better..

The current PMC plans are that 4.2 should be the base platform for the Juno release and it will be our primary focus for 4.2 to ensure that this happens. Any defects found by the release train components will be top priority (actually any component, release train or not, if you're willing to spend the time testing we're ready to spend the time fixing your issues). Here's how you can help:

Testing Your Components:

We've already done our own 'ad hoc' testing of various components (CDT, Mylyn...) but only the actual component team knows enough about their features to ensure that our compatibility coverage is 100%. Testing your component in Eclipse 4 is easy (if it weren't I'd likely be updating my resume rather than writing this blog post...;-), even for something like the CDT the testing time required was under 3 hours:
  1. Download Eclipse 4.1
  2. Use it as you would normally and install / check out your component
  3. Run through a test of your component's features
    • Open any perspectives and check that everything's there
    • Ensure that all views / editors work correctly
    • Check the part's menu and toolbar items to make sure they're OK
* Please don't play the waiting game on this ("I'll let the other components go first to see what happens"). The amount of interest shown in Eclipse 4 will be a significant factor in its remaining the release train target.

We're here for you:

If you encounter any issues which prevent you from testing your component feel free to contact me directly (emoffatt@ca.ibm.com) and I'd be happy to help out in any way (including working through the issues directly with you on the phone as I did with Chris for CDT, here's his blog entry about it).

Report any defects against Eclipse -> Platform -> UI as you normally would (setting the target milestone to 4.1.1 will help us triage them). As I said above these will receive preferred treatment...It is worth noting that we are well positioned for this, the compatibility layer and the e4 core are much more compartmentalized so our ability for find and fix defects is faaar faster than the equivalent in the 3.x stream.

Also feel free to join us on IRC at '#eclipse-e4' or by posting to the mailing list 'e4-dev@eclipse.org' to join in discussions about where we should be taking the 4.x stream beyond simply being compatible or just to share anything Eclipse 4 related.

I'm really looking forward to this year and seeing Eclipse 4 mature into the IDE of the future...

Eric

OK then, time to get the word out

Hi folks ! This is just a short intro blurb to introduce my self to Planet Eclipse...I'll scandalously steal most of this from by blog bio ;-).

My name is Eric Moffatt, I'm a tech fogie, having been in the User Interface arena for over 30 years (40 if you include the fact that my second ever program, Dartmouth Basic on a teletype, printed a triangle of asterisks). If we fast forward a bit I'm now an IBM employee and I've been a committer on the Eclipse Platform UI team since Eclipse 3.0 (and love it!).

I've also been involved with 'e4' since the original summit in 2008 and I'm an e4 committer as well as being the Eclipse 4 SDK development lead. This, of course, is what has brought me to the blogosphere...getting the word out about Eclipse 4.

I'll be immediately following this post with the first of quite a few specifically targeted at Eclipse 4 architecture and features. Various blog topics have been building up while I've been focused on the Eclipse 4.1 release, finally I have the time to put them together.

Eric