This Question is Answered

14 "helpful" answers available (3 pts)
12 Replies Last post: Jun 4, 2008 9:25 PM by fukuta

How to make the value of a ComboBox unchangeable?

Jun 3, 2008 7:36 PM

Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
Hi all,

I'm looking for the way to make the value of a ComboBox unchangeable. As you know, setting editable? option to false allows selecting the list or pressing the up and down keys to change the value, so I cannot choose this way. Likewise, I don't want to disable a ComboBox because a loss of ability to select and copy its text.

I considered the way to just replace the ComboBox with a text when it need to be unchangeable, but I'd like to use a ComboBox as is if possible.
Click to view tdeng's profile Level 3 tdeng 34 posts since
Oct 17, 2007
1. Re: How to make the value of a ComboBox unchangeable? Jun 3, 2008 8:04 PM
consume Pointer event and KeyPress event may be the simplest way to do this.

Here is a sample:

{Dialog
{VBox
{ComboBox
"Antelope", "Llama", "Horse",
value="Antelope",
editable?=false,
{on pe:PointerEnvelopeEvent do
{pe.consume}
},
{on kp:KeyPress do
{kp.consume}
}
}
,{TextField}
}
}
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
2. Re: How to make the value of a ComboBox unchangeable? Jun 3, 2008 9:03 PM
in response to: tdeng
Thanks tdeng, but it makes not to be able to select and copy the input text...
Click to view tdeng's profile Level 3 tdeng 34 posts since
Oct 17, 2007
3. Re: How to make the value of a ComboBox unchangeable? Jun 3, 2008 9:53 PM
in response to: fukuta
The sample is just one to express the idea on how to control your combobox, for completely implementing your requirements, you need further job :)

This one may work, but I won't promise :)

Please have a try and your comments will be welcomed.

{Dialog
{VBox
{ComboBox
"Antelope", "Llama", "Horse",
value="Antelope",
editable?=false,
{on pe:PointerEnvelopeEvent do

{if pe.state-mask.button-1? then
else
{pe.consume}
}
},
{on kp:KeyPress do
{if kp.ctrl? and
(kp.value == {KeyPressValue.char-for-name "c"}
or kp.value == {KeyPressValue.char-for-name "v"}) then
else
{kp.consume}
}
}
}

,{TextField}
}
}
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
4. Re: How to make the value of a ComboBox unchangeable? Jun 3, 2008 10:09 PM
in response to: tdeng
My requirement cannot probably be done by just adding some event handlers. I think I have to extend UI classes to implement this thing, but I have no reasonable idea.
As far as your second sample, rightmost button of combobox can be used to change its value.
Click to view tdeng's profile Level 3 tdeng 34 posts since
Oct 17, 2007
5. Re: How to make the value of a ComboBox unchangeable? Jun 3, 2008 11:28 PM
in response to: fukuta
How about this one?
The idea is to find the dropdown button in ComboBoxUI class, then disable it.
Risk here is that the API could be changed in future versions, which may lead to copiling errors.

{let cb:ComboBox =
{ComboBox
"Antelope", "Llama", "Horse",
value="Antelope",
editable?=false,
{on pe:PointerEnvelopeEvent at cb:ComboBox do
{if not pe.state-mask.button-1? then
{pe.consume}
}

},
{on kp:KeyPress do
{if kp.ctrl? and
(kp.value == {KeyPressValue.char-for-name "c"}
or kp.value == {KeyPressValue.char-for-name "v"}) then
else
{kp.consume}
}
}
}
}
{do
|| This is the HBox contains the dropdown button
def x =({(cb.ui-object asa StandardComboBoxUI).graphical-children.read-one} asa Box).graphical-children
|| This is a frame before dropdownbutton
def a = {x.read-one}
|| This is the dropdownbutton
def b = {x.read-one}
set b.enabled? = false
}
{Dialog
{VBox
cb
,{TextField}
}
}

Click to view carl's profile Curl carl 53 posts since
Oct 17, 2007
6. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 12:29 AM
in response to: tdeng
Messing around with the internals of a UI is not safe, and code assuming StandardComboBoxUI would throw if used with skinnable controls; e.g. STYLED-CONTROLS or on OS X. Defining a different UI is certainly possible, but switching from a ComboBox to a TextField and back is very easy, and has the advantage of offering more visual distinction to the user.

Here's the body of a quick-and-dirty test applet for Control-switching:

{def cb:ComboBox =
    {ComboBox
        value = "fish",
        "fish", "aaa", "bbb", "ccc"
    }
}
 
{def td:TextField = {TextField editable? = false}}
 
{def target-frame = {Frame cb}}
 
{RadioFrame
    value = "editable",
    {VBox
        {RadioButton radio-value = "editable"},
        {RadioButton radio-value = "noneditable"}
    },
    {on ValueFinished at rf:RadioFrame do
        def rf-value = rf.value
        {if rf-value == "editable" then
            {target-frame.add replace? = true, cb}
         else
            set td.value = cb.value
            {target-frame.add replace? = true, td}        
        }
    }
}
 
{value target-frame}
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
7. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 12:37 AM
in response to: tdeng
Thanks tdeng so much.
It looks like working fine, but I'm not sure that the solution considers every operation combobox supports. I'm afraid it's a pretty stopgap measure. For example, your last sample still doesn't work with tab traversal. You may propose adding the code not to consuming the Tab press event, but I wouldn't like to keep making the same thing, sorry.
Click to view tdeng's profile Level 3 tdeng 34 posts since
Oct 17, 2007
8. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 1:10 AM
in response to: fukuta
Sure, as carl pointed out, the sample I provided is in a "dirty" way, and I agree with this comment. Also, if you want a clean and easily maintainable solution, it would be better to either customize the combobox, or by switching between a combobox and a textfield, as you yourself stated, which is also carl's recommendation.
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
9. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 1:30 AM
in response to: carl
Thanks carl for your advice.
That's a steady and easy way, though what I'm still concerning is that it becomes unable to treat the control always as a combobox in my code. e.g. It's often required two variables having combobox and textfield, and I have to handle two controls respectively. This is why I said as below at the first post on this thread,
I considered the way to just replace the ComboBox with a text when it need to be unchangeable, but I'd like to use a ComboBox as is if possible.

It is possible to combine the solution into one object, indeed. But such object already isn't a combobox and I have to treat the object in distinction from other comboboxes.
But I understand the faults of assuming particular UI. If supposed to use STYLED-CONTROLS, does it make sense to extend SkinnableComboBoxUI?
Click to view carl's profile Curl carl 53 posts since
Oct 17, 2007
10. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 2:40 AM
in response to: fukuta
It's certainly possible to subclass SkinnableComboBoxUI, though you'll run into a few of the same issues -- namely, the easy changes will end up filtering out events based on what you think they'll do, rather than their actual actions. I would still recommend the switching approach, despite the slightly increased need to copy over options and value changes.

If you were determined to keep only ComboBox, you'd want to look to filter out the effects of up arrows and down arrows (on-key-press) and pointer scrolls (on-pointer-scroll). This could be done in the Feel or the UI, though there are drawbacks either way. You would also need to disable the internal CommandButton or change the UI's create-button method to alter its Action handler.
Click to view Kamal's profile Curl Kamal 99 posts since
Oct 17, 2007
11. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 5:35 AM
in response to: carl
Try this. The idea here is to change the ComboBox code back to what it was when the value changes and the ComboBox is marked as not editable. Note that in the example below I am using (or say misusing) ComboBox.editable?. You could have your own separate global boolean field and the code below will just work if you start using that global field instead of the "editable?" property. If you like the solution, then you could probably do this by sub classing the control itself to avoid having global fields.


{curl 6.0 applet}
{curl-file-attributes character-encoding = "windows-latin-1"}
 
{let package cb-has-value?:bool}
{let package cb-value:#String}
 
 
{def cb:ComboBox =
    {ComboBox
        value = "fish",
        "fish", "aaa", "bbb", "ccc",
        {on e:ValueChanged at cb:ComboBox do
            {if not cb.editable? then
                {if cb-has-value? and cb.value != cb-value then
                    set cb.value = {non-null cb-value}
                 else
                    {cb.unset-value}
                }
            }
        }
    }
}
 
{do
    set cb-has-value? = cb.has-value?
    set cb-value = cb.value
}
 
 
{RadioFrame
    value = "editable",
    {VBox
        {RadioButton radio-value = "editable"},
        {RadioButton radio-value = "noneditable"}
    },
    {on ValueFinished at rf:RadioFrame do
        def rf-value = rf.value
        set cb.editable? = (rf.value == "editable")
        {if not cb.editable? then
            set cb-has-value? = cb.has-value?
            set cb-value = cb.value
        }
    }
}
 
{value cb}
 
{CommandButton}
 
 
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
12. Re: How to make the value of a ComboBox unchangeable? Jun 4, 2008 9:25 PM
in response to: Kamal
Thanks carl and Kamal for your replies.

I focused on preventing the change of value from UI. But what a simple way Kamal's is! I couldn't think of such a alternative.
I made that into subclass of ComboBox, as a starter, like below


{define-class public PinningComboBox {inherits ComboBox}

  field has-pinning-value?:bool
  field pinning-value:#String

  {constructor public {default ...}
    {construct-super
        {splice ...},
        {on e:ValueChanged at cb:ComboBox do
            {if not cb.editable? then
                {if self.has-pinning-value? and cb.value != self.pinning-value then
                    set cb.value = {non-null self.pinning-value}
                 else
                    {cb.unset-value}
                }
            }
        }
    }
  }

  {local-option public editable?:bool
    {if not editable? then
        set self.has-pinning-value? = self.has-value?
        set self.pinning-value = self.value
    }
  }

}


{def cb:ComboBox =
    {PinningComboBox
        value = "fish",
        "fish", "aaa", "bbb", "ccc"
    }
}


{RadioFrame
    value = "editable",
    {VBox
        {RadioButton radio-value = "editable"},
        {RadioButton radio-value = "noneditable"}
    },
    {on ValueFinished at rf:RadioFrame do
        set cb.editable? = (rf.value == "editable")
    }
}

{value cb}

{CommandButton}


Thanks for helpful advices.