You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@isis.apache.org by Brian K <ha...@gmail.com> on 2019/11/15 19:52:07 UTC

Boolean property wicket display

Hi,

For 1.17 and 2.0, Boolean properties are displayed in the Wicket viewer
something like this:

sample        [X] Yes

Clicking on the checkbox itself is disabled, while clicking on the text
next to it brings up an inline edit prompt (without the text) that allows
you to click on the checkbox.

Question:  How can I have the property display with a simple checkbox,
similar to how it displays when editing inline?  Is there a setting
somewhere or would this require coding a new wicket component?

Thank you!
Brian

Re: Boolean property wicket display

Posted by Jeroen Houtevelts <je...@gmail.com>.
 UNSCRIBE

Op ma 27 jan. 2020 om 21:37 schreef Brian K <ha...@gmail.com>:

> Thanks for the input!  I took the approach of expanding the super class and
> associated interface.  I took a short step to start, which was to eliminate
> the redundant "yes" and "no" to the checkbox display [2].  I copied the
> code that was on that link to override the onChange() method of the
> CheckBoxX.  This works perfectly, unless you change the value again before
> clicking the Submit button.  In this case, it displays another inline edit
> form on top of the existing one.
>
> In trying to tamp this down, I looked for a property to query that would
> suppress this behavior.   Since first line of the code in question [1] sets
> scalarModel to edit mode, I looked at scalarModel.isEditMode().  This
> property seems to never be unset.  It seems to stay in edit mode even after
> submitting the value.
>
> Is this a bug?  I'd have to use a custom version of PropertyEditForm to put
> this back in.
>
> To make this exactly one-click, I will look deeper into what
> PropertyEditForm is doing in onUpdate and bring it up to this new class.
>
> Thanks,
> Brian
>
>
> [1]
> ```
> @Override
> protected void onChange(Boolean value, AjaxRequestTarget target) {
> scalarModel.toEditMode();
>
> switchFormForInlinePrompt(target);
>
> getComponentForRegular().setVisible(false);
> scalarIfRegularInlinePromptForm.setVisible(true);
>
> target.add(scalarTypeContainer);
> }
> ```
>
> [2] BooleanPanel.java:
> ```
> public  class BooleanPanel extends PanelAbstract<ScalarModel> implements
> BooleanModelSubscriber  {
>
>     private static final long serialVersionUID = 1L;
>
>     protected static final String ID_SCALAR_TYPE_CONTAINER =
> "scalarTypeContainer";
>
>     protected static final String ID_SCALAR_IF_COMPACT = "scalarIfCompact";
>     protected static final String ID_SCALAR_IF_REGULAR = "scalarIfRegular";
>     protected static final String ID_SCALAR_NAME = "scalarName";
>     protected static final String ID_SCALAR_VALUE = "scalarValue";
>
>
>
>     /**
>      * as per {@link #inlinePromptLink}
>      */
>     protected static final String ID_SCALAR_VALUE_INLINE_PROMPT_LINK =
> "scalarValueInlinePromptLink";
>     protected static final String ID_SCALAR_VALUE_INLINE_PROMPT_LABEL =
> "scalarValueInlinePromptLabel";
>
>     /**
>      * as per {@link #scalarIfRegularInlinePromptForm}.
>      */
>     public static final String ID_SCALAR_IF_REGULAR_INLINE_PROMPT_FORM =
> "scalarIfRegularInlinePromptForm";
>
>
>     private static final String ID_EDIT_PROPERTY = "editProperty";
>     private static final String ID_FEEDBACK = "feedback";
>     private static final String ID_ASSOCIATED_ACTION_LINKS_BELOW =
> "associatedActionLinksBelow";
>     private static final String ID_ASSOCIATED_ACTION_LINKS_RIGHT =
> "associatedActionLinksRight";
>
>     public static class InlinePromptConfigLocal {
>         private final boolean supported;
>         private final Component componentToHideIfAny;
>
>         public static InlinePromptConfigLocal supported() {
>             return new InlinePromptConfigLocal(true, null);
>         }
>
>         public static InlinePromptConfigLocal notSupported() {
>             return new InlinePromptConfigLocal(false, null);
>         }
>
>         public static InlinePromptConfigLocal supportedAndHide(final
> Component componentToHideIfAny) {
>             return new InlinePromptConfigLocal(true, componentToHideIfAny);
>         }
>
>         private InlinePromptConfigLocal(final boolean supported, final
> Component componentToHideIfAny) {
>             this.supported = supported;
>             this.componentToHideIfAny = componentToHideIfAny;
>         }
>
>         boolean isSupported() {
>             return supported;
>         }
>
>         Component getComponentToHideIfAny() {
>             return componentToHideIfAny;
>         }
>     }
>
>     // ///////////////////////////////////////////////////////////////////
>
>     protected final ScalarModel scalarModel;
>
>     private Component scalarIfCompact;
>     private MarkupContainer scalarIfRegular;
>
>     private WebMarkupContainer scalarTypeContainer;
>
>     /**
>      * Populated
>      * Used by most subclasses ({@link BooleanPanel}, {@link
> ReferencePanel}, {@link ValueChoicesSelect2Panel}) but not all ({@link
> IsisBlobOrClobPanelAbstract}, {@link BooleanPanel})
>      */
>     private WebMarkupContainer scalarIfRegularInlinePromptForm;
>
>     WebMarkupContainer inlinePromptLink;
>
>     private CheckBoxX checkBox;
>
>     public BooleanPanel(final String id, final ScalarModel scalarModel) {
>         super(id, scalarModel);
>         this.scalarModel = scalarModel;
>     }
>
>
>     protected MarkupContainer createComponentForRegular() {
>         final String name = getModel().getName();
>
>         checkBox = createCheckBox(ID_SCALAR_VALUE,
> CheckBoxXConfig.Sizes.lg);
>
>         checkBox.setLabel(Model.of(name));
>
>         final FormGroup scalarIfRegularFormGroup = new
> FormGroup(ID_SCALAR_IF_REGULAR, checkBox);
>         scalarIfRegularFormGroup.add(checkBox);
>         if(getModel().isRequired() && getModel().isEnabled()) {
>             scalarIfRegularFormGroup.add(new
> CssClassAppender("mandatory"));
>         }
>
>         final String labelCaption =
> getRenderingLocal().getLabelCaption(checkBox);
>         final Label scalarName = createScalarName(ID_SCALAR_NAME,
> labelCaption);
>
>         scalarIfRegularFormGroup.add(scalarName);
>
>         final String describedAs = getModel().getDescribedAs();
>         if(describedAs != null) {
>             scalarIfRegularFormGroup.add(new AttributeModifier("title",
> Model.of(describedAs)));
>         }
>
>
>         return scalarIfRegularFormGroup;
>     }
>     protected Component getScalarValueComponent() {
>         return checkBox;
>     }
>     /**
>      * Mandatory hook method to build the component to render the model
> when in
>      * {@link RenderingLocal#COMPACT compact} format.
>      */
>     protected Component createComponentForCompact() {
>         return createCheckBox(ID_SCALAR_IF_COMPACT,
> CheckBoxXConfig.Sizes.sm
> );
>     }
>
>
>     protected InlinePromptConfigLocal getInlinePromptConfigLocal() {
>         return InlinePromptConfigLocal.supportedAndHide(
>                 scalarModel.getMode() == EntityModel.Mode.EDIT ||
> scalarModel.hasActionWithInlineAsIfEdit() ||
>                 scalarModel.getKind() == ScalarModel.Kind.PARAMETER
>                         ? this.checkBox
>                         : null
>         );
>     }
>
>     protected IModel<String> obtainInlinePromptModel() {
>         return new Model<String>() {
>
>             private static final long serialVersionUID = 1L;
>
>             @Override public String getObject() {
>                 final ScalarModel model = getModel();
>                 final ObjectAdapter adapter = model.getObject();
>                 final Boolean bool = adapter != null ? (Boolean)
> adapter.getObject() : null;
>                 return bool == null? "(not set)" : bool ? "Yes" : "No";
>             }
>         };
>     }
>     private CheckBoxX createCheckBox(final String id, final
> CheckBoxXConfig.Sizes size) {
>
>         final CheckBoxXConfig config = configFor(getModel().isRequired(),
> size);
>
>         final CheckBoxX checkBox = new CheckBoxX(id, new Model<Boolean>() {
>             private static final long serialVersionUID = 1L;
>
>             @Override
>             public Boolean getObject() {
>                 final ScalarModel model = getModel();
>                 final ObjectAdapter adapter = model.getObject();
>                 return adapter != null? (Boolean) adapter.getObject():
> null;
>             }
>
>             @Override
>             public void setObject(final Boolean object) {
>                 final ObjectAdapter adapter =
> getPersistenceSession().adapterFor(object);
>                 getModel().setObject(adapter);
>             }
>         }) {
>             /**
> *
> */
> private static final long serialVersionUID = 1L;
>
> @Override
>             public CheckBoxXConfig getConfig() {
>                 return config;
>             }
>
>
> @Override
> protected void onChange(Boolean value, AjaxRequestTarget target) {
> scalarModel.toEditMode();
>
> switchFormForInlinePrompt(target);
>
> getComponentForRegular().setVisible(false);
> scalarIfRegularInlinePromptForm.setVisible(true);
>
> target.add(scalarTypeContainer);
> }
>
>             @Override protected void onComponentTag(final ComponentTag tag)
> {
>                 super.onComponentTag(tag);
>                 //
>                 // this is a horrid hack to allow the space bar to work as
> a way of toggling the checkbox.
>                 // this hack works for 1.5.4 of the JS plugin (
> https://github.com/kartik-v/bootstrap-checkbox-x)
>                 //
>                 // the problem is that the "change" event is not fired for
> a keystroke; instead the callback in the
>                 // JS code (
>
> https://github.com/kartik-v/bootstrap-checkbox-x/blob/v1.5.4/js/checkbox-x.js#L70
> )
>                 // calls self.change().  This in turn calls
> validateCheckbox().  In that method it is possible to
>                 // cause the "change" event to fire, but only if the input
> element is NOT type="checkbox".
>                 // (
>
> https://github.com/kartik-v/bootstrap-checkbox-x/blob/v1.5.4/js/checkbox-x.js#L132
> )
>                 //
>                 // It's not possible to simply change the associated markup
> to input type='xx' because it falls foul
>                 // of a check in super.onComponentTag(tag).  So instead we
> let that through then hack the tag
>                 // afterwards:
>                 //
>                 tag.put("type", "xx");
>             }
>         };
>         checkBox.setOutputMarkupId(true);
>         checkBox.setEnabled(true); // will be enabled before rendering if
>                                     // required
>
>         // must prime the underlying model if this is a primitive boolean
>         final ObjectSpecification objectSpecification =
> getModel().getTypeOfSpecification();
>         if(objectSpecification.getFullIdentifier().equals("boolean")) {
>             if(getModel().getObject() == null) {
>
> getModel().setObject(getPersistenceSession().adapterFor(false));
>             }
>         }
>
>         return checkBox;
>     }
>
>     private static CheckBoxXConfig configFor(final boolean required, final
> CheckBoxXConfig.Sizes size) {
>         final CheckBoxXConfig config = new CheckBoxXConfig() {
>             /**
> *
> */
> private static final long serialVersionUID = 1L;
>
> {
>                 // so can tab to the checkbox
>                 // not part of the API, so have to use this object
> initializer
>                 put(new Key<String>("tabindex"), "0");
>             }
>         };
>         return config
>                 .withSize(size)
>                 .withEnclosedLabel(false)
>                 .withIconChecked("<i class='fa fa-fw fa-check'></i>")
>                 .withIconNull("<i class='fa fa-fw fa-square'></i>")
>                 .withThreeState(!required);
>     }
>     protected void onInitializeWhenEnabled() {
>         checkBox.setEnabled(true);
>     }
>
>     protected void onInitializeWhenViewMode() {
>         checkBox.setEnabled(true);
>     }
>
>     protected void onInitializeWhenDisabled(final String disableReason) {
>         checkBox.setEnabled(false);
>     }
>
>     @Override
>     public String getVariation() {
>         String variation;
>         final LabelAtFacet facet = getModel().getFacet(LabelAtFacet.class);
>         if (facet != null && LabelPosition.RIGHT == facet.label()) {
>             variation = "labelRightPosition";
>         } else {
>             variation = super.getVariation();
>         }
>         return variation;
>     }
>
>     protected String getScalarPanelType() {
>         return "booleanPanel";
>     }
>
>
>
>     // ///////////////////////////////////////////////////////////////////
>
>
>     @Override
>     protected void onInitialize() {
>         super.onInitialize();
>
>         buildGuiAndCallHooks();
>
>         setOutputMarkupId(true);
>
>     }
>
>     private void buildGuiAndCallHooks() {
>
>         try {
>             buildGui();
>         } catch (ConcurrencyException ex) {
>             //
>             // this has to be here because it's the first method called
> when editing a property
>             // on a potentially stale model.
>             //
>             // there is similar code for invoking actions (ActionLink)
>             //
>
>
> IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getMessageBroker().addMessage(ex.getMessage());
>             final ObjectAdapter parentAdapter =
> getModel().getParentEntityModel().load();
>             throw new RestartResponseException(new
> EntityPage(parentAdapter));
>         }
>
>         final ScalarModel scalarModel = getModel();
>
>         if (scalarModel.isViewMode()) {
>             onInitializeWhenViewMode();
>         } else {
>             final String disableReasonIfAny =
> scalarModel.whetherDisabled();
>             if (disableReasonIfAny != null) {
>                 onInitializeWhenDisabled(disableReasonIfAny);
>             } else {
>                 onInitializeWhenEnabled();
>             }
>         }
>     }
>
>
>
>
>
>     /**
>      * Builds GUI lazily prior to first render.
>      *
>      * <p>
>      * This design allows the panel to be configured first.
>      *
>      * @see #onBeforeRender()
>      */
>     private void buildGui() {
>
>         scalarTypeContainer = new
> WebMarkupContainer(ID_SCALAR_TYPE_CONTAINER);
>         scalarTypeContainer.setOutputMarkupId(true);
>         scalarTypeContainer.add(new
> CssClassAppender(Model.of(getScalarPanelType())));
>         addOrReplace(scalarTypeContainer);
>
>         this.scalarIfCompact = createComponentForCompact();
>         this.scalarIfRegular = createComponentForRegular();
>         scalarIfRegular.setOutputMarkupId(true);
>
>         scalarTypeContainer.addOrReplace(scalarIfCompact, scalarIfRegular);
>
>         List<LinkAndLabel> linkAndLabels =
>
> LinkAndLabelUtil.asActionLinksForAssociation(this.scalarModel,
> getDeploymentCategory());
>
>         final InlinePromptConfigLocal inlinePromptConfig =
> getInlinePromptConfigLocal();
> //        if(inlinePromptConfig.isSupported()) { // private method in
> static class is not available
>         if(true) {
>
>             this.scalarIfRegularInlinePromptForm =
> createInlinePromptForm();
>
> scalarTypeContainer.addOrReplace(scalarIfRegularInlinePromptForm);
> //            inlinePromptLink = createInlinePromptLink();
> //            scalarIfRegular.add(inlinePromptLink);
>
>             // even if this particular scalarModel (property) is not
> configured for inline edits,
>             // it's possible that one of the associated actions is.  Thus
> we set the prompt context
>             scalarModel.setInlinePromptContext(
>                     new InlinePromptContext(
>                             getComponentForRegular(),
>                             scalarIfRegularInlinePromptForm,
> scalarTypeContainer));
>
>             // start off assuming that neither the property nor any of the
> associated actions
>             // are using inline prompts
> //            Component componentToHideIfAny = inlinePromptLink;
>
>             // check if one of the associated actions is configured to use
> an inline form "as if edit"
>             final LinkAndLabel linkAndLabelAsIfEdit =
> inlineAsIfEditIfAny(linkAndLabels);
>
>             if (this.scalarModel.getPromptStyle().isInline() &&
> scalarModel.canEnterEditMode()) {
>                 // we configure the prompt link if _this_ property is
> configured for inline edits...
> //                configureInlinePromptLinkCallback(inlinePromptLink);
> //                componentToHideIfAny =
> inlinePromptConfig.getComponentToHideIfAny();
>
>             } else {
>
>                 // not editable property, but maybe one of the actions is.
>                 if(linkAndLabelAsIfEdit != null) {
>
>                     scalarModel.setHasActionWithInlineAsIfEdit(true);
>
>                     // safe to do this, the inlineAsEditIfAny(...) method
> checks for us
>                     final ActionLink actionLinkInlineAsIfEdit =
> (ActionLink) linkAndLabelAsIfEdit.getLink();
>
>                     if(actionLinkInlineAsIfEdit.isVisible() &&
> actionLinkInlineAsIfEdit.isEnabled()) {
>                         configureInlinePromptLinkCallback(inlinePromptLink,
> actionLinkInlineAsIfEdit);
> //                        componentToHideIfAny =
> inlinePromptConfig.getComponentToHideIfAny();
>                     }
>                 }
>             }
>
>             if(linkAndLabelAsIfEdit != null) {
>                 // irrespective of whether the property is itself editable,
> if the action is annotated as
>                 // INLINE_AS_IF_EDIT then we never render it as an action
>                 linkAndLabels = Lists.newArrayList(linkAndLabels);
>                 linkAndLabels.remove(linkAndLabelAsIfEdit);
>             }
>
> //            if(componentToHideIfAny != null) {
> //                componentToHideIfAny.setVisibilityAllowed(false);
> //            }
>         }
>         if(scalarModel.getKind() == ScalarModel.Kind.PROPERTY &&
>            scalarModel.getMode() == EntityModel.Mode.VIEW     &&
>                 (scalarModel.getPromptStyle().isDialog() ||
> !scalarModel.canEnterEditMode())) {
>             getScalarValueComponent().add(new AttributeAppender("tabindex",
> "-1"));
>         }
>
>         addPositioningCssTo(scalarIfRegular, linkAndLabels);
>         addActionLinksBelowAndRight(scalarIfRegular, linkAndLabels);
>
>         addEditPropertyTo(scalarIfRegular);
>         addFeedbackOnlyTo(scalarIfRegular, getScalarValueComponent());
>
>         getRenderingLocal().buildGui(this);
>         addCssFromMetaModel();
>
>         notifyOnChange(this);
>         addFormComponentBehaviourToUpdateSubscribers();
>
>     }
>
>     /**
>      * @return the first {@link ActionLink} (if any) configured with a
>      * {@link LinkAndLabel#getPromptStyle() prompt style} of {@link
> PromptStyle#INLINE_AS_IF_EDIT}.
>      */
>     private static LinkAndLabel inlineAsIfEditIfAny(final
> List<LinkAndLabel> linkAndLabels) {
>         for (LinkAndLabel linkAndLabel : linkAndLabels) {
>             AbstractLink link = linkAndLabel.getLink();
>             if(link instanceof ActionLink) {
>
>                 PromptStyle promptStyle = linkAndLabel.getPromptStyle();
>
>                 if(promptStyle.isInlineAsIfEdit()) {
>                     return linkAndLabel;
>                 }
>             }
>         }
>         return null;
>     }
>
>
>
>     private void addCssFromMetaModel() {
>         final String cssForMetaModel = getModel().getCssClass();
>         if (!Strings.isNullOrEmpty(cssForMetaModel)) {
>             CssClassAppender.appendCssClassTo(this,
> CssClassAppender.asCssStyle(cssForMetaModel));
>         }
>
>         ScalarModel model = getModel();
>         final CssClassFacet facet = model.getFacet(CssClassFacet.class);
>         if(facet != null) {
>
>             final ObjectAdapter parentAdapter =
>
>
> model.getParentEntityModel().load(AdapterManager.ConcurrencyChecking.NO_CHECK);
>
>             final String cssClass = facet.cssClass(parentAdapter);
>             CssClassAppender.appendCssClassTo(this, cssClass);
>         }
>     }
>
>
>     // //////////////////////////////////////
>
>     /**
>      * Each component is now responsible for determining if it should be
> visible or not.
>      *
>      * <p>
>      * Unlike the constructor and <tt>onInitialize</tt>, which are called
> only once, the <tt>onConfigure</tt> callback
>      * is called multiple times, just prior to <tt>onBeforeRendering</tt>.
> It is therefore the correct place for
>      * components to set up their visibility/enablement.
>      * </p>
>      *
>      */
>     @Override
>     protected void onConfigure() {
>
>         final ScalarModel scalarModel = getModel();
>
>         final boolean hidden = scalarModel.whetherHidden();
>         setVisibilityAllowed(!hidden);
>
>         super.onConfigure();
>     }
>
>
>     // //////////////////////////////////////
>
>
>     static class ScalarUpdatingBehavior extends
> AjaxFormComponentUpdatingBehavior {
>         private static final long serialVersionUID = 1L;
>
>         private final BooleanPanel scalarPanel;
>
>         private ScalarUpdatingBehavior(final BooleanPanel scalarPanel) {
>             super("change");
>             this.scalarPanel = scalarPanel;
>         }
>
>         @Override
>         protected void onUpdate(AjaxRequestTarget target) {
>             for (BooleanModelSubscriber subscriber :
> scalarPanel.subscribers) {
>                 subscriber.onUpdate(target, scalarPanel);
>             }
>         }
>
>         @Override
>         protected void onError(AjaxRequestTarget target, RuntimeException
> e) {
>             super.onError(target, e);
>             for (BooleanModelSubscriber subscriber :
> scalarPanel.subscribers) {
>                 subscriber.onError(target, scalarPanel);
>             }
>         }
>     }
>
>     private final List<BooleanModelSubscriber> subscribers =
> Lists.newArrayList();
>
>     public void notifyOnChange(final BooleanModelSubscriber subscriber) {
>         subscribers.add(subscriber);
>     }
>
>     private void addFormComponentBehaviourToUpdateSubscribers() {
>         Component scalarValueComponent = getScalarValueComponent();
>         if(scalarValueComponent == null) {
>             return;
>         }
>         for (Behavior b :
> scalarValueComponent.getBehaviors(ScalarUpdatingBehavior.class)) {
>             scalarValueComponent.remove(b);
>         }
>         scalarValueComponent.add(new ScalarUpdatingBehavior(this));
>     }
>
>     // //////////////////////////////////////
>
>     public void onUpdate(
>             final AjaxRequestTarget target, final BooleanPanel scalarPanel)
> {
>
>         if(getModel().getKind() == ScalarModel.Kind.PARAMETER) {
>             target.appendJavaScript(
>
> String.format("Wicket.Event.publish(Isis.Topic.FOCUS_FIRST_PARAMETER,
> '%s')", getMarkupId()));
>         }
>     }
>
>
>     public void onError(
>             final AjaxRequestTarget target, final BooleanPanel scalarPanel)
> {
>
>     }
>
>
>     // ///////////////////////////////////////////////////////////////////
>
>
>     public enum RenderingLocal {
>         /**
>          * Does not show labels, eg for use in tables
>          */
>         COMPACT {
>             @Override
>             public String getLabelCaption(final LabeledWebMarkupContainer
> labeledContainer) {
>                 return "";
>             }
>
>             @Override
>             public void buildGui(final BooleanPanel panel) {
>                 panel.getComponentForRegular().setVisible(false);
>             }
>
>         },
>         /**
>          * Does show labels, eg for use in forms.
>          */
>         REGULAR {
>             @Override
>             public String getLabelCaption(final LabeledWebMarkupContainer
> labeledContainer) {
>                 return labeledContainer.getLabel().getObject();
>             }
>
>             @Override
>             public void buildGui(final BooleanPanel panel) {
>                 panel.scalarIfCompact.setVisible(false);
>             }
>
>         };
>
>         public abstract String getLabelCaption(LabeledWebMarkupContainer
> labeledContainer);
>
>         public abstract void buildGui(BooleanPanel panel);
>
>         private static RenderingLocal
> renderingFor(EntityModel.RenderingHint renderingHint) {
>             return renderingHint.isRegular()? RenderingLocal.REGULAR
> :RenderingLocal.COMPACT;
>         }
>     }
>
>     protected RenderingLocal getRenderingLocal() {
>         return RenderingLocal.renderingFor(scalarModel.getRenderingHint());
>     }
>
>     // ///////////////////////////////////////////////////////////////////
>
>     protected Component getComponentForRegular() {
>         return scalarIfRegular;
>     }
>
>     protected Label createScalarName(final String id, final String
> labelCaption) {
>         final Label scalarName = new Label(id, labelCaption);
>         final ScalarModel model = getModel();
>         if(model.isRequired() && model.isEnabled()) {
>             final String label =
> scalarName.getDefaultModelObjectAsString();
>             if(!Strings.isNullOrEmpty(label)) {
>                 scalarName.add(new CssClassAppender("mandatory"));
>             }
>         }
>         NamedFacet namedFacet = model.getFacet(NamedFacet.class);
>         if (namedFacet != null) {
>             scalarName.setEscapeModelStrings(namedFacet.escaped());
>         }
>         return scalarName;
>     }
>
>     /**
>      * Returns a container holding an empty form.  This can be switched out
> using {@link #switchFormForInlinePrompt(AjaxRequestTarget)}.
>      */
>     private WebMarkupContainer createInlinePromptForm() {
>
>         // (placeholder initially, create dynamically when needed -
> otherwise infinite loop because form references regular)
>
>         WebMarkupContainer scalarIfRegularInlinePromptForm =
>                 new WebMarkupContainer(
> ID_SCALAR_IF_REGULAR_INLINE_PROMPT_FORM);
>         scalarIfRegularInlinePromptForm.setOutputMarkupId(true);
>         scalarIfRegularInlinePromptForm.setVisible(false);
>
>         return scalarIfRegularInlinePromptForm;
>     }
>
>     private WebMarkupContainer createInlinePromptLink() {
>         final IModel<String> inlinePromptModel = obtainInlinePromptModel();
>         if(inlinePromptModel == null) {
>             throw new IllegalStateException(this.getClass().getName() + ":
> obtainInlinePromptModel() returning null is not compatible with
> supportsInlinePrompt() returning true ");
>         }
>
> //        final WebMarkupContainer inlinePromptLink = new
> WebMarkupContainer(ID_SCALAR_VALUE_INLINE_PROMPT_LINK);
> //        inlinePromptLink.setOutputMarkupId(true);
> //
> //        configureInlinePromptLink(inlinePromptLink);
>
> //        final Component editInlineLinkLabel =
> createInlinePromptComponent(ID_SCALAR_VALUE_INLINE_PROMPT_LABEL,
> //                inlinePromptModel
> //        );
> //        inlinePromptLink.add(editInlineLinkLabel);
>
>         return inlinePromptLink;
>     }
>
>     protected void configureInlinePromptLink(final WebMarkupContainer
> inlinePromptLink) {
>         final String append = obtainInlinePromptLinkCssIfAny();
>         if(append != null) {
>             inlinePromptLink.add(new CssClassAppender(append));
>         }
>     }
>
>     protected String obtainInlinePromptLinkCssIfAny() {
>         return "form-control input-sm";
>     }
>
>     protected Component createInlinePromptComponent(
>             final String id, final IModel<String> inlinePromptModel) {
>         return new Label(id, inlinePromptModel) {
>             @Override protected void onComponentTag(final ComponentTag tag)
> {
>                 super.onComponentTag(tag);
>                 tag.put("tabindex","-1");
>             }
>         };
>     }
>
>     // ///////////////////////////////////////////////////////////////////
>
>
>     private void configureInlinePromptLinkCallback(final WebMarkupContainer
> inlinePromptLink) {
>
>         inlinePromptLink.add(new AjaxEventBehavior("click") {
>             @Override
>             protected void onEvent(final AjaxRequestTarget target) {
>
>                 scalarModel.toEditMode();
>
>                 switchFormForInlinePrompt(target);
>
>                 getComponentForRegular().setVisible(false);
>                 scalarIfRegularInlinePromptForm.setVisible(true);
>
>                 target.add(scalarTypeContainer);
>             }
>
>             @Override
>             public boolean isEnabled(final Component component) {
>                 return true;
>             }
>         });
>     }
>
>     private void configureInlinePromptLinkCallback(
>             final WebMarkupContainer inlinePromptLink,
>             final ActionLink actionLink) {
>
>         inlinePromptLink.add(new AjaxEventBehavior("click") {
>             @Override
>             protected void onEvent(final AjaxRequestTarget target) {
>                 actionLink.onClick(target);
>             }
>
>             @Override
>             public boolean isEnabled(final Component component) {
>                 return true;
>             }
>         });
>     }
>
>     private void switchFormForInlinePrompt(final AjaxRequestTarget target)
> {
>         scalarIfRegularInlinePromptForm = (PropertyEditFormPanel)
> getComponentFactoryRegistry().addOrReplaceComponent(
>                 scalarTypeContainer,
> ID_SCALAR_IF_REGULAR_INLINE_PROMPT_FORM, ComponentType.PROPERTY_EDIT_FORM,
> scalarModel);
>
>         onSwitchFormForInlinePrompt(scalarIfRegularInlinePromptForm,
> target);
>     }
>
>     /**
>      * Optional hook.
>      */
>     protected void onSwitchFormForInlinePrompt(
>             final WebMarkupContainer inlinePromptForm,
>             final AjaxRequestTarget target) {
>
>     }
>
>
>     // ///////////////////////////////////////////////////////////////////
>
>     protected void addEditPropertyTo(
>             final MarkupContainer scalarIfRegularFormGroup) {
>
>         if(  scalarModel.canEnterEditMode() &&
>             (scalarModel.getPromptStyle().isDialog() ||
>             !getInlinePromptConfigLocal().isSupported())) {
>
>             final WebMarkupContainer editProperty = new
> WebMarkupContainer(ID_EDIT_PROPERTY);
>             editProperty.setOutputMarkupId(true);
>             scalarIfRegularFormGroup.addOrReplace(editProperty);
>
>             editProperty.add(new AjaxEventBehavior("click") {
>                 protected void onEvent(AjaxRequestTarget target) {
>
>                     final ActionPrompt prompt = ActionPromptProvider.Util
>                             .getFrom(BooleanPanel.this).getActionPrompt();
>
>                     PropertyEditPromptHeaderPanel titlePanel = new
> PropertyEditPromptHeaderPanel(prompt.getTitleId(),
>                             BooleanPanel.this.scalarModel);
>
>                     final PropertyEditPanel propertyEditPanel =
>                             (PropertyEditPanel)
> getComponentFactoryRegistry().createComponent(
>                                     ComponentType.PROPERTY_EDIT_PROMPT,
> prompt.getContentId(),
>                                     BooleanPanel.this.scalarModel);
>
>                     propertyEditPanel.setShowHeader(false);
>
>                     prompt.setTitle(titlePanel, target);
>                     prompt.setPanel(propertyEditPanel, target);
>                     prompt.showPrompt(target);
>
>                 }
>             });
>         } else {
>             Components.permanentlyHide(scalarIfRegularFormGroup,
> ID_EDIT_PROPERTY);
>         }
>
>     }
>
>
>     private void addFeedbackOnlyTo(final MarkupContainer markupContainer,
> final Component component) {
>         markupContainer.addOrReplace(new NotificationPanel(ID_FEEDBACK,
> component, new ComponentFeedbackMessageFilter(component)));
>     }
>
>     private void addActionLinksBelowAndRight(
>             final MarkupContainer labelIfRegular,
>             final List<LinkAndLabel> linkAndLabels) {
>         final List<LinkAndLabel> linksBelow =
> LinkAndLabel.positioned(linkAndLabels, ActionLayout.Position.BELOW);
>         AdditionalLinksPanel.addAdditionalLinks(labelIfRegular,
> ID_ASSOCIATED_ACTION_LINKS_BELOW, linksBelow,
> AdditionalLinksPanel.Style.INLINE_LIST);
>
>         final List<LinkAndLabel> linksRight =
> LinkAndLabel.positioned(linkAndLabels, ActionLayout.Position.RIGHT);
>         AdditionalLinksPanel.addAdditionalLinks(labelIfRegular,
> ID_ASSOCIATED_ACTION_LINKS_RIGHT, linksRight,
> AdditionalLinksPanel.Style.DROPDOWN);
>     }
>
>     /**
>      * Applies the {@literal @}{@link LabelAtFacet} and also CSS based on
>      * whether any of the associated actions have {@literal @}{@link
> ActionLayout layout} positioned to
>      * the {@link ActionLayout.Position#RIGHT right}.
>      *
>      * @param markupContainer The form group element
>      * @param actionLinks
>      */
>     private void addPositioningCssTo(
>             final MarkupContainer markupContainer,
>             final List<LinkAndLabel> actionLinks) {
>         CssClassAppender.appendCssClassTo(markupContainer,
> determinePropParamLayoutCss(getModel()));
>         CssClassAppender.appendCssClassTo(markupContainer,
> determineActionLayoutPositioningCss(actionLinks));
>     }
>
>     private static String determinePropParamLayoutCss(ScalarModel model) {
>         final LabelAtFacet facet = model.getFacet(LabelAtFacet.class);
>         if (facet != null) {
>             switch (facet.label()) {
>             case LEFT:
>                 return "label-left";
>             case RIGHT:
>                 return "label-right";
>             case NONE:
>                 return "label-none";
>             case TOP:
>                 return "label-top";
>             }
>         }
>         return "label-left";
>     }
>
>     private static String
> determineActionLayoutPositioningCss(List<LinkAndLabel> entityActionLinks) {
>         boolean actionsPositionedOnRight =
> hasActionsPositionedOn(entityActionLinks, ActionLayout.Position.RIGHT);
>         return actionsPositionedOnRight ? "actions-right" : null;
>     }
>
>     private static boolean hasActionsPositionedOn(final List<LinkAndLabel>
> entityActionLinks, final ActionLayout.Position position) {
>         for (LinkAndLabel entityActionLink : entityActionLinks) {
>             if(entityActionLink.getPosition() == position) {
>                 return true;
>             }
>         }
>         return false;
>     }
>
>     // ///////////////////////////////////////////////////////////////////
>
>     /**
>      * Repaints this panel of just some of its children
>      *
>      * @param target The Ajax request handler
>      */
>     public void repaint(AjaxRequestTarget target) {
>         target.add(this);
>     }
>
>
>     /**
>      * null stub required by super.  @see InlinePromptConfigLocal.   the
> version in super has private methods that cannot be accessed from this
> class.
>      */
> protected InlinePromptConfig getInlinePromptConfig() {
> return null;
> }
>
>
>
> }
> ```
>
>
>
> On Sun, Jan 5, 2020 at 11:52 PM Dan Haywood <da...@haywood-associates.co.uk>
> wrote:
>
> > HI Brian,
> >
> > I think we exchanged a few thoughts on our Slack channel [1], but let me
> > reply more thoroughly here.  Within...
> >
> > [1] https://cwiki.apache.org/confluence/display/ISIS/Signing+up+to+Slack
> >
> > On Sun, 29 Dec 2019 at 19:04, Brian K <ha...@gmail.com> wrote:
> >
> > > Hi Dan,
> > >
> > > I followed the wicket viewer docs [1] and was able to call my custom
> > > component after doing the following.
> > >
> > > I implemented a copy of BooleanPanel, BooleanPanelFactory,
> > > I created a subclass of ComponentFactoryRegistrar and
> > IsisWicketApplication
> > > I referenced my new IsisWicketApplication in the webapp module's
> web.xml.
> > > Overriding the init() method per the docs, I can see it is being
> called,
> > > but the newIsisWicketModule() method is not being called.
> > >
> > > Turns out, newIsisWicketModule(isisConfiguration) was being called.  I
> am
> > > overriding that method now, and just one more discrepancy:
> > > The docs sample code has the extension of ComponentRegistrarDefault add
> > the
> > > default componentFactories before adding the custom one.  This used the
> > > default BooleanPanelFactory instead of my custom one.  When I added the
> > > custom one first then it used the custom one.
> > >
> >
> > The wiring together of this stuff changes in v2 (though the classes and
> > their responsibilities does not), so if it's ok with you I'm not going to
> > spend any time now improving/fixing the way that this stuff is setup in
> v1.
> >
> >
> >
> > > I put in a trivial html change to verify my component is being used.
> > I'll
> > > be adding the useful parts next.  I'm using the ScalarModel's cssClass
> to
> > > determine whether appliesTo() will select the custom component.  Is
> > there a
> > > better way to identify places to use it, like another annotation or
> > > @PropertyLayout entry?
> > >
> > >
> > The purpose of this new version of the BooleanPanel is for a smoother UI,
> > that is for the user to just click the checkbox rather than having to
> enter
> > edit mode.
> > Do we think that any given Isis application that there might be some
> object
> > properties which use the original UI, and other object properties which
> use
> > the smooth UI?  I think the answer to that is "no" - it would be rather
> > confusing for the interaction mechanism to vary between pages... users
> > would want consistency.
> >
> > Therefore, I think that there should probably be some sort of global flag
> > under "isis.viewer.wicket.xxx" which determines which style of
> BooleanPanel
> > to render.
> >
> > Does that make sense?
> >
> > Cheers
> > Dan
> >
> >
> >
> >
> > > Thanks,
> > > Brian
> > >
> > > [1]
> > >
> > >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > >
> > > On Sun, Dec 1, 2019 at 8:45 AM Dan Haywood <
> dan@haywood-associates.co.uk
> > >
> > > wrote:
> > >
> > > > Hi Brian,
> > > >
> > > > Apols for the delay.
> > > >
> > > > First port of call - did you look at the docs on replacing page
> > elements?
> > > > [1] .
> > > >
> > > >
> > > > [1]
> > > >
> > > >
> > >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > > > (nb: this is for v1.16.2, it seems the v1.17.0 doc formatting is
> > slightly
> > > > messed up, but the content didn't change between these two versions,
> I
> > > > think).
> > > > <
> > > >
> > >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > > > >
> > > >
> > > >
> > > > On Sun, 17 Nov 2019 at 05:36, Brian K <ha...@gmail.com>
> > wrote:
> > > >
> > > > > Yes that would be great!  I'm seeing how much I can do with the
> > wicket
> > > > > viewer to make a fluid user experience.  The Wicket project itself
> > > seems
> > > > to
> > > > > indicate a lot is possible.
> > > > >
> > > > > -Brian
> > > > >
> > > > > On Sat, Nov 16, 2019, 1:43 AM Dan Haywood <
> > > dan@haywood-associates.co.uk>
> > > > > wrote:
> > > > >
> > > > > > Hi Brian,
> > > > > > I agree, it is a bit clunky. I'm afraid it would require a new
> > > > component
> > > > > to
> > > > > > be written. Contributions welcome, I can provide some guidance if
> > > > you're
> > > > > > willing.
> > > > > > Cheers,
> > > > > > Dan.
> > > > > >
> > > > > > On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com>
> > > wrote:
> > > > > >
> > > > > > > Hi,
> > > > > > >
> > > > > > > For 1.17 and 2.0, Boolean properties are displayed in the
> Wicket
> > > > viewer
> > > > > > > something like this:
> > > > > > >
> > > > > > > sample        [X] Yes
> > > > > > >
> > > > > > > Clicking on the checkbox itself is disabled, while clicking on
> > the
> > > > text
> > > > > > > next to it brings up an inline edit prompt (without the text)
> > that
> > > > > allows
> > > > > > > you to click on the checkbox.
> > > > > > >
> > > > > > > Question:  How can I have the property display with a simple
> > > > checkbox,
> > > > > > > similar to how it displays when editing inline?  Is there a
> > setting
> > > > > > > somewhere or would this require coding a new wicket component?
> > > > > > >
> > > > > > > Thank you!
> > > > > > > Brian
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
>

Re: Boolean property wicket display

Posted by Brian K <ha...@gmail.com>.
Thanks for the input!  I took the approach of expanding the super class and
associated interface.  I took a short step to start, which was to eliminate
the redundant "yes" and "no" to the checkbox display [2].  I copied the
code that was on that link to override the onChange() method of the
CheckBoxX.  This works perfectly, unless you change the value again before
clicking the Submit button.  In this case, it displays another inline edit
form on top of the existing one.

In trying to tamp this down, I looked for a property to query that would
suppress this behavior.   Since first line of the code in question [1] sets
scalarModel to edit mode, I looked at scalarModel.isEditMode().  This
property seems to never be unset.  It seems to stay in edit mode even after
submitting the value.

Is this a bug?  I'd have to use a custom version of PropertyEditForm to put
this back in.

To make this exactly one-click, I will look deeper into what
PropertyEditForm is doing in onUpdate and bring it up to this new class.

Thanks,
Brian


[1]
```
@Override
protected void onChange(Boolean value, AjaxRequestTarget target) {
scalarModel.toEditMode();

switchFormForInlinePrompt(target);

getComponentForRegular().setVisible(false);
scalarIfRegularInlinePromptForm.setVisible(true);

target.add(scalarTypeContainer);
}
```

[2] BooleanPanel.java:
```
public  class BooleanPanel extends PanelAbstract<ScalarModel> implements
BooleanModelSubscriber  {

    private static final long serialVersionUID = 1L;

    protected static final String ID_SCALAR_TYPE_CONTAINER =
"scalarTypeContainer";

    protected static final String ID_SCALAR_IF_COMPACT = "scalarIfCompact";
    protected static final String ID_SCALAR_IF_REGULAR = "scalarIfRegular";
    protected static final String ID_SCALAR_NAME = "scalarName";
    protected static final String ID_SCALAR_VALUE = "scalarValue";



    /**
     * as per {@link #inlinePromptLink}
     */
    protected static final String ID_SCALAR_VALUE_INLINE_PROMPT_LINK =
"scalarValueInlinePromptLink";
    protected static final String ID_SCALAR_VALUE_INLINE_PROMPT_LABEL =
"scalarValueInlinePromptLabel";

    /**
     * as per {@link #scalarIfRegularInlinePromptForm}.
     */
    public static final String ID_SCALAR_IF_REGULAR_INLINE_PROMPT_FORM =
"scalarIfRegularInlinePromptForm";


    private static final String ID_EDIT_PROPERTY = "editProperty";
    private static final String ID_FEEDBACK = "feedback";
    private static final String ID_ASSOCIATED_ACTION_LINKS_BELOW =
"associatedActionLinksBelow";
    private static final String ID_ASSOCIATED_ACTION_LINKS_RIGHT =
"associatedActionLinksRight";

    public static class InlinePromptConfigLocal {
        private final boolean supported;
        private final Component componentToHideIfAny;

        public static InlinePromptConfigLocal supported() {
            return new InlinePromptConfigLocal(true, null);
        }

        public static InlinePromptConfigLocal notSupported() {
            return new InlinePromptConfigLocal(false, null);
        }

        public static InlinePromptConfigLocal supportedAndHide(final
Component componentToHideIfAny) {
            return new InlinePromptConfigLocal(true, componentToHideIfAny);
        }

        private InlinePromptConfigLocal(final boolean supported, final
Component componentToHideIfAny) {
            this.supported = supported;
            this.componentToHideIfAny = componentToHideIfAny;
        }

        boolean isSupported() {
            return supported;
        }

        Component getComponentToHideIfAny() {
            return componentToHideIfAny;
        }
    }

    // ///////////////////////////////////////////////////////////////////

    protected final ScalarModel scalarModel;

    private Component scalarIfCompact;
    private MarkupContainer scalarIfRegular;

    private WebMarkupContainer scalarTypeContainer;

    /**
     * Populated
     * Used by most subclasses ({@link BooleanPanel}, {@link
ReferencePanel}, {@link ValueChoicesSelect2Panel}) but not all ({@link
IsisBlobOrClobPanelAbstract}, {@link BooleanPanel})
     */
    private WebMarkupContainer scalarIfRegularInlinePromptForm;

    WebMarkupContainer inlinePromptLink;

    private CheckBoxX checkBox;

    public BooleanPanel(final String id, final ScalarModel scalarModel) {
        super(id, scalarModel);
        this.scalarModel = scalarModel;
    }


    protected MarkupContainer createComponentForRegular() {
        final String name = getModel().getName();

        checkBox = createCheckBox(ID_SCALAR_VALUE,
CheckBoxXConfig.Sizes.lg);

        checkBox.setLabel(Model.of(name));

        final FormGroup scalarIfRegularFormGroup = new
FormGroup(ID_SCALAR_IF_REGULAR, checkBox);
        scalarIfRegularFormGroup.add(checkBox);
        if(getModel().isRequired() && getModel().isEnabled()) {
            scalarIfRegularFormGroup.add(new CssClassAppender("mandatory"));
        }

        final String labelCaption =
getRenderingLocal().getLabelCaption(checkBox);
        final Label scalarName = createScalarName(ID_SCALAR_NAME,
labelCaption);

        scalarIfRegularFormGroup.add(scalarName);

        final String describedAs = getModel().getDescribedAs();
        if(describedAs != null) {
            scalarIfRegularFormGroup.add(new AttributeModifier("title",
Model.of(describedAs)));
        }


        return scalarIfRegularFormGroup;
    }
    protected Component getScalarValueComponent() {
        return checkBox;
    }
    /**
     * Mandatory hook method to build the component to render the model
when in
     * {@link RenderingLocal#COMPACT compact} format.
     */
    protected Component createComponentForCompact() {
        return createCheckBox(ID_SCALAR_IF_COMPACT, CheckBoxXConfig.Sizes.sm
);
    }


    protected InlinePromptConfigLocal getInlinePromptConfigLocal() {
        return InlinePromptConfigLocal.supportedAndHide(
                scalarModel.getMode() == EntityModel.Mode.EDIT ||
scalarModel.hasActionWithInlineAsIfEdit() ||
                scalarModel.getKind() == ScalarModel.Kind.PARAMETER
                        ? this.checkBox
                        : null
        );
    }

    protected IModel<String> obtainInlinePromptModel() {
        return new Model<String>() {

            private static final long serialVersionUID = 1L;

            @Override public String getObject() {
                final ScalarModel model = getModel();
                final ObjectAdapter adapter = model.getObject();
                final Boolean bool = adapter != null ? (Boolean)
adapter.getObject() : null;
                return bool == null? "(not set)" : bool ? "Yes" : "No";
            }
        };
    }
    private CheckBoxX createCheckBox(final String id, final
CheckBoxXConfig.Sizes size) {

        final CheckBoxXConfig config = configFor(getModel().isRequired(),
size);

        final CheckBoxX checkBox = new CheckBoxX(id, new Model<Boolean>() {
            private static final long serialVersionUID = 1L;

            @Override
            public Boolean getObject() {
                final ScalarModel model = getModel();
                final ObjectAdapter adapter = model.getObject();
                return adapter != null? (Boolean) adapter.getObject(): null;
            }

            @Override
            public void setObject(final Boolean object) {
                final ObjectAdapter adapter =
getPersistenceSession().adapterFor(object);
                getModel().setObject(adapter);
            }
        }) {
            /**
*
*/
private static final long serialVersionUID = 1L;

@Override
            public CheckBoxXConfig getConfig() {
                return config;
            }


@Override
protected void onChange(Boolean value, AjaxRequestTarget target) {
scalarModel.toEditMode();

switchFormForInlinePrompt(target);

getComponentForRegular().setVisible(false);
scalarIfRegularInlinePromptForm.setVisible(true);

target.add(scalarTypeContainer);
}

            @Override protected void onComponentTag(final ComponentTag tag)
{
                super.onComponentTag(tag);
                //
                // this is a horrid hack to allow the space bar to work as
a way of toggling the checkbox.
                // this hack works for 1.5.4 of the JS plugin (
https://github.com/kartik-v/bootstrap-checkbox-x)
                //
                // the problem is that the "change" event is not fired for
a keystroke; instead the callback in the
                // JS code (
https://github.com/kartik-v/bootstrap-checkbox-x/blob/v1.5.4/js/checkbox-x.js#L70
)
                // calls self.change().  This in turn calls
validateCheckbox().  In that method it is possible to
                // cause the "change" event to fire, but only if the input
element is NOT type="checkbox".
                // (
https://github.com/kartik-v/bootstrap-checkbox-x/blob/v1.5.4/js/checkbox-x.js#L132
)
                //
                // It's not possible to simply change the associated markup
to input type='xx' because it falls foul
                // of a check in super.onComponentTag(tag).  So instead we
let that through then hack the tag
                // afterwards:
                //
                tag.put("type", "xx");
            }
        };
        checkBox.setOutputMarkupId(true);
        checkBox.setEnabled(true); // will be enabled before rendering if
                                    // required

        // must prime the underlying model if this is a primitive boolean
        final ObjectSpecification objectSpecification =
getModel().getTypeOfSpecification();
        if(objectSpecification.getFullIdentifier().equals("boolean")) {
            if(getModel().getObject() == null) {

getModel().setObject(getPersistenceSession().adapterFor(false));
            }
        }

        return checkBox;
    }

    private static CheckBoxXConfig configFor(final boolean required, final
CheckBoxXConfig.Sizes size) {
        final CheckBoxXConfig config = new CheckBoxXConfig() {
            /**
*
*/
private static final long serialVersionUID = 1L;

{
                // so can tab to the checkbox
                // not part of the API, so have to use this object
initializer
                put(new Key<String>("tabindex"), "0");
            }
        };
        return config
                .withSize(size)
                .withEnclosedLabel(false)
                .withIconChecked("<i class='fa fa-fw fa-check'></i>")
                .withIconNull("<i class='fa fa-fw fa-square'></i>")
                .withThreeState(!required);
    }
    protected void onInitializeWhenEnabled() {
        checkBox.setEnabled(true);
    }

    protected void onInitializeWhenViewMode() {
        checkBox.setEnabled(true);
    }

    protected void onInitializeWhenDisabled(final String disableReason) {
        checkBox.setEnabled(false);
    }

    @Override
    public String getVariation() {
        String variation;
        final LabelAtFacet facet = getModel().getFacet(LabelAtFacet.class);
        if (facet != null && LabelPosition.RIGHT == facet.label()) {
            variation = "labelRightPosition";
        } else {
            variation = super.getVariation();
        }
        return variation;
    }

    protected String getScalarPanelType() {
        return "booleanPanel";
    }



    // ///////////////////////////////////////////////////////////////////


    @Override
    protected void onInitialize() {
        super.onInitialize();

        buildGuiAndCallHooks();

        setOutputMarkupId(true);

    }

    private void buildGuiAndCallHooks() {

        try {
            buildGui();
        } catch (ConcurrencyException ex) {
            //
            // this has to be here because it's the first method called
when editing a property
            // on a potentially stale model.
            //
            // there is similar code for invoking actions (ActionLink)
            //

IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getMessageBroker().addMessage(ex.getMessage());
            final ObjectAdapter parentAdapter =
getModel().getParentEntityModel().load();
            throw new RestartResponseException(new
EntityPage(parentAdapter));
        }

        final ScalarModel scalarModel = getModel();

        if (scalarModel.isViewMode()) {
            onInitializeWhenViewMode();
        } else {
            final String disableReasonIfAny = scalarModel.whetherDisabled();
            if (disableReasonIfAny != null) {
                onInitializeWhenDisabled(disableReasonIfAny);
            } else {
                onInitializeWhenEnabled();
            }
        }
    }





    /**
     * Builds GUI lazily prior to first render.
     *
     * <p>
     * This design allows the panel to be configured first.
     *
     * @see #onBeforeRender()
     */
    private void buildGui() {

        scalarTypeContainer = new
WebMarkupContainer(ID_SCALAR_TYPE_CONTAINER);
        scalarTypeContainer.setOutputMarkupId(true);
        scalarTypeContainer.add(new
CssClassAppender(Model.of(getScalarPanelType())));
        addOrReplace(scalarTypeContainer);

        this.scalarIfCompact = createComponentForCompact();
        this.scalarIfRegular = createComponentForRegular();
        scalarIfRegular.setOutputMarkupId(true);

        scalarTypeContainer.addOrReplace(scalarIfCompact, scalarIfRegular);

        List<LinkAndLabel> linkAndLabels =

LinkAndLabelUtil.asActionLinksForAssociation(this.scalarModel,
getDeploymentCategory());

        final InlinePromptConfigLocal inlinePromptConfig =
getInlinePromptConfigLocal();
//        if(inlinePromptConfig.isSupported()) { // private method in
static class is not available
        if(true) {

            this.scalarIfRegularInlinePromptForm = createInlinePromptForm();

scalarTypeContainer.addOrReplace(scalarIfRegularInlinePromptForm);
//            inlinePromptLink = createInlinePromptLink();
//            scalarIfRegular.add(inlinePromptLink);

            // even if this particular scalarModel (property) is not
configured for inline edits,
            // it's possible that one of the associated actions is.  Thus
we set the prompt context
            scalarModel.setInlinePromptContext(
                    new InlinePromptContext(
                            getComponentForRegular(),
                            scalarIfRegularInlinePromptForm,
scalarTypeContainer));

            // start off assuming that neither the property nor any of the
associated actions
            // are using inline prompts
//            Component componentToHideIfAny = inlinePromptLink;

            // check if one of the associated actions is configured to use
an inline form "as if edit"
            final LinkAndLabel linkAndLabelAsIfEdit =
inlineAsIfEditIfAny(linkAndLabels);

            if (this.scalarModel.getPromptStyle().isInline() &&
scalarModel.canEnterEditMode()) {
                // we configure the prompt link if _this_ property is
configured for inline edits...
//                configureInlinePromptLinkCallback(inlinePromptLink);
//                componentToHideIfAny =
inlinePromptConfig.getComponentToHideIfAny();

            } else {

                // not editable property, but maybe one of the actions is.
                if(linkAndLabelAsIfEdit != null) {

                    scalarModel.setHasActionWithInlineAsIfEdit(true);

                    // safe to do this, the inlineAsEditIfAny(...) method
checks for us
                    final ActionLink actionLinkInlineAsIfEdit =
(ActionLink) linkAndLabelAsIfEdit.getLink();

                    if(actionLinkInlineAsIfEdit.isVisible() &&
actionLinkInlineAsIfEdit.isEnabled()) {
                        configureInlinePromptLinkCallback(inlinePromptLink,
actionLinkInlineAsIfEdit);
//                        componentToHideIfAny =
inlinePromptConfig.getComponentToHideIfAny();
                    }
                }
            }

            if(linkAndLabelAsIfEdit != null) {
                // irrespective of whether the property is itself editable,
if the action is annotated as
                // INLINE_AS_IF_EDIT then we never render it as an action
                linkAndLabels = Lists.newArrayList(linkAndLabels);
                linkAndLabels.remove(linkAndLabelAsIfEdit);
            }

//            if(componentToHideIfAny != null) {
//                componentToHideIfAny.setVisibilityAllowed(false);
//            }
        }
        if(scalarModel.getKind() == ScalarModel.Kind.PROPERTY &&
           scalarModel.getMode() == EntityModel.Mode.VIEW     &&
                (scalarModel.getPromptStyle().isDialog() ||
!scalarModel.canEnterEditMode())) {
            getScalarValueComponent().add(new AttributeAppender("tabindex",
"-1"));
        }

        addPositioningCssTo(scalarIfRegular, linkAndLabels);
        addActionLinksBelowAndRight(scalarIfRegular, linkAndLabels);

        addEditPropertyTo(scalarIfRegular);
        addFeedbackOnlyTo(scalarIfRegular, getScalarValueComponent());

        getRenderingLocal().buildGui(this);
        addCssFromMetaModel();

        notifyOnChange(this);
        addFormComponentBehaviourToUpdateSubscribers();

    }

    /**
     * @return the first {@link ActionLink} (if any) configured with a
     * {@link LinkAndLabel#getPromptStyle() prompt style} of {@link
PromptStyle#INLINE_AS_IF_EDIT}.
     */
    private static LinkAndLabel inlineAsIfEditIfAny(final
List<LinkAndLabel> linkAndLabels) {
        for (LinkAndLabel linkAndLabel : linkAndLabels) {
            AbstractLink link = linkAndLabel.getLink();
            if(link instanceof ActionLink) {

                PromptStyle promptStyle = linkAndLabel.getPromptStyle();

                if(promptStyle.isInlineAsIfEdit()) {
                    return linkAndLabel;
                }
            }
        }
        return null;
    }



    private void addCssFromMetaModel() {
        final String cssForMetaModel = getModel().getCssClass();
        if (!Strings.isNullOrEmpty(cssForMetaModel)) {
            CssClassAppender.appendCssClassTo(this,
CssClassAppender.asCssStyle(cssForMetaModel));
        }

        ScalarModel model = getModel();
        final CssClassFacet facet = model.getFacet(CssClassFacet.class);
        if(facet != null) {

            final ObjectAdapter parentAdapter =

model.getParentEntityModel().load(AdapterManager.ConcurrencyChecking.NO_CHECK);

            final String cssClass = facet.cssClass(parentAdapter);
            CssClassAppender.appendCssClassTo(this, cssClass);
        }
    }


    // //////////////////////////////////////

    /**
     * Each component is now responsible for determining if it should be
visible or not.
     *
     * <p>
     * Unlike the constructor and <tt>onInitialize</tt>, which are called
only once, the <tt>onConfigure</tt> callback
     * is called multiple times, just prior to <tt>onBeforeRendering</tt>.
It is therefore the correct place for
     * components to set up their visibility/enablement.
     * </p>
     *
     */
    @Override
    protected void onConfigure() {

        final ScalarModel scalarModel = getModel();

        final boolean hidden = scalarModel.whetherHidden();
        setVisibilityAllowed(!hidden);

        super.onConfigure();
    }


    // //////////////////////////////////////


    static class ScalarUpdatingBehavior extends
AjaxFormComponentUpdatingBehavior {
        private static final long serialVersionUID = 1L;

        private final BooleanPanel scalarPanel;

        private ScalarUpdatingBehavior(final BooleanPanel scalarPanel) {
            super("change");
            this.scalarPanel = scalarPanel;
        }

        @Override
        protected void onUpdate(AjaxRequestTarget target) {
            for (BooleanModelSubscriber subscriber :
scalarPanel.subscribers) {
                subscriber.onUpdate(target, scalarPanel);
            }
        }

        @Override
        protected void onError(AjaxRequestTarget target, RuntimeException
e) {
            super.onError(target, e);
            for (BooleanModelSubscriber subscriber :
scalarPanel.subscribers) {
                subscriber.onError(target, scalarPanel);
            }
        }
    }

    private final List<BooleanModelSubscriber> subscribers =
Lists.newArrayList();

    public void notifyOnChange(final BooleanModelSubscriber subscriber) {
        subscribers.add(subscriber);
    }

    private void addFormComponentBehaviourToUpdateSubscribers() {
        Component scalarValueComponent = getScalarValueComponent();
        if(scalarValueComponent == null) {
            return;
        }
        for (Behavior b :
scalarValueComponent.getBehaviors(ScalarUpdatingBehavior.class)) {
            scalarValueComponent.remove(b);
        }
        scalarValueComponent.add(new ScalarUpdatingBehavior(this));
    }

    // //////////////////////////////////////

    public void onUpdate(
            final AjaxRequestTarget target, final BooleanPanel scalarPanel)
{

        if(getModel().getKind() == ScalarModel.Kind.PARAMETER) {
            target.appendJavaScript(

String.format("Wicket.Event.publish(Isis.Topic.FOCUS_FIRST_PARAMETER,
'%s')", getMarkupId()));
        }
    }


    public void onError(
            final AjaxRequestTarget target, final BooleanPanel scalarPanel)
{

    }


    // ///////////////////////////////////////////////////////////////////


    public enum RenderingLocal {
        /**
         * Does not show labels, eg for use in tables
         */
        COMPACT {
            @Override
            public String getLabelCaption(final LabeledWebMarkupContainer
labeledContainer) {
                return "";
            }

            @Override
            public void buildGui(final BooleanPanel panel) {
                panel.getComponentForRegular().setVisible(false);
            }

        },
        /**
         * Does show labels, eg for use in forms.
         */
        REGULAR {
            @Override
            public String getLabelCaption(final LabeledWebMarkupContainer
labeledContainer) {
                return labeledContainer.getLabel().getObject();
            }

            @Override
            public void buildGui(final BooleanPanel panel) {
                panel.scalarIfCompact.setVisible(false);
            }

        };

        public abstract String getLabelCaption(LabeledWebMarkupContainer
labeledContainer);

        public abstract void buildGui(BooleanPanel panel);

        private static RenderingLocal
renderingFor(EntityModel.RenderingHint renderingHint) {
            return renderingHint.isRegular()? RenderingLocal.REGULAR
:RenderingLocal.COMPACT;
        }
    }

    protected RenderingLocal getRenderingLocal() {
        return RenderingLocal.renderingFor(scalarModel.getRenderingHint());
    }

    // ///////////////////////////////////////////////////////////////////

    protected Component getComponentForRegular() {
        return scalarIfRegular;
    }

    protected Label createScalarName(final String id, final String
labelCaption) {
        final Label scalarName = new Label(id, labelCaption);
        final ScalarModel model = getModel();
        if(model.isRequired() && model.isEnabled()) {
            final String label = scalarName.getDefaultModelObjectAsString();
            if(!Strings.isNullOrEmpty(label)) {
                scalarName.add(new CssClassAppender("mandatory"));
            }
        }
        NamedFacet namedFacet = model.getFacet(NamedFacet.class);
        if (namedFacet != null) {
            scalarName.setEscapeModelStrings(namedFacet.escaped());
        }
        return scalarName;
    }

    /**
     * Returns a container holding an empty form.  This can be switched out
using {@link #switchFormForInlinePrompt(AjaxRequestTarget)}.
     */
    private WebMarkupContainer createInlinePromptForm() {

        // (placeholder initially, create dynamically when needed -
otherwise infinite loop because form references regular)

        WebMarkupContainer scalarIfRegularInlinePromptForm =
                new WebMarkupContainer(
ID_SCALAR_IF_REGULAR_INLINE_PROMPT_FORM);
        scalarIfRegularInlinePromptForm.setOutputMarkupId(true);
        scalarIfRegularInlinePromptForm.setVisible(false);

        return scalarIfRegularInlinePromptForm;
    }

    private WebMarkupContainer createInlinePromptLink() {
        final IModel<String> inlinePromptModel = obtainInlinePromptModel();
        if(inlinePromptModel == null) {
            throw new IllegalStateException(this.getClass().getName() + ":
obtainInlinePromptModel() returning null is not compatible with
supportsInlinePrompt() returning true ");
        }

//        final WebMarkupContainer inlinePromptLink = new
WebMarkupContainer(ID_SCALAR_VALUE_INLINE_PROMPT_LINK);
//        inlinePromptLink.setOutputMarkupId(true);
//
//        configureInlinePromptLink(inlinePromptLink);

//        final Component editInlineLinkLabel =
createInlinePromptComponent(ID_SCALAR_VALUE_INLINE_PROMPT_LABEL,
//                inlinePromptModel
//        );
//        inlinePromptLink.add(editInlineLinkLabel);

        return inlinePromptLink;
    }

    protected void configureInlinePromptLink(final WebMarkupContainer
inlinePromptLink) {
        final String append = obtainInlinePromptLinkCssIfAny();
        if(append != null) {
            inlinePromptLink.add(new CssClassAppender(append));
        }
    }

    protected String obtainInlinePromptLinkCssIfAny() {
        return "form-control input-sm";
    }

    protected Component createInlinePromptComponent(
            final String id, final IModel<String> inlinePromptModel) {
        return new Label(id, inlinePromptModel) {
            @Override protected void onComponentTag(final ComponentTag tag)
{
                super.onComponentTag(tag);
                tag.put("tabindex","-1");
            }
        };
    }

    // ///////////////////////////////////////////////////////////////////


    private void configureInlinePromptLinkCallback(final WebMarkupContainer
inlinePromptLink) {

        inlinePromptLink.add(new AjaxEventBehavior("click") {
            @Override
            protected void onEvent(final AjaxRequestTarget target) {

                scalarModel.toEditMode();

                switchFormForInlinePrompt(target);

                getComponentForRegular().setVisible(false);
                scalarIfRegularInlinePromptForm.setVisible(true);

                target.add(scalarTypeContainer);
            }

            @Override
            public boolean isEnabled(final Component component) {
                return true;
            }
        });
    }

    private void configureInlinePromptLinkCallback(
            final WebMarkupContainer inlinePromptLink,
            final ActionLink actionLink) {

        inlinePromptLink.add(new AjaxEventBehavior("click") {
            @Override
            protected void onEvent(final AjaxRequestTarget target) {
                actionLink.onClick(target);
            }

            @Override
            public boolean isEnabled(final Component component) {
                return true;
            }
        });
    }

    private void switchFormForInlinePrompt(final AjaxRequestTarget target) {
        scalarIfRegularInlinePromptForm = (PropertyEditFormPanel)
getComponentFactoryRegistry().addOrReplaceComponent(
                scalarTypeContainer,
ID_SCALAR_IF_REGULAR_INLINE_PROMPT_FORM, ComponentType.PROPERTY_EDIT_FORM,
scalarModel);

        onSwitchFormForInlinePrompt(scalarIfRegularInlinePromptForm,
target);
    }

    /**
     * Optional hook.
     */
    protected void onSwitchFormForInlinePrompt(
            final WebMarkupContainer inlinePromptForm,
            final AjaxRequestTarget target) {

    }


    // ///////////////////////////////////////////////////////////////////

    protected void addEditPropertyTo(
            final MarkupContainer scalarIfRegularFormGroup) {

        if(  scalarModel.canEnterEditMode() &&
            (scalarModel.getPromptStyle().isDialog() ||
            !getInlinePromptConfigLocal().isSupported())) {

            final WebMarkupContainer editProperty = new
WebMarkupContainer(ID_EDIT_PROPERTY);
            editProperty.setOutputMarkupId(true);
            scalarIfRegularFormGroup.addOrReplace(editProperty);

            editProperty.add(new AjaxEventBehavior("click") {
                protected void onEvent(AjaxRequestTarget target) {

                    final ActionPrompt prompt = ActionPromptProvider.Util
                            .getFrom(BooleanPanel.this).getActionPrompt();

                    PropertyEditPromptHeaderPanel titlePanel = new
PropertyEditPromptHeaderPanel(prompt.getTitleId(),
                            BooleanPanel.this.scalarModel);

                    final PropertyEditPanel propertyEditPanel =
                            (PropertyEditPanel)
getComponentFactoryRegistry().createComponent(
                                    ComponentType.PROPERTY_EDIT_PROMPT,
prompt.getContentId(),
                                    BooleanPanel.this.scalarModel);

                    propertyEditPanel.setShowHeader(false);

                    prompt.setTitle(titlePanel, target);
                    prompt.setPanel(propertyEditPanel, target);
                    prompt.showPrompt(target);

                }
            });
        } else {
            Components.permanentlyHide(scalarIfRegularFormGroup,
ID_EDIT_PROPERTY);
        }

    }


    private void addFeedbackOnlyTo(final MarkupContainer markupContainer,
final Component component) {
        markupContainer.addOrReplace(new NotificationPanel(ID_FEEDBACK,
component, new ComponentFeedbackMessageFilter(component)));
    }

    private void addActionLinksBelowAndRight(
            final MarkupContainer labelIfRegular,
            final List<LinkAndLabel> linkAndLabels) {
        final List<LinkAndLabel> linksBelow =
LinkAndLabel.positioned(linkAndLabels, ActionLayout.Position.BELOW);
        AdditionalLinksPanel.addAdditionalLinks(labelIfRegular,
ID_ASSOCIATED_ACTION_LINKS_BELOW, linksBelow,
AdditionalLinksPanel.Style.INLINE_LIST);

        final List<LinkAndLabel> linksRight =
LinkAndLabel.positioned(linkAndLabels, ActionLayout.Position.RIGHT);
        AdditionalLinksPanel.addAdditionalLinks(labelIfRegular,
ID_ASSOCIATED_ACTION_LINKS_RIGHT, linksRight,
AdditionalLinksPanel.Style.DROPDOWN);
    }

    /**
     * Applies the {@literal @}{@link LabelAtFacet} and also CSS based on
     * whether any of the associated actions have {@literal @}{@link
ActionLayout layout} positioned to
     * the {@link ActionLayout.Position#RIGHT right}.
     *
     * @param markupContainer The form group element
     * @param actionLinks
     */
    private void addPositioningCssTo(
            final MarkupContainer markupContainer,
            final List<LinkAndLabel> actionLinks) {
        CssClassAppender.appendCssClassTo(markupContainer,
determinePropParamLayoutCss(getModel()));
        CssClassAppender.appendCssClassTo(markupContainer,
determineActionLayoutPositioningCss(actionLinks));
    }

    private static String determinePropParamLayoutCss(ScalarModel model) {
        final LabelAtFacet facet = model.getFacet(LabelAtFacet.class);
        if (facet != null) {
            switch (facet.label()) {
            case LEFT:
                return "label-left";
            case RIGHT:
                return "label-right";
            case NONE:
                return "label-none";
            case TOP:
                return "label-top";
            }
        }
        return "label-left";
    }

    private static String
determineActionLayoutPositioningCss(List<LinkAndLabel> entityActionLinks) {
        boolean actionsPositionedOnRight =
hasActionsPositionedOn(entityActionLinks, ActionLayout.Position.RIGHT);
        return actionsPositionedOnRight ? "actions-right" : null;
    }

    private static boolean hasActionsPositionedOn(final List<LinkAndLabel>
entityActionLinks, final ActionLayout.Position position) {
        for (LinkAndLabel entityActionLink : entityActionLinks) {
            if(entityActionLink.getPosition() == position) {
                return true;
            }
        }
        return false;
    }

    // ///////////////////////////////////////////////////////////////////

    /**
     * Repaints this panel of just some of its children
     *
     * @param target The Ajax request handler
     */
    public void repaint(AjaxRequestTarget target) {
        target.add(this);
    }


    /**
     * null stub required by super.  @see InlinePromptConfigLocal.   the
version in super has private methods that cannot be accessed from this
class.
     */
protected InlinePromptConfig getInlinePromptConfig() {
return null;
}



}
```



On Sun, Jan 5, 2020 at 11:52 PM Dan Haywood <da...@haywood-associates.co.uk>
wrote:

> HI Brian,
>
> I think we exchanged a few thoughts on our Slack channel [1], but let me
> reply more thoroughly here.  Within...
>
> [1] https://cwiki.apache.org/confluence/display/ISIS/Signing+up+to+Slack
>
> On Sun, 29 Dec 2019 at 19:04, Brian K <ha...@gmail.com> wrote:
>
> > Hi Dan,
> >
> > I followed the wicket viewer docs [1] and was able to call my custom
> > component after doing the following.
> >
> > I implemented a copy of BooleanPanel, BooleanPanelFactory,
> > I created a subclass of ComponentFactoryRegistrar and
> IsisWicketApplication
> > I referenced my new IsisWicketApplication in the webapp module's web.xml.
> > Overriding the init() method per the docs, I can see it is being called,
> > but the newIsisWicketModule() method is not being called.
> >
> > Turns out, newIsisWicketModule(isisConfiguration) was being called.  I am
> > overriding that method now, and just one more discrepancy:
> > The docs sample code has the extension of ComponentRegistrarDefault add
> the
> > default componentFactories before adding the custom one.  This used the
> > default BooleanPanelFactory instead of my custom one.  When I added the
> > custom one first then it used the custom one.
> >
>
> The wiring together of this stuff changes in v2 (though the classes and
> their responsibilities does not), so if it's ok with you I'm not going to
> spend any time now improving/fixing the way that this stuff is setup in v1.
>
>
>
> > I put in a trivial html change to verify my component is being used.
> I'll
> > be adding the useful parts next.  I'm using the ScalarModel's cssClass to
> > determine whether appliesTo() will select the custom component.  Is
> there a
> > better way to identify places to use it, like another annotation or
> > @PropertyLayout entry?
> >
> >
> The purpose of this new version of the BooleanPanel is for a smoother UI,
> that is for the user to just click the checkbox rather than having to enter
> edit mode.
> Do we think that any given Isis application that there might be some object
> properties which use the original UI, and other object properties which use
> the smooth UI?  I think the answer to that is "no" - it would be rather
> confusing for the interaction mechanism to vary between pages... users
> would want consistency.
>
> Therefore, I think that there should probably be some sort of global flag
> under "isis.viewer.wicket.xxx" which determines which style of BooleanPanel
> to render.
>
> Does that make sense?
>
> Cheers
> Dan
>
>
>
>
> > Thanks,
> > Brian
> >
> > [1]
> >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> >
> > On Sun, Dec 1, 2019 at 8:45 AM Dan Haywood <dan@haywood-associates.co.uk
> >
> > wrote:
> >
> > > Hi Brian,
> > >
> > > Apols for the delay.
> > >
> > > First port of call - did you look at the docs on replacing page
> elements?
> > > [1] .
> > >
> > >
> > > [1]
> > >
> > >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > > (nb: this is for v1.16.2, it seems the v1.17.0 doc formatting is
> slightly
> > > messed up, but the content didn't change between these two versions, I
> > > think).
> > > <
> > >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > > >
> > >
> > >
> > > On Sun, 17 Nov 2019 at 05:36, Brian K <ha...@gmail.com>
> wrote:
> > >
> > > > Yes that would be great!  I'm seeing how much I can do with the
> wicket
> > > > viewer to make a fluid user experience.  The Wicket project itself
> > seems
> > > to
> > > > indicate a lot is possible.
> > > >
> > > > -Brian
> > > >
> > > > On Sat, Nov 16, 2019, 1:43 AM Dan Haywood <
> > dan@haywood-associates.co.uk>
> > > > wrote:
> > > >
> > > > > Hi Brian,
> > > > > I agree, it is a bit clunky. I'm afraid it would require a new
> > > component
> > > > to
> > > > > be written. Contributions welcome, I can provide some guidance if
> > > you're
> > > > > willing.
> > > > > Cheers,
> > > > > Dan.
> > > > >
> > > > > On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com>
> > wrote:
> > > > >
> > > > > > Hi,
> > > > > >
> > > > > > For 1.17 and 2.0, Boolean properties are displayed in the Wicket
> > > viewer
> > > > > > something like this:
> > > > > >
> > > > > > sample        [X] Yes
> > > > > >
> > > > > > Clicking on the checkbox itself is disabled, while clicking on
> the
> > > text
> > > > > > next to it brings up an inline edit prompt (without the text)
> that
> > > > allows
> > > > > > you to click on the checkbox.
> > > > > >
> > > > > > Question:  How can I have the property display with a simple
> > > checkbox,
> > > > > > similar to how it displays when editing inline?  Is there a
> setting
> > > > > > somewhere or would this require coding a new wicket component?
> > > > > >
> > > > > > Thank you!
> > > > > > Brian
> > > > > >
> > > > >
> > > >
> > >
> >
>

Re: Boolean property wicket display

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
HI Brian,

I think we exchanged a few thoughts on our Slack channel [1], but let me
reply more thoroughly here.  Within...

[1] https://cwiki.apache.org/confluence/display/ISIS/Signing+up+to+Slack

On Sun, 29 Dec 2019 at 19:04, Brian K <ha...@gmail.com> wrote:

> Hi Dan,
>
> I followed the wicket viewer docs [1] and was able to call my custom
> component after doing the following.
>
> I implemented a copy of BooleanPanel, BooleanPanelFactory,
> I created a subclass of ComponentFactoryRegistrar and IsisWicketApplication
> I referenced my new IsisWicketApplication in the webapp module's web.xml.
> Overriding the init() method per the docs, I can see it is being called,
> but the newIsisWicketModule() method is not being called.
>
> Turns out, newIsisWicketModule(isisConfiguration) was being called.  I am
> overriding that method now, and just one more discrepancy:
> The docs sample code has the extension of ComponentRegistrarDefault add the
> default componentFactories before adding the custom one.  This used the
> default BooleanPanelFactory instead of my custom one.  When I added the
> custom one first then it used the custom one.
>

The wiring together of this stuff changes in v2 (though the classes and
their responsibilities does not), so if it's ok with you I'm not going to
spend any time now improving/fixing the way that this stuff is setup in v1.



> I put in a trivial html change to verify my component is being used.  I'll
> be adding the useful parts next.  I'm using the ScalarModel's cssClass to
> determine whether appliesTo() will select the custom component.  Is there a
> better way to identify places to use it, like another annotation or
> @PropertyLayout entry?
>
>
The purpose of this new version of the BooleanPanel is for a smoother UI,
that is for the user to just click the checkbox rather than having to enter
edit mode.
Do we think that any given Isis application that there might be some object
properties which use the original UI, and other object properties which use
the smooth UI?  I think the answer to that is "no" - it would be rather
confusing for the interaction mechanism to vary between pages... users
would want consistency.

Therefore, I think that there should probably be some sort of global flag
under "isis.viewer.wicket.xxx" which determines which style of BooleanPanel
to render.

Does that make sense?

Cheers
Dan




> Thanks,
> Brian
>
> [1]
>
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
>
> On Sun, Dec 1, 2019 at 8:45 AM Dan Haywood <da...@haywood-associates.co.uk>
> wrote:
>
> > Hi Brian,
> >
> > Apols for the delay.
> >
> > First port of call - did you look at the docs on replacing page elements?
> > [1] .
> >
> >
> > [1]
> >
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > (nb: this is for v1.16.2, it seems the v1.17.0 doc formatting is slightly
> > messed up, but the content didn't change between these two versions, I
> > think).
> > <
> >
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> > >
> >
> >
> > On Sun, 17 Nov 2019 at 05:36, Brian K <ha...@gmail.com> wrote:
> >
> > > Yes that would be great!  I'm seeing how much I can do with the wicket
> > > viewer to make a fluid user experience.  The Wicket project itself
> seems
> > to
> > > indicate a lot is possible.
> > >
> > > -Brian
> > >
> > > On Sat, Nov 16, 2019, 1:43 AM Dan Haywood <
> dan@haywood-associates.co.uk>
> > > wrote:
> > >
> > > > Hi Brian,
> > > > I agree, it is a bit clunky. I'm afraid it would require a new
> > component
> > > to
> > > > be written. Contributions welcome, I can provide some guidance if
> > you're
> > > > willing.
> > > > Cheers,
> > > > Dan.
> > > >
> > > > On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com>
> wrote:
> > > >
> > > > > Hi,
> > > > >
> > > > > For 1.17 and 2.0, Boolean properties are displayed in the Wicket
> > viewer
> > > > > something like this:
> > > > >
> > > > > sample        [X] Yes
> > > > >
> > > > > Clicking on the checkbox itself is disabled, while clicking on the
> > text
> > > > > next to it brings up an inline edit prompt (without the text) that
> > > allows
> > > > > you to click on the checkbox.
> > > > >
> > > > > Question:  How can I have the property display with a simple
> > checkbox,
> > > > > similar to how it displays when editing inline?  Is there a setting
> > > > > somewhere or would this require coding a new wicket component?
> > > > >
> > > > > Thank you!
> > > > > Brian
> > > > >
> > > >
> > >
> >
>

Re: Boolean property wicket display

Posted by Brian K <ha...@gmail.com>.
Hi Dan,

I followed the wicket viewer docs [1] and was able to call my custom
component after doing the following.

I implemented a copy of BooleanPanel, BooleanPanelFactory,
I created a subclass of ComponentFactoryRegistrar and IsisWicketApplication
I referenced my new IsisWicketApplication in the webapp module's web.xml.
Overriding the init() method per the docs, I can see it is being called,
but the newIsisWicketModule() method is not being called.

Turns out, newIsisWicketModule(isisConfiguration) was being called.  I am
overriding that method now, and just one more discrepancy:
The docs sample code has the extension of ComponentRegistrarDefault add the
default componentFactories before adding the custom one.  This used the
default BooleanPanelFactory instead of my custom one.  When I added the
custom one first then it used the custom one.

I put in a trivial html change to verify my component is being used.  I'll
be adding the useful parts next.  I'm using the ScalarModel's cssClass to
determine whether appliesTo() will select the custom component.  Is there a
better way to identify places to use it, like another annotation or
@PropertyLayout entry?

Thanks,
Brian

[1]
http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements

On Sun, Dec 1, 2019 at 8:45 AM Dan Haywood <da...@haywood-associates.co.uk>
wrote:

> Hi Brian,
>
> Apols for the delay.
>
> First port of call - did you look at the docs on replacing page elements?
> [1] .
>
>
> [1]
>
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> (nb: this is for v1.16.2, it seems the v1.17.0 doc formatting is slightly
> messed up, but the content didn't change between these two versions, I
> think).
> <
> http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
> >
>
>
> On Sun, 17 Nov 2019 at 05:36, Brian K <ha...@gmail.com> wrote:
>
> > Yes that would be great!  I'm seeing how much I can do with the wicket
> > viewer to make a fluid user experience.  The Wicket project itself seems
> to
> > indicate a lot is possible.
> >
> > -Brian
> >
> > On Sat, Nov 16, 2019, 1:43 AM Dan Haywood <da...@haywood-associates.co.uk>
> > wrote:
> >
> > > Hi Brian,
> > > I agree, it is a bit clunky. I'm afraid it would require a new
> component
> > to
> > > be written. Contributions welcome, I can provide some guidance if
> you're
> > > willing.
> > > Cheers,
> > > Dan.
> > >
> > > On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com> wrote:
> > >
> > > > Hi,
> > > >
> > > > For 1.17 and 2.0, Boolean properties are displayed in the Wicket
> viewer
> > > > something like this:
> > > >
> > > > sample        [X] Yes
> > > >
> > > > Clicking on the checkbox itself is disabled, while clicking on the
> text
> > > > next to it brings up an inline edit prompt (without the text) that
> > allows
> > > > you to click on the checkbox.
> > > >
> > > > Question:  How can I have the property display with a simple
> checkbox,
> > > > similar to how it displays when editing inline?  Is there a setting
> > > > somewhere or would this require coding a new wicket component?
> > > >
> > > > Thank you!
> > > > Brian
> > > >
> > >
> >
>

Re: Boolean property wicket display

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Hi Brian,

Apols for the delay.

First port of call - did you look at the docs on replacing page elements?
[1] .


[1]
http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements
(nb: this is for v1.16.2, it seems the v1.17.0 doc formatting is slightly
messed up, but the content didn't change between these two versions, I
think).
<http://isis.apache.org/versions/1.16.2/guides/ugvw/ugvw.html#_ugvw_extending_replacing-page-elements>


On Sun, 17 Nov 2019 at 05:36, Brian K <ha...@gmail.com> wrote:

> Yes that would be great!  I'm seeing how much I can do with the wicket
> viewer to make a fluid user experience.  The Wicket project itself seems to
> indicate a lot is possible.
>
> -Brian
>
> On Sat, Nov 16, 2019, 1:43 AM Dan Haywood <da...@haywood-associates.co.uk>
> wrote:
>
> > Hi Brian,
> > I agree, it is a bit clunky. I'm afraid it would require a new component
> to
> > be written. Contributions welcome, I can provide some guidance if you're
> > willing.
> > Cheers,
> > Dan.
> >
> > On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com> wrote:
> >
> > > Hi,
> > >
> > > For 1.17 and 2.0, Boolean properties are displayed in the Wicket viewer
> > > something like this:
> > >
> > > sample        [X] Yes
> > >
> > > Clicking on the checkbox itself is disabled, while clicking on the text
> > > next to it brings up an inline edit prompt (without the text) that
> allows
> > > you to click on the checkbox.
> > >
> > > Question:  How can I have the property display with a simple checkbox,
> > > similar to how it displays when editing inline?  Is there a setting
> > > somewhere or would this require coding a new wicket component?
> > >
> > > Thank you!
> > > Brian
> > >
> >
>

Re: Boolean property wicket display

Posted by Brian K <ha...@gmail.com>.
Yes that would be great!  I'm seeing how much I can do with the wicket
viewer to make a fluid user experience.  The Wicket project itself seems to
indicate a lot is possible.

-Brian

On Sat, Nov 16, 2019, 1:43 AM Dan Haywood <da...@haywood-associates.co.uk>
wrote:

> Hi Brian,
> I agree, it is a bit clunky. I'm afraid it would require a new component to
> be written. Contributions welcome, I can provide some guidance if you're
> willing.
> Cheers,
> Dan.
>
> On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com> wrote:
>
> > Hi,
> >
> > For 1.17 and 2.0, Boolean properties are displayed in the Wicket viewer
> > something like this:
> >
> > sample        [X] Yes
> >
> > Clicking on the checkbox itself is disabled, while clicking on the text
> > next to it brings up an inline edit prompt (without the text) that allows
> > you to click on the checkbox.
> >
> > Question:  How can I have the property display with a simple checkbox,
> > similar to how it displays when editing inline?  Is there a setting
> > somewhere or would this require coding a new wicket component?
> >
> > Thank you!
> > Brian
> >
>

Re: Boolean property wicket display

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Hi Brian,
I agree, it is a bit clunky. I'm afraid it would require a new component to
be written. Contributions welcome, I can provide some guidance if you're
willing.
Cheers,
Dan.

On Fri, 15 Nov 2019, 19:52 Brian K, <ha...@gmail.com> wrote:

> Hi,
>
> For 1.17 and 2.0, Boolean properties are displayed in the Wicket viewer
> something like this:
>
> sample        [X] Yes
>
> Clicking on the checkbox itself is disabled, while clicking on the text
> next to it brings up an inline edit prompt (without the text) that allows
> you to click on the checkbox.
>
> Question:  How can I have the property display with a simple checkbox,
> similar to how it displays when editing inline?  Is there a setting
> somewhere or would this require coding a new wicket component?
>
> Thank you!
> Brian
>