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