You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@isis.apache.org by Jeroen Houtevelts <je...@gmail.com> on 2020/02/06 12:42:46 UTC

Re: Boolean property wicket display

 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
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
>