From f3392c7df10517abb0b231dff8a9e2cf7ad1300b Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sat, 24 Jan 2026 16:55:26 +0100 Subject: [PATCH] fixed: RuleEditor was not functional --- AppKit/CPTextField.j | 10 + .../Manual/ThemeKitchenSinkA3/AppController.j | 269 ++++++++++++------ 2 files changed, 195 insertions(+), 84 deletions(-) diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index de123ef27..b6f716a52 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -1269,6 +1269,16 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); [super textDidChange:note]; } +- (void)validateEditing +{ +#if PLATFORM(DOM) + var element = [self _inputElement]; + + if (element) + [self _setStringValue:element.value isNewValue:YES errorDescription:nil]; +#endif +} + - (void)textDidBeginEditing:(CPNotification)note { //this looks to prevent false propagation of notifications for other objects diff --git a/Tests/Manual/ThemeKitchenSinkA3/AppController.j b/Tests/Manual/ThemeKitchenSinkA3/AppController.j index fc7c478cb..121d58ba3 100644 --- a/Tests/Manual/ThemeKitchenSinkA3/AppController.j +++ b/Tests/Manual/ThemeKitchenSinkA3/AppController.j @@ -3,7 +3,7 @@ * KitchenSink in Code * * Created by Daniel Böhringer 2026. - * Refactored: Progress Stepper aligned right, layout optimized. + * Refactored: Fixed RuleEditor action handling. */ @import @@ -110,7 +110,6 @@ [contentView addSubview:tabView]; } -// Helper to recursively apply HUD state - (void)_applyHUDStateToView:(CPView)aView { if ([aView respondsToSelector:@selector(setThemeState:)]) @@ -136,9 +135,7 @@ fieldHeight = 25.0, boxTopY = 20.0; - // ------------------------------------------------------ - // LEFT BOX: Input, Buttons & Sliders - // ------------------------------------------------------ + // --- LEFT BOX --- var leftBox = [[CPBox alloc] initWithFrame:CGRectMake(margin, boxTopY, boxWidth, 100)]; [leftBox setTitle:@"Input & Controls"]; [leftBox setAutoresizingMask:CPViewMaxXMargin | CPViewMinYMargin]; @@ -147,7 +144,7 @@ var leftContent = [leftBox contentView]; var currentY = startY; - // 1. Buttons + // Buttons var pushButton = [[CPButton alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth, fieldHeight)]; [pushButton setTitle:@"Push Button"]; [pushButton setBezelStyle:CPRoundedBezelStyle]; @@ -166,7 +163,7 @@ [leftContent addSubview:roundRectButton]; currentY += gapY + 5.0; - // 2. Text Inputs + // Text Inputs var placeholderField = [[CPTextField alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth, fieldHeight)]; [placeholderField setEditable:YES]; [placeholderField setBezeled:YES]; @@ -191,7 +188,7 @@ [leftContent addSubview:tokenField]; currentY += gapY + 20.0; - // 3. Sliders + // Sliders var tickSlider = [[CPSlider alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth - 40, 24)]; [leftContent addSubview:tickSlider]; @@ -207,11 +204,8 @@ [leftBox setFrameSize:CGSizeMake(boxWidth, currentY + 15.0)]; - // ------------------------------------------------------ - // RIGHT BOX: Menus, Toggles & Progress - // ------------------------------------------------------ + // --- RIGHT BOX --- var rightBoxX = margin + boxWidth + margin; - var rightBox = [[CPBox alloc] initWithFrame:CGRectMake(rightBoxX, boxTopY, boxWidth, 100)]; [rightBox setTitle:@"Selection & Status"]; [rightBox setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin]; @@ -220,14 +214,14 @@ var rightContent = [rightBox contentView]; currentY = startY; - // 1. Date Picker + // Date Picker var datePicker = [[CPDatePicker alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth, fieldHeight)]; [datePicker setDatePickerStyle:CPTextFieldAndStepperDatePickerStyle]; [datePicker setDateValue:[CPDate date]]; [rightContent addSubview:datePicker]; currentY += gapY; - // 2. Menus + // Menus var comboBox = [[CPComboBox alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth, fieldHeight)]; [comboBox setPlaceholderString:@"Combo Box"]; [comboBox addItemsWithObjectValues:["Alpha", "Beta", "Gamma"]]; @@ -246,7 +240,7 @@ [rightContent addSubview:popUp]; currentY += gapY + 5.0; - // 3. Toggles + // Toggles var cbOn = [CPCheckBox checkBoxWithTitle:@"On"]; [cbOn setFrameOrigin:CGPointMake(innerX, currentY)]; [cbOn setState:CPOnState]; @@ -275,14 +269,11 @@ [radio2 setTarget:self]; [radio2 setAction:@selector(dummyAction:)]; currentY += gapY + 15.0; - // 4. Progress & Level Group - - // Calculate right-side alignment x-coordinate for steppers + // Progress var stepperWidth = 19.0; var alignRightX = innerX + controlWidth - stepperWidth; - - // -- Level Indicator -- - var levelWidth = alignRightX - innerX - 5.0; // Fill space up to stepper + var levelWidth = alignRightX - innerX - 5.0; + var levelInd = [[CPLevelIndicator alloc] initWithFrame:CGRectMake(innerX, currentY + 2, levelWidth, 18)]; [levelInd setMaxValue:5]; [levelInd setDoubleValue:3]; @@ -296,8 +287,6 @@ [levelInd bind:CPValueBinding toObject:levelStepper withKeyPath:@"doubleValue" options:nil]; currentY += gapY; - // -- Progress Row -- - // Regular Spinner (Left) var spinner = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX, currentY, 24, 24)]; [spinner setStyle:CPProgressIndicatorSpinningStyle]; [spinner setIndeterminate:YES]; @@ -305,7 +294,6 @@ [spinner startAnimation:self]; [rightContent addSubview:spinner]; - // Circular Determinate (Middle-Left) var circProg = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX + 40, currentY, 24, 24)]; [circProg setStyle:CPProgressIndicatorSpinningStyle]; [circProg setIndeterminate:NO]; @@ -314,17 +302,14 @@ [circProg setMaxValue:100.0]; [rightContent addSubview:circProg]; - // Stepper (Right Aligned) var stepper = [[CPStepper alloc] initWithFrame:CGRectMake(alignRightX, currentY, stepperWidth, 27)]; [stepper setValueWraps:NO]; [stepper setAutorepeat:YES]; [stepper setMinValue:0]; [stepper setMaxValue:100]; [stepper setDoubleValue:65]; [rightContent addSubview:stepper]; - // Binding [circProg bind:CPValueBinding toObject:stepper withKeyPath:@"doubleValue" options:nil]; currentY += 35.0; - // -- Bar Determinate -- var detProgress = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth, 16)]; [detProgress setStyle:CPProgressIndicatorBarStyle]; [detProgress setIndeterminate:NO]; @@ -334,7 +319,6 @@ [rightContent addSubview:detProgress]; currentY += 20.0; - // -- Bar Indeterminate -- var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX, currentY, controlWidth, 16)]; [progressBar setStyle:CPProgressIndicatorBarStyle]; [progressBar setIndeterminate:YES]; @@ -343,21 +327,17 @@ currentY += gapY; - // Sync box heights var maxHeight = MAX(CGRectGetHeight([leftBox frame]), currentY + 15.0); [leftBox setFrameSize:CGSizeMake(boxWidth, maxHeight)]; [rightBox setFrameSize:CGSizeMake(boxWidth, maxHeight)]; if (isHUD) - { [self _applyHUDStateToView:containerView]; - } } - (void)_buildTableTab:(CPView)containerView isHUD:(BOOL)isHUD { var bounds = [containerView bounds]; - var bottomBarHeight = 32.0; var ruleEditorHeight = 140.0; // --- RULE EDITOR SECTION (Top) --- @@ -367,13 +347,17 @@ _ruleEditor = [[CPRuleEditor alloc] initWithFrame:CGRectMake(0,0, CGRectGetWidth(bounds), ruleEditorHeight)]; [_ruleEditor setRowHeight:25.0]; - [_ruleEditor setFormattingStringsFilename:@"Rules"]; [_ruleEditor setCanRemoveAllRows:YES]; _ruleDelegate = [[RuleDelegate alloc] init]; [_ruleEditor setDelegate:_ruleDelegate]; - [_ruleEditor addRow:self]; + [_ruleEditor setTarget:self]; + [_ruleEditor setAction:@selector(ruleEditorAction:)]; + + // 1. Add the initial row structure + [_ruleEditor addRow:self]; + [ruleContainer setDocumentView:_ruleEditor]; [containerView addSubview:ruleContainer]; @@ -386,17 +370,22 @@ [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(ruleEditorRowsDidChange:) name:CPRuleEditorRowsDidChangeNotification object:_ruleEditor]; - // --- SPLIT VIEW SECTION (Bottom) --- var splitY = ruleEditorHeight + 30.0; - var splitHeight = CGRectGetHeight(bounds) - splitY - bottomBarHeight; + var splitHeight = CGRectGetHeight(bounds) - splitY; var splitView = [[CPSplitView alloc] initWithFrame:CGRectMake(0, splitY, CGRectGetWidth(bounds), splitHeight)]; [splitView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [splitView setVertical:NO]; - // Top Pane: Table - var tableScroll = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(bounds), splitHeight / 2.0)]; + // --- TOP PANE: Table + ButtonBar --- + var topPaneWrapper = [[CPView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(bounds), splitHeight / 2.0)]; + [topPaneWrapper setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + var buttonBarHeight = 32.0; + + // Table ScrollView + var tableScroll = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(bounds), CGRectGetHeight([topPaneWrapper bounds]) - buttonBarHeight)]; [tableScroll setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [tableScroll setAutohidesScrollers:YES]; @@ -407,24 +396,20 @@ [tableView bind:CPSelectionIndexesBinding toObject:_arrayController withKeyPath:@"selectionIndexes" options:nil]; [tableView bind:@"sortDescriptors" toObject:_arrayController withKeyPath:@"sortDescriptors" options:nil]; - // Column 1: Animal var colAnimal = [[CPTableColumn alloc] initWithIdentifier:@"animal"]; [[colAnimal headerView] setStringValue:@"Animal"]; [colAnimal setWidth:150]; [colAnimal setEditable:YES]; - var animalCell = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; [animalCell setEditable:YES]; [colAnimal setDataView:animalCell]; [tableView addTableColumn:colAnimal]; [colAnimal bind:CPValueBinding toObject:_arrayController withKeyPath:@"arrangedObjects.animal" options:nil]; - // Column 2: Legs var colLegs = [[CPTableColumn alloc] initWithIdentifier:@"legs"]; [[colLegs headerView] setStringValue:@"Legs"]; [colLegs setWidth:100]; [colLegs setEditable:YES]; - var legsCell = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; [legsCell setEditable:YES]; [colLegs setDataView:legsCell]; @@ -432,26 +417,10 @@ [colLegs bind:CPValueBinding toObject:_arrayController withKeyPath:@"arrangedObjects.legs" options:nil]; [tableScroll setDocumentView:tableView]; + [topPaneWrapper addSubview:tableScroll]; - // Bottom Pane: Text (Updated for Menu support) - var textScroll = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(bounds), splitHeight / 2.0)]; - [textScroll setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; - [textScroll setAutohidesScrollers:YES]; - - var textView = [[CPTextView alloc] initWithFrame:[textScroll bounds]]; - [textView setEditable:YES]; - // --- ENABLE RICH TEXT TO SUPPORT FONTS/UNDERLINE --- - [textView setRichText:YES]; - [textView setString:@"Select text here and use the 'Format' or 'Edit' menus.\n\n(This is a Rich Text enabled CPTextView)"]; - [textView setFont:[CPFont fontWithName:@"Courier" size:13.0]]; - - [textScroll setDocumentView:textView]; - - [splitView addSubview:tableScroll]; - [splitView addSubview:textScroll]; - // Button Bar - var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(bounds) - bottomBarHeight, CGRectGetWidth(bounds), bottomBarHeight)]; + var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0, CGRectGetHeight([topPaneWrapper bounds]) - buttonBarHeight, CGRectGetWidth(bounds), buttonBarHeight)]; [buttonBar setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin]; if ([buttonBar respondsToSelector:@selector(setHasResizeControl:)]) @@ -470,31 +439,55 @@ [minusBtn bind:CPEnabledBinding toObject:_arrayController withKeyPath:@"canRemove" options:nil]; [buttonBar setButtons:[plusBtn, minusBtn]]; + [topPaneWrapper addSubview:buttonBar]; + + // --- BOTTOM PANE: Text View --- + var textScroll = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(bounds), splitHeight / 2.0)]; + [textScroll setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + [textScroll setAutohidesScrollers:YES]; + + var textView = [[CPTextView alloc] initWithFrame:[textScroll bounds]]; + [textView setEditable:YES]; + [textView setRichText:YES]; + [textView setString:@"Select text here and use the 'Format' or 'Edit' menus.\n\n(This is a Rich Text enabled CPTextView)"]; + [textView setFont:[CPFont fontWithName:@"Courier" size:13.0]]; + + [textScroll setDocumentView:textView]; + + [splitView addSubview:topPaneWrapper]; + [splitView addSubview:textScroll]; - [containerView addSubview:buttonBar]; [containerView addSubview:splitView]; if (isHUD) { - // Apply HUD state to everything in the view hierarchy (Buttons, RuleEditor, Table, ScrollViews) [self _applyHUDStateToView:containerView]; - - // Specific overrides [textView setBackgroundColor:[CPColor blackColor]]; [textView setTextColor:[CPColor whiteColor]]; [_predicateField setTextColor:[CPColor whiteColor]]; - [self _applyHUDStateToView:_ruleEditor]; } } +// This method catches the action sent by the RuleEditor when the text field (or other control) changes. +- (void)ruleEditorAction:(id)sender +{ + // Simply delegate to the update logic + [self ruleEditorRowsDidChange:nil]; +} + - (void)ruleEditorRowsDidChange:(CPNotification)note { var predicate = [_ruleEditor predicate]; + + // Update the debug text if (predicate) [_predicateField setStringValue:[predicate predicateFormat]]; else [_predicateField setStringValue:@"(Incomplete Predicate)"]; + + // Apply the filter to the table's data source + [_arrayController setFilterPredicate:predicate]; } // --- SIZES TAB BUILDER --- @@ -505,9 +498,7 @@ [self _addSizeColumnTo:containerView atX:280.0 controlSize:CPMiniControlSize title:@"Mini size"]; if (isHUD) - { [self _applyHUDStateToView:containerView]; - } } - (void)_addSizeColumnTo:(CPView)parentView atX:(float)xPos controlSize:(CPControlSize)aSize title:(CPString)title @@ -516,14 +507,12 @@ var rowHeight = 40.0; var width = (aSize == CPRegularControlSize) ? 100.0 : ((aSize == CPSmallControlSize) ? 90.0 : 80.0); - // 1. Title Label var label = [CPTextField labelWithTitle:title]; [label setFrameOrigin:CGPointMake(xPos, y)]; [label setFont:[CPFont systemFontOfSize:13.0]]; [parentView addSubview:label]; y += 35.0; - // 2. PopUp Button var popUp = [[CPPopUpButton alloc] initWithFrame:CGRectMake(xPos, y, width, 24)]; [popUp addItemWithTitle:@"Item 1"]; [popUp addItemWithTitle:@"Item 2"]; @@ -531,7 +520,6 @@ [parentView addSubview:popUp]; y += rowHeight; - // 3. Text Field var tf = [[CPTextField alloc] initWithFrame:CGRectMake(xPos, y, width, 24)]; [tf setStringValue:@"Input"]; [tf setBezeled:YES]; @@ -540,13 +528,11 @@ [parentView addSubview:tf]; y += rowHeight; - // 4. Stepper var stepper = [[CPStepper alloc] initWithFrame:CGRectMake(xPos, y, 13, 23)]; [stepper setControlSize:aSize]; [parentView addSubview:stepper]; y += rowHeight; - // 5. Date Picker var dpWidth = width + (aSize == CPRegularControlSize ? 20 : 15); var dp = [[CPDatePicker alloc] initWithFrame:CGRectMake(xPos, y, dpWidth, 28)]; [dp setControlSize:aSize]; @@ -556,7 +542,6 @@ [parentView addSubview:dp]; y += rowHeight; - // 6. Checkbox var cb = [CPCheckBox checkBoxWithTitle:@"Check"]; [cb setFrameOrigin:CGPointMake(xPos, y)]; [cb setControlSize:aSize]; @@ -564,14 +549,12 @@ [parentView addSubview:cb]; y += rowHeight; - // 7. Standard Button var btn = [[CPButton alloc] initWithFrame:CGRectMake(xPos, y, width, 24)]; [btn setTitle:@"Button"]; [btn setControlSize:aSize]; [parentView addSubview:btn]; y += rowHeight; - // 8. Textured Button var texBtn = [[CPButton alloc] initWithFrame:CGRectMake(xPos, y, width, 24)]; [texBtn setTitle:@"Textured"]; [texBtn setBezelStyle:CPTexturedSquareBezelStyle]; @@ -579,7 +562,6 @@ [parentView addSubview:texBtn]; y += rowHeight; - // 9. Round Textured Button var roundTexBtn = [[CPButton alloc] initWithFrame:CGRectMake(xPos, y, width, 24)]; [roundTexBtn setTitle:@"Round"]; [roundTexBtn setBezelStyle:CPTexturedRoundedBezelStyle]; @@ -587,7 +569,6 @@ [parentView addSubview:roundTexBtn]; y += rowHeight; - // 10. Radio Buttons var rad1 = [CPRadio radioWithTitle:@"Radio"]; [rad1 setFrameOrigin:CGPointMake(xPos, y)]; [rad1 setControlSize:aSize]; @@ -603,7 +584,6 @@ [parentView addSubview:rad2]; y += rowHeight; - // 11. Small Bottom PopUp var popUp2 = [[CPPopUpButton alloc] initWithFrame:CGRectMake(xPos, y, width, 24)]; [popUp2 setPullsDown:YES]; [popUp2 setControlSize:aSize]; @@ -666,16 +646,51 @@ { } +// 1. Determine how many items are in the popup for a specific row type - (int)ruleEditor:(CPRuleEditor)editor numberOfChildrenForCriterion:(id)criterion withRowType:(CPRuleEditorRowType)rowType { - if (criterion == nil) return 2; - if (criterion == @"animal") return 2; - if (criterion == @"legs") return 3; - return 0; + // --- COMPOUND ROW (The container: "Any/All of the following...") --- + if (rowType === CPRuleEditorRowTypeCompound) + { + // Root: The dropdown options ("Any", "All") + if (criterion == nil) return 2; + + // Children of Any/All: The static text ("of the following are true") + if (criterion == CPOrPredicateType || criterion == CPAndPredicateType) return 1; + + return 0; + } + + // --- SIMPLE ROW (The actual rules: "Animal", "Legs"...) --- + if (rowType === CPRuleEditorRowTypeSimple) + { + if (criterion == nil) return 2; // animal, legs + if (criterion == @"animal") return 2; // contains, is + if (criterion == @"legs") return 3; // >, <, == + + // Operator's child is the value placeholder + if ([self isOperator:criterion]) return 1; + } + + return 0; } +// 2. Return the actual item for the popup - (id)ruleEditor:(CPRuleEditor)editor child:(int)index forCriterion:(id)criterion withRowType:(CPRuleEditorRowType)rowType { + // --- COMPOUND ROW --- + if (rowType === CPRuleEditorRowTypeCompound) + { + // Root: Return the predicate constants + if (criterion == nil) { + return (index == 0) ? CPOrPredicateType : CPAndPredicateType; + } + + // Return a marker for the static text + return @"_static_text_"; + } + + // --- SIMPLE ROW --- if (criterion == nil) { if (index == 0) return @"animal"; return @"legs"; @@ -689,11 +704,33 @@ if (index == 1) return @"<"; return @"=="; } + if ([self isOperator:criterion]) { + return @"_value_"; + } return nil; } +// 3. What the user sees on screen - (id)ruleEditor:(CPRuleEditor)editor displayValueForCriterion:(id)criterion inRow:(int)row { + // --- COMPOUND DISPLAY --- + if (criterion === CPOrPredicateType) return @"Any"; + if (criterion === CPAndPredicateType) return @"All"; + // This creates the static text label next to the dropdown + if (criterion === @"_static_text_") return @"of the following are true"; + + // --- SIMPLE DISPLAY --- + if (criterion == @"_value_") + { + var field = [[CPTextField alloc] initWithFrame:CGRectMake(0, 0, 100, 24)]; + [field setEditable:YES]; + [field setBezeled:YES]; + [field setBackgroundColor:[CPColor whiteColor]]; + [field setPlaceholderString:@"Value"]; + [field setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + return field; + } + if (criterion == @"animal") return @"Animal Name"; if (criterion == @"legs") return @"Leg Count"; if (criterion == @"contains") return @"contains"; @@ -701,9 +738,75 @@ if (criterion == @">") return @"is greater than"; if (criterion == @"<") return @"is less than"; if (criterion == @"==") return @"is equal to"; + return criterion; } +- (BOOL)isOperator:(id)criterion +{ + return (criterion == @"contains" || criterion == @"is" || + criterion == @">" || criterion == @"<" || criterion == @"=="); +} + +// 4. Convert UI to Predicate +- (CPDictionary)ruleEditor:(CPRuleEditor)editor predicatePartsForCriterion:(id)criterion withDisplayValue:(id)value inRow:(int)row +{ + var result = [CPMutableDictionary dictionary]; + + // Handle Compound Type (Any vs All) + if (criterion === CPOrPredicateType || criterion === CPAndPredicateType) + { + [result setObject:criterion forKey:CPRuleEditorPredicateCompoundType]; + return result; + } + + // Ignore the static text part + if (criterion === @"_static_text_") return nil; + + // --- SIMPLE ROW LOGIC --- + [result setObject:CPDirectPredicateModifier forKey:CPRuleEditorPredicateComparisonModifier]; + [result setObject:CPCaseInsensitivePredicateOption forKey:CPRuleEditorPredicateOptions]; + + if (criterion == @"animal" || criterion == @"legs") + { + [result setObject:[CPExpression expressionForKeyPath:criterion] + forKey:CPRuleEditorPredicateLeftExpression]; + } + else if ([self isOperator:criterion]) + { + var operatorType = CPEqualToPredicateOperatorType; + + if (criterion == @"contains") operatorType = CPContainsPredicateOperatorType; + else if (criterion == @"is") operatorType = CPEqualToPredicateOperatorType; + else if (criterion == @"==") operatorType = CPEqualToPredicateOperatorType; + else if (criterion == @">") operatorType = CPGreaterThanPredicateOperatorType; + else if (criterion == @"<") operatorType = CPLessThanPredicateOperatorType; + + [result setObject:operatorType forKey:CPRuleEditorPredicateOperatorType]; + } + else if (criterion == @"_value_") + { +debugger + if ([value respondsToSelector:@selector(validateEditing)]) + [value validateEditing]; + + var stringValue = [value stringValue]; + var typedValue = stringValue; + + // Type Coercion for Legs + var criteria = [editor criteriaForRow:row]; + if ([criteria count] > 0 && [criteria objectAtIndex:0] == @"legs") + { + typedValue = [value intValue]; + } + + [result setObject:[CPExpression expressionForConstantValue:typedValue] + forKey:CPRuleEditorPredicateRightExpression]; + } + + return result; +} + @end @@ -725,7 +828,6 @@ { windows = []; - // REDUCED height to fix aesthetic "too high" issue and increased width slightly for balance var winWidth = 480.0, winHeight = 450.0, padding = 20.0; @@ -746,7 +848,6 @@ [wc4 showWindow:self]; [windows addObject:wc4]; - // --- BUILD MENU FROM EXAMPLE --- var mainMenu = [CPApp mainMenu]; while ([mainMenu numberOfItems] > 0)