CActions vs DockActions

Hello,

I’m new to DockingFrames, so please bear with me a little -_-

I have DefaultSingleCDockables inside a StackDockStation, and I want to use Actions to obtain some sort of toolbar for every dockable.

I went through section 6 Actions in common.pdf, and was able to use CActions like CButton, CMenu, etc. by adding them to my dockables directly using:

dockable.addAction(action); //where action is an instance of CAction

First question: is it correct to add my actions one-by-one to my dockable (of type DefaultSingleCDockable)? I’m asking because after going through section 4 Actions in core.pdf, and in particular ActionsExample.java under tutorial.core.guide, it seems Actions are handled quite differently in the core library. The example says that “We cannot add actions directly to a Dockable, we add a group of actions. […]”, and introduces DefaultDockActionSource, where actions are grouped and then the whole group of actions is passed to the Dockable through:

dockable.setActionOffers(actions);

So is there the concept of “ActionSource” for CActions in the common library? Or is it ok to add actions one-by-one as I mentioned? Honestly, I was a bit confused to find two “Actions” implemntations in core and common, that’s because I’m new to the framework, as I already mentioned. So could you also briefly explain how does one decide what “Actions” implementation to choose? I mean DockActions from core, or CActions from common? Does it simply depend on the type of Dockable I’m using? I mean are DockActions compatible with Dockable types in core, and are CActions compatible with Dockable types in common (like DefaultSingleCDockable)?

You might conclude from my question that I didn’t really understand what the two libraries core and common are in the first place, or how are they divided. Well, I read in common.pdf that “Common provides advanced functionalities that are built on top of Core[…]” but now that I read a bit about Actions, I started to feel that some features like Actions have two independent implementations in the two libraries. I mean CActions are not (meaning do not extend or so) DockActions. So how should I relate the two implementations? Or maybe I shouldn’t?

Second question: Since I have a StackDockStation with possibly many DefaultSingleCDockables, some of the actions are common to all dockables in the station and hence must appear on all dockables. Again, would it be correct here to add the actions on every dockable separately, or is there a better practice to handle actions common among many dockables? Again, my question is inspired from section 4.2.2 Group actions in core.pdf; is there a similar concept in the common library?

Thanks a lot
Shant

Hi

I hope I can help a bit with the confusion.

  1. Core vs. Common: The Core layer is very, very generic. The idea behind Core is „it can do everything“. This is great if you want to implement some shiny, new, never seen application, and if you have a lot of time because understanding Core is not easy (you may have noticed :slight_smile: ).

But most applications lack all of these conditions. So say hello to Common: Common is a configuration for Core. It reduces complexity by removing some of the options Core offers, and by setting some reasonable default properties. Since the API of Core is big and complex, Common builds a layer around Core and hides many methods and interfaces you don’t need for a small application. The final goal would be, that you can implement an application with Common, and without ever needing to access anything from Core. That goal is not reached a 100%, but it’s true for 99%.

  1. It is completely ok if you add one CAction at a time to a DefaultSingleCDockable. It’s even the recommended way to handle actions in Common.

CActions build a layer atop of DockActions. They use DockActions to appear on the screen and to interact with the user, but from a developers perspective they replace the API from DockActions with their own API.

Internally each CAction references one DockAction, and these (hidden) DockActions are then grouped in a (hidden) DockActionSource. If you add a CAction to a DefaultSingleCDockable the framework internally adds a DockAction to this hidden DockActionSource.

The whole idea behind CActions is, that you do not need to use - or even know - DockActions, because using DockActions is much harder than using CActions. DockActions need to be grouped, and there are different actions doing nearly yet not exactly the same thing. The interface „DockAction“ itself also contains stuff you don’t really want to deal with. In contrast CActions only offer methods and properties a client actually needs, and each CAction has one clearly defined task.

If you really want, you can pierce through the Common layer, access the Core layer and add one or several groups of DockActions to a DefaultSingleCDockable. But for almost all tasks this is complet overkill.

  1. One CAction consumes almost no memory, just do share the icons between them, images require a lot space… :wink:
    This action, does it depend on a Dockable being child of a StackDockStation? Why? What does it do exactly? The Core layer offers some methods to deal with such cases, so this might might be one of the very few cases where accessing the Core layer and adding DockActions directly might be the easier solution. It is possible to mix CActions and DockActions…

Hi Beni,

Thanks a lot for your extremely quick reply. Yes, it helped me indeed to resolve my confusion, very informative and most of all comforting! :smiley:

Hence, to recap:

Concerning my first question: you said it is completely OK to use CActions and to add them one at a time to a DefaultSingleCDockable. Before I was worried about the existence of „DockActions“ that I thought I wasn’t using when using CActions, but now that you told me CActions will take care of (hidden) DockActions and (hidden) DockActionSource for me, all behind the scenes, I will gladly use those CActions without worries! Thanks :slight_smile:

As for my second question: I’m not sure I was able to illustrate it well in my previous post. Say I have three DefaultSingleCDockables, all inside a single StackDockStation. Each Dockable has actions (CActions) that are only relevant for it. So these CActions, should appear only when the concerned Dockable is the one displayed in the StackDockStation. I was already able to achieve this, using the way suggested for my first question (i.e. adding those CActions one at a time to their respective Dockables). Now, say in addition to those CActions, I have a single other CAction whose „action“ is relevant to all Dockables (like opening an input dialog, or anything general you can think about). My question is: is there a way to add this particular action „centrally“ (say at the level of the StackDockStation instead of the individual Dockable), so that every Dockable in the station offers that action?

BIG thanks,
Shant

You need to go down to the Core API and install an “ActionGuard”. The “ActionGuard” can add DockActions to any Dockable it wants. As you see in the skeleton example below, you can define a rule when the action should show up and when not. This rule is evaluated every time when the hierarchy (=its parents) of a Dockable changes.

Whether you use a CButton like in Solution 1, or directly a DockAction like in Solution 2, is up to you.


import javax.swing.JFrame;

import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.StackDockStation;
import bibliothek.gui.dock.action.ActionGuard;
import bibliothek.gui.dock.action.DefaultDockActionSource;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.LocationHint;
import bibliothek.gui.dock.action.LocationHint.Origin;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.action.CButton;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CommonDockable;

public class Test {
	public static void main( String[] args ){
		// setting up frame, controller, etc.
		JFrame frame = new JFrame();
		
		CControl control = new CControl( frame );
		
		// the guards should be installed early on, one of the two solutions is enough
		control.getController().addActionGuard( new Solution1() );
		control.getController().addActionGuard( new Solution2() );
		
		// and here we would continue adding our CustomDockables, building the layout
	}
	
	// The LocationHint tells where the action will show up, in this case it will preferre the
	// right side
	public static LocationHint getLocationHint(){
		return new LocationHint( LocationHint.ACTION_GUARD, LocationHint.LITTLE_RIGHT );
	}
	
	public static class CustomDockable extends DefaultSingleCDockable{
		// custom stuff ... 
	}
	
	// checks whether "dockable" is a "CustomDockable" that should show our special action
	public static CustomDockable validate( Dockable dockable ){
		// all DefaultCDockables reference a CommonDockable...
		if( dockable instanceof CommonDockable ){
			// .. and each CommonDockable knows its DefaultCDockable
			CDockable cdockable = ((CommonDockable)dockable).getDockable();
			
			// If the CDockable is of our custom type...
			if( cdockable instanceof CustomDockable ){
				// ... and at the right position ...
				if( validPosition( dockable )){
					// .. return it
					return (CustomDockable)cdockable;
				}
			}
		}
		return null;
	}
	
	public static boolean validPosition( Dockable dockable ){
		// ensure dockable has a StackDockStation as parent
		DockStation parent = dockable.getDockParent();
		if( parent == null ){
			return false;
		}
		if( parent instanceof StackDockStation ){
			return true;
		}
		if( parent.asDockable() == null ){
			return false;
		}
		return validPosition( parent.asDockable() );
	}
	
	// ********************** Solution 1
	// In this solution we just create a new button each time the guard is accessed - which may
	// happen quite often.
	public static class Solution1 implements ActionGuard{
		@Override
		public boolean react( Dockable dockable ){
			return validate( dockable ) != null;
		}

		@Override
		public DockActionSource getSource( Dockable dockable ){
			CustomDockable custom = (CustomDockable)((CommonDockable)dockable).getDockable();
			CustomButton1 button = new CustomButton1( custom );
			return new DefaultDockActionSource( getLocationHint(), button.intern() );
		}		
	}

	public static class CustomButton1 extends CButton{
		private CustomDockable dockable;
		
		public CustomButton1( CustomDockable dockable ){
			this.dockable = dockable;
		}
		
		@Override
		protected void action(){
			super.action();
			// called if the button is pressed
		}
	}
	
	// ********************** Solution 2
	// In this solution we reuse the same action over and over again. This is not possible with
	// an unmodified CButton (a CAction), hence we use SimpleButtonAction (a DockAction) instead.
	// Note: you can add a CButton to more than one dockable, but you will not know which one of the buttons that
	// show up was pressed.
	public static class Solution2 implements ActionGuard{
		private DockActionSource source;
		
		public Solution2(){
			// we need only one group of actions
			source = new DefaultDockActionSource( getLocationHint(), new CustomButton2() );
		}
		
		@Override
		public boolean react( Dockable dockable ){
			return validate( dockable ) != null;
		}

		@Override
		public DockActionSource getSource( Dockable dockable ){
			return source;
		}
	}
	
	// This is our custom DockAction, it's API is very similar to CButton
	public static class CustomButton2 extends SimpleButtonAction{
		@Override
		public void action( Dockable dockable ){
			super.action( dockable );
			CustomDockable custom = (CustomDockable)((CommonDockable)dockable).getDockable();
			
			// perform the action here
		}
	}
	
	// **** Solution 3: if you don't need to know on which Dockable the action was called,
	// you can also combine the two solutions. Use the CButton from the first solution,
	// but reuse it all the time like in the second solution
}

Hi Beni,

Thanks a lot for your help and support, and most of all for your dedicated time.
I’ll get back to you in case I still have something unclear.

I really appreciate it.

Thank you, :slight_smile:
Shant