New: Support modern Cocoa behavior regarding radio buttons groups. (#2941)

This commit is contained in:
Didier Korthoudt authored and GitHub committed 2020-10-09 09:35:56 +02:00
1 parent c1543d4a2f
commit ef779c346e
2 files changed
+137

No files matched your search

+74
View File
@@ -67,6 +67,18 @@ CPRadioImageOffset = 4.0;
[[button1 radioGroup] selectedRadio] returns the currently selected
option.
UPDATE 09/2020 : Implementation of modern Cocoa behavior :
As in Cocoa, radio buttons grouping is now automatic.
To be associated in a common group (and so being mutually exclusive),
radio buttons must combine 2 criteria :
- same superview (enclosing view)
- same action
TODO: This first implementation uses "as is" CPRadioGroup. This could
be simplified (no more need for radio group action, for example)
*/
@implementation CPRadio : CPButton
{
@@ -160,6 +172,63 @@ CPRadioImageOffset = 4.0;
[CPApp sendAction:[_radioGroup action] to:[_radioGroup target] from:_radioGroup];
}
- (void)viewDidMoveToSuperview
{
[self _setRadioGroup];
[super viewDidMoveToSuperview];
}
- (void)setAction:(SEL)anAction
{
if (anAction === _action)
return;
[super setAction:anAction];
[self _setRadioGroup];
}
#pragma mark Private methods
- (void)_setRadioGroup
{
// Implementation of modern Cocoa behavior : automatic radio group
// If no action is set or no superview, no grouping can be done.
if (![self action] || ![self superview])
{
// If I'm in a group (size > 1), remove me.
if ([[self radioGroup] size] > 1)
[self setRadioGroup:[CPRadioGroup new]];
return;
}
// Search in superview subviews for other radio buttons having the same action.
// Take the one with the radio group having the greatest number of members.
var radioGroup;
for (var i = 0, superviewSubviews = [[self superview] subviews], count = [superviewSubviews count], aSubview, myAction = [self action], radioGroupSize = -1; (i < count); i++)
{
aSubview = superviewSubviews[i];
if ([aSubview isKindOfClass:CPRadio] && (aSubview !== self) && ([aSubview action] === myAction) && ([[aSubview radioGroup] size] > radioGroupSize))
{
radioGroup = [aSubview radioGroup];
radioGroupSize = [radioGroup size];
}
}
if (radioGroup)
[self setRadioGroup:radioGroup];
else
// No other radio buttons to group with found.
// It may be because this radio button was in a radio group and its action was changed.
// If this is the case, we must reisolate it in a new radio group.
if ([_radioGroup size] > 1)
[self setRadioGroup:[CPRadioGroup new]];
}
@end
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
@@ -320,6 +389,11 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
return _radios;
}
- (int)size
{
return [_radios count];
}
- (void)setEnabled:(BOOL)enabled
{
[_radios makeObjectsPerformSelector:@selector(setEnabled:) withObject:enabled];
+63
View File
@@ -190,6 +190,69 @@
[self assertTrue:wasClicked message:@"a user click on a radio button should fire the group action"];
}
- (void)testAutomaticRadioGroup
{
var radioButton1 = [CPRadio radioWithTitle:@"Radio 1"],
radioButton2 = [CPRadio radioWithTitle:@"Radio 2"],
radioButton3 = [CPRadio radioWithTitle:@"Radio 3"],
simpleView1 = [[CPView alloc] initWithFrame:CGRectMakeZero()],
simpleView2 = [[CPView alloc] initWithFrame:CGRectMakeZero()];
// Initially, buttons are isolated
[self assertFalse:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"initially, buttons should be isolated"];
[simpleView1 addSubview:radioButton1];
[simpleView1 addSubview:radioButton2];
// As no actions are defined, buttons are still isolated
[self assertFalse:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"no actions defined, buttons should be isolated"];
[radioButton1 setAction:@selector(dummyAction1:)];
[radioButton2 setAction:@selector(dummyAction2:)];
// As different actions are defined, buttons are still isolatdd
[self assertFalse:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"different actions defined, buttons should be isolated"];
[radioButton2 setAction:@selector(dummyAction1:)];
// As the same action is defined, buttons must be grouped
[self assertTrue:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"same action defined, buttons should be grouped"];
[radioButton3 setAction:@selector(dummyAction1:)];
// As radioButton3 is not inserted in a view, it's isolated
[self assertTrue:([[radioButton3 radioGroup] size] === 1) message:@"not in a view, button should be isolated"];
[simpleView2 addSubview:radioButton3];
// As radioButton3 is in another view, it's isolated from radioButton1 & 2
[self assertTrue:([[radioButton3 radioGroup] size] === 1) message:@"alone in a view, button should be isolated"];
[simpleView1 addSubview:radioButton3];
// As all 3 buttons are in the same view, with the same action, they are grouped
[self assertTrue:([[radioButton3 radioGroup] size] === 3) message:@"after moving to the same view, buttons should be grouped"];
[radioButton3 setAction:@selector(dummyAction2:)];
// As the action of button 3 is now different, it's isolated
[self assertTrue:([[radioButton3 radioGroup] size] === 1) message:@"after changing the action, button 3 should be isolated"];
// And buttons 1 & 2 are still grouped
[self assertTrue:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"buttons 1 & 2 should remain grouped"];
[self assertTrue:([[radioButton1 radioGroup] size] === 2) message:@"radio group should contain only buttons 1 & 2"];
}
- (IBAction)dummyAction1:(id)sender
{
}
- (IBAction)dummyAction2:(id)sender
{
}
- (void)testTypeMasks
{
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];