Im using GWTP, working on their tab panel example. My issue is i need to demonstrate taking a search term and adding a new tab to the tab panel with the search results. So if i search 5 times, i have 5 tabs. easy enough, so i thought.
Gin is used extensively in GWTP. So my method to add a new tab, which should be something as simple as
tabPanel.addTab(new SearchDataGridView(), NameTokens.searchPage + 1);
gets confusing because of the constructor for the SearchDataGridView class
#Inject
SearchDataGridView(Binder uiBinder) {
employeeDataProvider = new ListDataProvider<Employee>();
initSearchGrid();
initWidget(uiBinder.createAndBindUi(this));
}
yea, i know im not passing the search term yet, im still trying to get the tab to open.
My gin config is this
bindPresenter(
SearchDataGridPresenter.class,
SearchDataGridPresenter.MyView.class,
SearchDataGridView.class,
SearchDataGridPresenter.MyProxy.class);
The gwtp gin config
#Override
protected void configure() {
// RestDispatchAsyncModule.Builder dispatchBuilder = new RestDispatchAsyncModule.Builder();
// install(dispatchBuilder.build());
// install(new RestDispatchAsyncModule.Builder().build());
install(new DefaultModule(DefaultPlaceManager.class));
install(new ApplicationModule());
bind(CurrentUser.class).in(Singleton.class);
bind(IsAdminGatekeeper.class).in(Singleton.class);
// DefaultPlaceManager Constants
bindConstant().annotatedWith(DefaultPlace.class).to(NameTokens.homeNewsPage);
bindConstant().annotatedWith(ErrorPlace.class).to(NameTokens.homeNewsPage);
bindConstant().annotatedWith(UnauthorizedPlace.class).to(NameTokens.homeNewsPage);
bindConstant().annotatedWith(Names.named("rest")).to("http://localhost/services");
// Google Analytics
// bindConstant().annotatedWith(GaAccount.class).to("UA-8319339-6");
// Load and inject CSS resources
bind(ResourceLoader.class).asEagerSingleton();
}
How do i pull this off?
thanks
Using comments below, sorta got it working. The problem is i can't get the contents of the tab to display. I added debugging code to the setInSlot method of my SearchContainer class and realized that whenever i click the search tab, it fires this setInSlot method, but its fired with my default page presenter listed as content.
#Override
public void setInSlot(Object slot, IsWidget content) {
Window.alert("fired setInSlot: " + slot.toString());
Window.alert("fired setInSlot: " + content.toString());
if (slot == ApplicationPresenter.TYPE_SetTabContent) {
tabPanel.setPanelContent(content);
} else {
super.setInSlot(slot, content);
}
}
Thats the method im using to get my info. Its weird that the tab appears properly, the jsonRPC calls that are built into the view are executed properly, it just doesn't display.
My main container presenter has its view and proxy identified by this
public class ApplicationPresenter
extends
TabContainerPresenter<ApplicationPresenter.MyView, ApplicationPresenter.MyProxy> implements
CurrentUserChangedHandler, AsyncCallStartHandler, AsyncCallFailHandler, AsyncCallSucceedHandler,
ApplicationUiHandler {
/**
* {#link ApplicationPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends Proxy<ApplicationPresenter> {
}
/**
* {#link ApplicationPresenter}'s view.
*/
public interface MyView extends TabView, HasUiHandlers<ApplicationUiHandler> {
void refreshTabs();
void setTopMessage(String string);
}
Could my issue be with my content type? Here is what i have defined for all my types
/**
* This will be the event sent to our "unknown" child presenters, in order for them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_RequestTabs = new Type<RequestTabsHandler>();
/**
* Fired by child proxie's when their tab content is changed.
*/
#ChangeTab
public static final Type<ChangeTabHandler> TYPE_ChangeTab = new Type<ChangeTabHandler>();
/**
* Use this in leaf presenters, inside their {#link #revealInParent} method.
*/
#ContentSlot
public static final Type<RevealContentHandler<?>> TYPE_SetTabContent = new Type<RevealContentHandler<?>>();
The proper way to do this is to use a PresenterWidget and a Provider.
In your ClientModule you define this:
bindPresenterWidget(SearchDataGridPresenter.class, SearchDataGridPresenter.MyView.class, SearchDataGridView.class);
In your Presenter that adds the SearchDataGridView you inject a Provider:
#Inject
public SeachContainerPresenter(final Provider<SearchDataGridPresenter> seachDataGridProvider) {
}
For each search you call addToSlot(TYPE_SetSearchDataGridContent,seachDataGridProvider.get()) in your SearchContainerPresenter.
In the SearchContainerView you override the addToSlot() method:
#Override
public void addToSlot(Object slot,Widget content) {
if (slot == SeachContainerPresenter.TYPE_SetSearchDataGridContent) {
tabPanel.addTab(content, NameTokens.searchPage + 1);
}
else {
super.addToSlot(slot,content);
}
}
Look in binding everything together. I think that you have to add the AsyncProvider of your presenter to yuor Ginjector.
AsyncProvider<SearchDataGridPresenter> getSearchDataGridPresenter();
You can put a SearchDataGridView getSearchDataGridView() method in the GIN Injector and call it in order to obtain the SearchDataGridView instance.
Related
In java-9 Skins made it into public scope, while Behaviors are left in the dark - nevertheless changed considerably, in now using InputMap for all input bindings.
CellBehaviorBase installs mouse bindings like:
InputMap.MouseMapping pressedMapping, releasedMapping;
addDefaultMapping(
pressedMapping = new InputMap.MouseMapping(MouseEvent.MOUSE_PRESSED, this::mousePressed),
releasedMapping = new InputMap.MouseMapping(MouseEvent.MOUSE_RELEASED, this::mouseReleased),
new InputMap.MouseMapping(MouseEvent.MOUSE_DRAGGED, this::mouseDragged)
);
A concrete XXSkin now installs the behavior privately:
final private BehaviorBase behavior;
public TableCellSkin(TableCell control) {
super(control);
behavior = new TableCellBehavior(control);
....
}
The requirement is replace the mousePressed behavior (in jdk9 context). The idea is to grab super's field reflectively, dispose all its mappings and install the custom behavior. For some reason that I don't understand, the old bindings are still active (though the old mappings are empty!) and are invoked before the new bindings.
Below is a runnable example to play with: the mapping to mousePressed is simply implemented to do nothing, particularly to not invoke super. To see the old bindings at work, I set a conditional debug breakpoint at CellBehaviorBase.mousePressed like (in Eclipse):
System.out.println("mousePressed super");
new RuntimeException("whoIsCalling: " + getNode().getClass()).printStackTrace();
return false;
Run a debug and click into any cell, then the output is:
mousePressed super
java.lang.RuntimeException: whoIsCalling: class de.swingempire.fx.scene.control.cell.TableCellBehaviorReplace$PlainCustomTableCell
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mousePressed(CellBehaviorBase.java:169)
at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
//... lots more of event dispatching
// until finally the output in my custom cell behavior
Feb. 02, 2016 3:14:02 NACHM. de.swingempire.fx.scene.control.cell.TableCellBehaviorReplace$PlainCustomTableCellBehavior mousePressed
INFORMATION: short-circuit super: Bulgarisch
I would expect to only see the very last part, that is the printout by my custom behavior. It feels like I'm somehow fundamentally off - but can't nail it. Ideas?
The runnable code (sorry for its length, most is boiler-plate, though):
public class TableCellBehaviorReplace extends Application {
private final ObservableList<Locale> locales =
FXCollections.observableArrayList(Locale.getAvailableLocales());
private Parent getContent() {
TableView<Locale> table = createLocaleTable();
BorderPane content = new BorderPane(table);
return content;
}
private TableView<Locale> createLocaleTable() {
TableView<Locale> table = new TableView<>(locales);
TableColumn<Locale, String> name = new TableColumn<>("Name");
name.setCellValueFactory(new PropertyValueFactory<>("displayName"));
name.setCellFactory(p -> new PlainCustomTableCell<>());
TableColumn<Locale, String> lang = new TableColumn<>("Language");
lang.setCellValueFactory(new PropertyValueFactory<>("displayLanguage"));
lang.setCellFactory(p -> new PlainCustomTableCell<>());
table.getColumns().addAll(name, lang);
return table;
}
/**
* Custom skin that installs custom Behavior. Note: this is dirty!
* Access super's behavior, dispose to get rid off its handlers, install
* custom behavior.
*/
public static class PlainCustomTableCellSkin<S, T> extends TableCellSkin<S, T> {
private BehaviorBase<?> replacedBehavior;
public PlainCustomTableCellSkin(TableCell<S, T> control) {
super(control);
replaceBehavior();
}
private void replaceBehavior() {
BehaviorBase<?> old = (BehaviorBase<?>) invokeGetField(TableCellSkin.class, this, "behavior");
old.dispose();
// at this point, InputMap mappings are empty:
// System.out.println("old mappings: " + old.getInputMap().getMappings().size());
replacedBehavior = new PlainCustomTableCellBehavior<>(getSkinnable());
}
#Override
public void dispose() {
replacedBehavior.dispose();
super.dispose();
}
}
/**
* Custom behavior that's meant to override basic handlers. Here: short-circuit
* mousePressed.
*/
public static class PlainCustomTableCellBehavior<S, T> extends TableCellBehavior<S, T> {
public PlainCustomTableCellBehavior(TableCell<S, T> control) {
super(control);
}
#Override
public void mousePressed(MouseEvent e) {
if (true) {
LOG.info("short-circuit super: " + getNode().getItem());
return;
}
super.mousePressed(e);
}
}
/**
* C&P of default tableCell in TableColumn. Extended to install custom
* skin.
*/
public static class PlainCustomTableCell<S, T> extends TableCell<S, T> {
public PlainCustomTableCell() {
}
#Override protected void updateItem(T item, boolean empty) {
if (item == getItem()) return;
super.updateItem(item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node)item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
#Override
protected Skin<?> createDefaultSkin() {
return new PlainCustomTableCellSkin<>(this);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent(), 400, 200));
primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
/**
* Reflectively access super field.
*/
public static Object invokeGetField(Class source, Object target, String name) {
try {
Field field = source.getDeclaredField(name);
field.setAccessible(true);
return field.get(target);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableCellBehaviorReplace.class.getName());
}
Edit
The suggestion inherit from the abstract skin XXSkinBase instead of the concrete XXSkin (then you are free to install whatever behavior you want, dude :-) is very reasonable and should be the first option. In the particular case of XX being TableCell, that's currently not possible, as the base class contains abstract package-private methods. Also, there are XX that don't have an abstract base (like f.i. ListCell).
Might be a bug in InputMap:
Digging into the sources I found some internal book-keeping (eventTypeMappings) parallel to mappings (these are the handlers). InputMap is listening to changes in mappings and updates the internal book-keeping on changes
mappings.addListener((ListChangeListener<Mapping<?>>) c -> {
while (c.next()) {
// TODO handle mapping removal
if (c.wasRemoved()) {
for (Mapping<?> mapping : c.getRemoved()) {
removeMapping(mapping);
}
}
// removeMapping
private void removeMapping(Mapping<?> mapping) {
// TODO
}
Meaning that the internal structure is never cleaned, particularly not when the mappings are removed in behavior.dispose(). When looking up eventHandlers - by inputMap.handle(e), see debug stacktrace shown in the question - the old handler is found in the internal book-keeping structure.
Joys of early experiments ... ;-)
At the end, a (very dirty, very hacky!) solution is to take over InputMap's job and force a cleanup of the internals:
private void replaceBehavior() {
BehaviorBase<?> old = (BehaviorBase<?>) invokeGetField(TableCellSkin.class, this, "behavior");
old.dispose();
cleanupInputMap(old.getInputMap());
// at this point, InputMap mappings are empty:
// System.out.println("old mappings: " + old.getInputMap().getMappings().size());
replacedBehavior = new PlainCustomTableCellBehavior<>(getSkinnable());
}
/**
* This is a hack around InputMap not cleaning up internals on removing mappings.
* We remove MousePressed/MouseReleased/MouseDragged mappings from the internal map.
* Beware: obviously this is dirty!
*
* #param inputMap
*/
private void cleanupInputMap(InputMap<?> inputMap) {
Map eventTypeMappings = (Map) invokeGetField(InputMap.class, inputMap, "eventTypeMappings");
eventTypeMappings.remove(MouseEvent.MOUSE_PRESSED);
eventTypeMappings.remove(MouseEvent.MOUSE_RELEASED);
eventTypeMappings.remove(MouseEvent.MOUSE_DRAGGED);
}
BTW: just in case anybody is wondering wtf - without, my hack around the missing commitOnFocusLost when editing a cell stopped working in java-9.
Try in PlainCustomTableCellSkin to inherit from the abstract class TableCellSkinBase rather than from TableCellSkin.
Then you can call the super constructor, which takes an TableCellBehaviorBase object as additional param.
Then you can save your time replacing it, by initializing it directly with the right one.
Just for more claryfication:
TableCellSkin extends TableCellSkinBase
TableCellBehavior extends TableCellBehaviorBase
One more thing. You need to also call super.init(tableCell) in your constructor.
Take the TableCellSkin class as reference.
I have a controller that extends window and implments IdSpace, AfterCompose.
But the function afterCompose never executes when the controller is initialized. A cant figure out what I am missing. My code for this part:
DataTemplateWindowController.java
public class DataTemplateWindowController extends Window implements IdSpace, AfterCompose {
...
public DataTemplateWindowController() {
Executions.createComponents("dataTemplate.zul", this, null);
Selectors.wireComponents(this, this, false);
Selectors.wireEventListeners(this, this);
}
#Override
public void afterCompose() {
Do something smart!!
}
}
And the initializetion.
HomeWindowController.java
public class HomeWindowController extends SelectorComposer<Component> {
...
#Wire
Window homeWindow;
DataTemplateWindowController fa2;
public void setDataTemplate() {
fa2 = new FA2WindowController();
fa2.setParent(homeWindow);
}
}
The page loads fine, but the afterCompose function never executes.
I know that i can just avoid implementing AfterCompose and then run the function fa2.afterCompose() after initialization but I expect AfterCompose to be able to do the job for me.
As you can see in the javadoc of AfterCompose (of org.zkoss.zk.ui.ext.AfterCompose) interface :
Implemented by a component if it wants to know when ZK loader created
it. If this interface is implemented, {#link #afterCompose} is called,
after ZK loader creates this component, all of its children, and
assigns all properties defined in the ZUML page. It is so-called
"compose".
So the method : "afterCompose" will never be call automatically by your own java code (the code in your method setDataTemplate() in your example). It will only be called if you use your component in a ZUL page.
And you can also see in the Javadoc of org.zkoss.zk.ui.ext.AfterCompose:
If it is created manually, it is caller's job to invoke {#link#afterCompose}.
If you don't need to set any properties or child in you afterCompose process, just don't use this interface and put your code in the constructor, otherwise, you will have to call it manually when you need it (usually in the doAfterCompose of your SelectorComposer) :
public class HomeWindowController extends SelectorComposer<Component> {
...
#Wire
Window homeWindow;
DataTemplateWindowController fa2;
#Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
setDataTemplate();
}
public void setDataTemplate() {
fa2 = new FA2WindowController();
fa2.setParent(homeWindow);
fa2.afterCompose();
}
}
I'm trying to add a custom HeaderResponseContainer in my wicket application. The tutorial looks quite simple (see Positioning of contributions), but when I add these lines and run the application I alwas get an IllegalStateException:
java.lang.IllegalStateException: No FilteringHeaderResponse is present in the request cycle. This may mean that you have not decorated the header response with a FilteringHeaderResponse. Simply calling the FilteringHeaderResponse constructor sets itself on the request cycle
at org.apache.wicket.markup.head.filter.FilteringHeaderResponse.get(FilteringHeaderResponse.java:165)
at org.apache.wicket.markup.head.filter.HeaderResponseContainer.onComponentTagBody(HeaderResponseContainer.java:64)
at org.apache.wicket.markup.html.panel.DefaultMarkupSourcingStrategy.onComponentTagBody(DefaultMarkupSourcingStrategy.java:71)
...
Yes, I already saw the note about FilteringHeaderResponse. But I am not sure where I should call the constructor. I already tried to add it in renderHead before calling response.render but I still get the same exception:
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
FilteringHeaderResponse resp = new FilteringHeaderResponse(response);
resp.render(new FilteredHeaderItem(..., "myKey"));
}
You can create a decorator that wraps responses in a FilteringHeaderResponse:
public final class FilteringHeaderResponseDecorator implements IHeaderResponseDecorator {
#Override
public IHeaderResponse decorate(IHeaderResponse response) {
return new FilteringHeaderResponse(response);
}
}
And that set it during application initialization:
Override
public void init() {
super.init();
setHeaderResponseDecorator(new FilteringHeaderResponseDecorator());
}
I just ran into this same problem and found that the Wicket In Action tutorial leaves out the part about setting up a custom IHeaderResponseDecorator in your main Wicket Application init. The Wicket guide has a more thorough example:
Apache Wicket User Guide - Put JavaScript inside page body
You need something like this in your wicket Application:
#Override
public void init()
{
setHeaderResponseDecorator(new JavaScriptToBucketResponseDecorator("myKey"));
}
/**
* Decorates an original IHeaderResponse and renders all javascript items
* (JavaScriptHeaderItem), to a specific container in the page.
*/
static class JavaScriptToBucketResponseDecorator implements IHeaderResponseDecorator
{
private String bucketName;
public JavaScriptToBucketResponseDecorator(String bucketName) {
this.bucketName = bucketName;
}
#Override
public IHeaderResponse decorate(IHeaderResponse response) {
return new JavaScriptFilteredIntoFooterHeaderResponse(response, bucketName);
}
}
I use ListEditor to allow editing list of chilren and I do everything just like I saw in some examples.The only difference from examples is that I want widgets editing children to be added as a tabs to some TabLayoutPanel.
The problem is that I want to give a header to this new tab and this header is not constant but depends on object being edited by newly created sub-editor (so let the header be child.getName()) which I don't know inside EditorSource#create() method.
ListEditor<ChildProxy, ChildPanel> children = ListEditor
.of(new EditorSource<ChildPanel>() {
#Override
public ChildPanel create(int index) {
ChildPanel tab = new ChildPanel();
//here's a problem, how I can get tabHeader here?
tabPanel.add(tab,tabHeader);
}
});
How can I set value-dependent headers to tabs created by create()? Any help/workaround would be greatly appreciated.
Does this approach work for you :
public class ChildrenEditor extends Composite implements
IsEditor<ListEditor<Child, ChildInTabEditor>> {
ListEditor<Child, ChildInTabEditor> editor;
public ChildrenEditor() {
initWidget(uiBinder.createAndBindUi(this));
editor = ListEditor.of(new ChildInTabEditorSource());
}
private class ChildInTabEditorSource extends EditorSource<ChildInTabEditor> {
public ChildInTabEditor create(int index) {
ChildInTabEditor tab = new ChildInTabEditor();
// here's the trick :
Child child = editor.getList().get(index);
tabPanel.add(tab,child.getTabTitle());
return tab;
}
}
#Override
public ListEditor<Child, ChildInTabEditor> asEditor() {
return editor;
}
}
ChildInTabEditor must be a Tab that implements Editor<Child> then too!
What worked for me was passing tabPanel and index to newly created ChildPanel() and make it ValueAwareEditor. Then on setValue() I was setting header on tabPanel reference at given index.
I am developing an Eclipse plug-in that has currently 2 views. In my first view I have a list of connections displayed in a TableViewer (name and connection status).In my second view I want to load the tables in a database (the connection). This loading will be done by clicking a menu item on a connection ("view details"). These tables will be displayed in a TreeViewer because they can also have children. I have tried to do it this way:
My View class:
public class DBTreeView extends ViewPart {
private TreeViewer treeViewer;
private Connection root = null;
public DBTreeView() {
Activator.getDefault().setDbTreeView(this);
}
public void createPartControl(Composite parent) {
treeViewer = new TreeViewer(parent);
treeViewer.setContentProvider(new DBTreeContentProvider());
treeViewer.setLabelProvider(new DBTreeLabelProvider());
}
public void setInput(Connection conn){
root = conn;
treeViewer.setInput(root);
treeViewer.refresh();
}
}
I made a setInput method that is called from the action registered with the menu item in the connections view with the currently selected connection as argument:
MViewContentsAction class:
public void run(){
selectedConnection = Activator.getDefault().getConnectionsView().getSelectedConnection();
Activator.getDefault().getDbTreeView().setInput(selectedConnection);
}
In my ContentProvider class:
public Object[] getChildren(Object arg0) {
if (arg0 instanceof Connection){
return ((Connection) arg0).getTables().toArray();
}
return EMPTY_ARRAY;
}
where EMPTY_ARRAY is an...empty array
The problem I'm facing is that when in debug mode, this piece of code is not executed somehow:
Activator.getDefault().getDbTreeView().setInput(selectedConnection);
And also nothing happens in the tree view when clicking the menu item. Any ideas?
Thank you
Huh. Ok, what you're doing here is.. not really the right way. What you should be doing is registering your TableViewer as a selection provider.
getSite().setSelectionProvider(tableViewer);
Then, define a selection listener and add it to the view with the tree viewer like this:
ISelectionListener listener = new ISelectionListener() {
public void selectionChanged(IWorkbenchPart part, ISelection sel) {
if (!(sel instanceof IStructuredSelection))
return;
IStructuredSelection ss = (IStructuredSelection) sel;
// rest of your code dealing with checking whether selection is what is
//expected and if it is, setting it as an input to
//your tree viewer
}
};
public void createPartControl(Composite parent) {
getSite().getPage().addSelectionListener(listener);
}
Now your tree viewer's input will be changed according to what is selected in the table viewer (btw, don't forget to call treeviewer.refresh() after you set new input).
See an example here.