Jump to content

How to apply the IndirectUI rules correctly?


ReMake

Recommended Posts

It's a simple interface and a CodeLab script for it.

 

Color-ui.png

 

Spoiler
// Name:Color
// Submenu:
// Author:
// Title:
// Version:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
ListBoxControl Mode = 0; // {!Gray} Color|None|Primary|Secondary|Custom
ColorWheelControl CustomColor = ColorBgra.FromBgr(0, 60, 120); // [!] {!Gray} 
CheckboxControl Gray = false; // Grayscale
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    ColorBgra currentPixel;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            currentPixel = src[x,y];

            dst[x,y] = currentPixel;
        }
    }
}

 

 

Both controls (drop-down list and color wheel) active until the Grayscale checkbox is selected. This is easy to do in CodeLab. But I need the color wheel to be active only when the Custom item is selected in the drop-down list. The solution seems simple - add the rule IndirectUI 'ReadOnlyBoundToValueRule' in VS project.

 

Spoiler
public enum PropertyNames
        {
            Mode,
            CustomColor,
            Gray
        }

        public enum ModeOptions
        {
            None,
            Primary,
            Secondary,
            Custom
        }

        protected override PropertyCollection OnCreatePropertyCollection()
        {
            List<Property> props = new List<Property>();

            props.Add(StaticListChoiceProperty.CreateForEnum<ModeOptions>(PropertyNames.Mode, 0, false));
            props.Add(new Int32Property(PropertyNames.CustomColor, ColorBgra.ToOpaqueInt32(PrimaryColor), 0, 0xffffff));
            props.Add(new BooleanProperty(PropertyNames.Gray, false));

            List<PropertyCollectionRule> propRules = new List<PropertyCollectionRule>();

            propRules.Add(new ReadOnlyBoundToBooleanRule(PropertyNames.Mode, PropertyNames.Gray, false));
            propRules.Add(new ReadOnlyBoundToBooleanRule(PropertyNames.CustomColor, PropertyNames.Gray, false));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.CustomColor, PropertyNames.Mode, ModeOptions.Custom, true));

            return new PropertyCollection(props, propRules);
        }

 

 

This works correctly until you check the Grayscale checkbox. If you have selected one of the first three items in the drop-down list, then after unchecking the Grayscale checkbox, the color wheel becomes active, although it should be active only when selecting the fourth item (Custom).

 

Any suggestions?

Link to comment
Share on other sites

You are trying to activate a control based on two separate conditions, which is not easy to do. I believe the two conditions are treated entirely independently; they aren't logically combined.  As I recall, it's simply the state of the last-changed conditional control that determines whether the control will be active.

 

However, a comment by BoltBait suggests there's a way around this:

 

Quote
  • BoltBait Grand Master
  • Administrator
  • team_admin.png
  • California, USA

I'm no expert in IndirectUI rules.  And, any time I need to make a complicated one I get a headache. But, this one's got me stumped.

 

Along with other controls, I have 2 check boxes and a slider that I want to control this way:

 

Box 1 - Box 2 - Slider

[   ]   [   ]   Disabled

[ X ]   [   ]   Enabled

[   ]   [ X ]   Disabled

[ X ]   [ X ]   Disabled

 

As you can see, Box 2 is a kind of "override" and if it is checked, the slider is always disabled.

 

I nearly have it working like this:

propRules.Add(new ReadOnlyBoundToNameValuesRule(PropertyNames.Slider, false, new TupleStruct<object, object>[] {
    new TupleStruct<object, object>(PropertyNames.Box1, false),
    new TupleStruct<object, object>(PropertyNames.Box2, true)
}));

 

I say "nearly" because the default settings of the two boxes are unchecked and the slider always starts out enabled instead of disabled.  Once you click either of the checkboxes the slider is en/disabled according to my design.  However, I can not figure out how to get the slider to start out in the disabled state.

 

I'm not sure if this is important or not, but I have an additional rule in place that controls the enabled/disabled state of Box 1:

 

Box 2 - Box 1

[   ]   Enabled

[ X ]   Disabled

 

Code:

propRules.Add(new ReadOnlyBoundToBooleanRule(PropertyNames.Box1, PropertyNames.Box2, false));

Of course, this is very simple and works fine.

 

And, of course, I have several other rules controlling other sliders, etc. All of those rules are working fine.

 

Can anyone help here?

 

EDIT: From what I can tell, using "ReadOnlyBoundToNameValueRule" where the conditions would start out a control as disabled just doesn't work.  Is this a bug in Paint.NET?  Or, am I using the rules wrong?

 

The solution to BoltBait;'s problem (which shows BoltBait's approach was essentially correct -- he just overlooked a detail):

 

Quote

null54

On 4/6/2020 at 4:19 PM, BoltBait said:

I say "nearly" because the default settings of the two boxes are unchecked and the slider always starts out enabled instead of disabled.  Once you click either of the checkboxes the slider is en/disabled according to my design.  However, I can not figure out how to get the slider to start out in the disabled state.

 

Are you setting the slider to readonly in the Int32Property constructor?

 

new Int32Property(PropertyNames.Slider, defaultValue, minValue, maxValue, true)

 

 

It's one of those things I always intended to read carefully and understand, but never got around to it.  Perhaps someone (maybe even me) can do so, and write a tutorial on the matter.

  • Like 1
  • Upvote 1
Link to comment
Share on other sites

14 minutes ago, MJW said:

one of those things I always intended to read carefully and understand, but never got around to it.  Perhaps someone (maybe even me) can do so, and write a tutorial on the matter.

I would appreciate that if you could. Also to anyone else that volunteer to this.

G'MIC Filter Developer

Link to comment
Share on other sites

Sorry about that. I didn't realize that wasn't in a public place. I don't know why it wasn't. I replaced the link with BoltBait's comment and Null54's followup comment . I hope that's okay with BoltBait, Null54, and the other administrators.

  • Thanks 1
  • Upvote 1
Link to comment
Share on other sites

@otuncelli's link provides the answer (in a thread I'd somehow missed). ReadOnlyBoundToValueRule has a overload that accepts an array of conditions. The conditions can be from separate controls. Of course, this only works for VS projects, not for CodeLab.

 

I believe @midora's problem mentioned in the last thread comment is probably the same as BoltBait's problem: if the default state of the conditioning controls would cause the conditioned control to be deactivated, the conditioned control needs to be initialized to a deactivated state.

  • Like 1
Link to comment
Share on other sites

15 minutes ago, MJW said:

Of course, this only works for VS projects, not for CodeLab.

It is precisely because CodeLab cannot set two different dependencies for the same control that I am trying to solve this problem in VS.

@MJW, thank you for your suggestions.

Link to comment
Share on other sites

2 hours ago, BoltBait said:

Sure, make me look like an idiot... IN PUBLIC! 

 

I thought it made you look pretty smart. Until I read your comment I had no idea there was a way to activate a control based on the state of two others. You just missed a minor detail about initializing the control.

Link to comment
Share on other sites

12 hours ago, MJW said:

I added a comment ...

 

@MJW, thanks for the comment. I tried to apply ReadOnlyBoundToNameValuesRule:

 

List<PropertyCollectionRule> propRules = new List<PropertyCollectionRule>();

propRules.Add(new ReadOnlyBoundToBooleanRule(PropertyNames.Mode, PropertyNames.Gray, false));
// propRules.Add(new ReadOnlyBoundToBooleanRule(PropertyNames.CustomColor, PropertyNames.Gray, false));
// propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.CustomColor, PropertyNames.Mode, ModeOptions.Custom, true));
propRules.Add(new ReadOnlyBoundToNameValuesRule(PropertyNames.CustomColor, true, new TupleStruct<object, object>[] {
	new TupleStruct<object, object>(PropertyNames.Gray, false),
	new TupleStruct<object, object>(PropertyNames.Mode, ModeOptions.Custom)
}));		

return new PropertyCollection(props, propRules);

 

So far, this rule does not work for me.  ReadOnlyBoundToNameValuesRule only works correctly with the Gray checkbox.  The state of the CustomColor control is practically independent of the Mode drop-down list item.

 

Probably the difficulties of language translation did not allow me to interpret your comment correctly. I have tried many options for writing the ReadOnlyBoundToNameValuesRule rule, but so far to no avail.

 

Link to comment
Share on other sites

2 hours ago, ReMake said:

So far, this rule does not work for me.  ReadOnlyBoundToNameValuesRule only works correctly with the Gray checkbox.  The state of the CustomColor control is practically independent of the Mode drop-down list item.

 

I don't know why the state of the CustomColor control would be practically independent of the Mode drop-down list item, but I don't think the logic of your call is quite right. What you have will enable the CustomColor control if either Gray is false OR Custom is selected. I think you want to enable it only if Gray is false AND Custom is selected. This is like the situation I mentioned in the Note, where you must disable the control if either Gray is true or any of the other modes is selected.

 

EDIT: Also, don't overlook the little reminder that unless Custom is the default mode, you'll need to explicitly disable the colorwheel when you create it by setting the ReadOnly flag. Otherwise, it will initially be enabled even though it shouldn't be.

Link to comment
Share on other sites

14 hours ago, BoltBait said:

 

Yeah, something I like to call a "rookie mistake!"

 

I actually think it's a slight bug in the array version of ReadOnlyBoundToNameValuesRule. The simpler versions correctly initially disable a control based on the default value of the control it depends on. Or at least I've never encountered any problem with that.

Link to comment
Share on other sites

2 hours ago, MJW said:

 

I actually think it's a slight bug in the array version of ReadOnlyBoundToNameValuesRule. The simpler versions correctly initially disable a control based on the default value of the control it depends on. Or at least I've never encountered any problem with that.

 

If there's a bug, I'll need a repro so that I can fix it

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

It's a pretty minor bug, it that's what it is, but it's easy to reproduce.  Just use BoltBait's example I quoted above:

 

propRules.Add(new ReadOnlyBoundToNameValuesRule(PropertyNames.Slider, false, new TupleStruct<object, object>[] {
    new TupleStruct<object, object>(PropertyNames.Box1, false),
    new TupleStruct<object, object>(PropertyNames.Box2, true)
}));

 

Despite that fact that the Slider control should initially be disabled because the default for Box2 is checked, it will be enabled unless it's initialized to ReadOnly.

Link to comment
Share on other sites

18 hours ago, MJW said:
propRules.Add(new ReadOnlyBoundToNameValuesRule(PropertyNames.Slider, false, new TupleStruct<object, object>[] {
    new TupleStruct<object, object>(PropertyNames.Box1, false),
    new TupleStruct<object, object>(PropertyNames.Box2, true)
}));

 

 

Also, you can use a simpler syntax for the tuples. I just added this support for 4.3.2 to do implicit conversions to-and-from regular ValueTuples:

 

propRules.Add(new ReadOnlyBoundToNameValuesRule(PropertyNames.Slider, false, 
    (PropertyNames.Box1, false),
    (PropertyNames.Box2, true)
}));

And because the array parameter is params, no need for the new ...[] { ... } boilerplate

  • Like 1
  • Upvote 2

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

10 hours ago, Rick Brewster said:
  • Fixed ReadOnlyBoundToNameValuesRule for IndirectUI-based plugins. It was not initializing correctly, although it would work correctly afterward.

 

Perhaps I am trying to achieve something unattainable, but in my opinion, the last change did not lead to a change in the behavior of the controls, which I described above in the first post. On the contrary, the Color Wheel is now always active regardless of the drop-down list item. 

 

Maybe I'm so stupid that I couldn't figure out the logic of Indirect UI rules. Can any of the developers give me an example of writing an IndirectUI rule that would allow the Color Wheel to be active if and only if the Custom item is selected in the drop-down list and the Gray checkbox is not checked?

Edited by ReMake
Link to comment
Share on other sites

You just have to do it step by step.

 

So first you should think about the readonly conditions:

 

             Mode.Readonly = Gray

             CustomColor.Readonly = Gray || (Mode != CustomColor)

 

We can not handle '!=' as far as I know so just rewrite the condition

 

             CustomColor.Readonly = Gray || (Mode == None) || (Mode == Primary) || (Mode == Secondary)

 

Now create the rules

 

            List<PropertyCollectionRule> propRules = new List<PropertyCollectionRule>()
            {
                new ReadOnlyBoundToBooleanRule(PropertyNames.Mode, PropertyNames.Gray, false),
                new ReadOnlyBoundToNameValuesRule(PropertyNames.CustomColor, false, new TupleStruct<object, object>[] {
                        new TupleStruct<object, object>(PropertyNames.Gray, true),
                        new TupleStruct<object, object>(PropertyNames.Mode, ModeOptions.None),
                        new TupleStruct<object, object>(PropertyNames.Mode, ModeOptions.Primary),
                        new TupleStruct<object, object>(PropertyNames.Mode, ModeOptions.Secondary),
                }),
            };

 

or use the new syntax variant provided in 4.3.2

 

            List<PropertyCollectionRule> propRules = new List<PropertyCollectionRule>()
            {
                new ReadOnlyBoundToBooleanRule(PropertyNames.Mode, PropertyNames.Gray, false),
                new ReadOnlyBoundToNameValuesRule(PropertyNames.CustomColor, false,
                        (PropertyNames.Gray, true),
                        (PropertyNames.Mode, ModeOptions.None),
                        (PropertyNames.Mode, ModeOptions.Primary),
                        (PropertyNames.Mode, ModeOptions.Secondary)
                ),
            };

 

  • Upvote 1

midoras signature.gif

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...