diff --git a/AppKit/Themes/Aristo3/ThemeDescriptors.j b/AppKit/Themes/Aristo3/ThemeDescriptors.j index c9e861973..8fd6a410c 100644 --- a/AppKit/Themes/Aristo3/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo3/ThemeDescriptors.j @@ -8280,11 +8280,13 @@ var themedButtonValues = nil, fontColor = [CPColor colorWithWhite:150 / 255 alpha:1], // --- HUD Colors --- - // Alternating rows: Transparent and very faint white + // Matches themedTableView: Clear + Very Faint White (5%) hudBackgroundColors = [[CPColor clearColor], [CPColor colorWithWhite:1.0 alpha:0.05]], - // Selection: Semi-transparent white + + // Selection: Semi-transparent white (20%) hudSelectedRowColor = [CPColor colorWithWhite:1.0 alpha:0.2], - // Borders: Very faint white lines + + // Borders: Very faint white lines (10%) to prevent "bright stripes" effect hudSliceBorderColor = [CPColor colorWithWhite:1.0 alpha:0.1], hudFontColor = [CPColor whiteColor], @@ -8317,7 +8319,7 @@ var themedButtonValues = nil, buttonAddImageHUD = [CPImage imageWithCSSDictionary:@{ "-webkit-mask-image": svgPlus, "mask-image": svgPlus, - "background-color": @"#FFFFFF", // White + "background-color": @"#FFFFFF", "-webkit-mask-size": "contain", "mask-size": "contain", "-webkit-mask-repeat": "no-repeat", @@ -8329,7 +8331,7 @@ var themedButtonValues = nil, buttonRemoveImageHUD = [CPImage imageWithCSSDictionary:@{ "-webkit-mask-image": svgMinus, "mask-image": svgMinus, - "background-color": @"#FFFFFF", // White + "background-color": @"#FFFFFF", "-webkit-mask-size": "contain", "mask-size": "contain", "-webkit-mask-repeat": "no-repeat", @@ -8354,19 +8356,22 @@ var themedButtonValues = nil, [@"vertical-alignment", CPCenterVerticalTextAlignment], // --- HUD Mappings --- + // Alternating Row Colors (Zebra) [@"alternating-row-colors", hudBackgroundColors, CPThemeStateHUD], + + // Selection Color [@"selected-color", hudSelectedRowColor, [CPThemeStateHUD, CPThemeStateNormal]], [@"selected-color", hudSelectedRowColor, [CPThemeStateHUD, CPThemeStateDisabled]], - // HUD Borders + // Row Dividers (Borders) - Must be dimmed for HUD [@"slice-top-border-color", hudSliceBorderColor, CPThemeStateHUD], [@"slice-bottom-border-color", hudSliceBorderColor, CPThemeStateHUD], [@"slice-last-bottom-border-color", hudSliceBorderColor, CPThemeStateHUD], - // HUD Font + // Font Color [@"font-color", hudFontColor, CPThemeStateHUD], - // HUD Icons + // Buttons [@"add-image", buttonAddImageHUD, [CPThemeStateHUD, CPThemeStateNormal]], [@"remove-image", buttonRemoveImageHUD, [CPThemeStateHUD, CPThemeStateNormal]] ]; diff --git a/Tests/Manual/ThemeKitchenSinkA3/AppController.j b/Tests/Manual/ThemeKitchenSinkA3/AppController.j index 44ec56cfe..6130fdb21 100644 --- a/Tests/Manual/ThemeKitchenSinkA3/AppController.j +++ b/Tests/Manual/ThemeKitchenSinkA3/AppController.j @@ -3,8 +3,8 @@ * KitchenSink in Code * * Created by Daniel Böhringer 2026. - * Modified for TabView, SplitView, Control Sizes support & Grouped Boxes. - * Update: Fixed Table Editing (Column Editable Property). + * Modified for TabView, SplitView, Control Sizes, Grouped Boxes, Rules & Menus. + * Update: Added Edit and Format Menus (Cut/Copy/Paste/Font Panel). */ @import @@ -90,7 +90,7 @@ [item1 setView:controlsView]; [tabView addTabViewItem:item1]; - // --- TAB 2: Table & Text Split (Now with Rule Editor) --- + // --- TAB 2: Table & Text Split (Rules & Editable Text) --- var item2 = [[CPTabViewItem alloc] initWithIdentifier:@"Table"]; [item2 setLabel:@"Data & Rules"]; @@ -118,13 +118,13 @@ boxWidth = 190.0, innerX = 15.0, startY = 10.0, - gapY = 35.0, // Uniform vertical spacing + gapY = 35.0, controlWidth = 150.0; // ------------------------------------------------------ // LEFT BOX: Standard Controls // ------------------------------------------------------ - var leftBox = [[CPBox alloc] initWithFrame:CGRectMake(boxMargin, 15.0, boxWidth, 100)]; // Height set later + var leftBox = [[CPBox alloc] initWithFrame:CGRectMake(boxMargin, 15.0, boxWidth, 100)]; [leftBox setTitle:@"Standard Controls"]; [leftBox setAutoresizingMask:CPViewMaxXMargin | CPViewMinYMargin]; [containerView addSubview:leftBox]; @@ -246,8 +246,7 @@ [rightContent addSubview:cbBoth]; currentY += gapY; - // 3. Spinners (Indeterminate & Determinate/Circular) - // A. Indeterminate Spinner + // 3. Spinners var spinner = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX + 50, currentY - 4, 32, 32)]; [spinner setStyle:CPProgressIndicatorSpinningStyle]; [spinner setIndeterminate:YES]; @@ -255,10 +254,9 @@ [spinner startAnimation:self]; [rightContent addSubview:spinner]; - // B. Circular Progress Bar (Determinate Spinner) var circProg = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX + 90, currentY - 4, 32, 32)]; [circProg setStyle:CPProgressIndicatorSpinningStyle]; - [circProg setIndeterminate:NO]; // Determinate makes it a circular progress bar usually + [circProg setIndeterminate:NO]; [circProg setControlSize:CPRegularControlSize]; [circProg setDoubleValue:65.0]; [circProg setMaxValue:100.0]; @@ -275,7 +273,6 @@ [stepper setDoubleValue:65]; [rightContent addSubview:stepper]; - // Bind Circular Progress to Stepper [circProg bind:CPValueBinding toObject:stepper withKeyPath:@"doubleValue" options:nil]; var detProgress = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(innerX + 25, currentY + 5.5, 125, 16)]; @@ -298,7 +295,7 @@ [rightContent addSubview:radio2]; [radio1 setTarget:self]; [radio1 setAction:@selector(dummyAction:)]; [radio2 setTarget:self]; [radio2 setAction:@selector(dummyAction:)]; - currentY += gapY * 1.5; // Extra space for radios + currentY += gapY * 1.5; // 6. Level Indicator var levelInd = [[CPLevelIndicator alloc] initWithFrame:CGRectMake(innerX, currentY, 130, 18)]; @@ -355,24 +352,16 @@ _ruleEditor = [[CPRuleEditor alloc] initWithFrame:CGRectMake(0,0, CGRectGetWidth(bounds), ruleEditorHeight)]; [_ruleEditor setRowHeight:25.0]; - [_ruleEditor setFormattingStringsFilename:@"Rules"]; // Not used without resources, but good practice + [_ruleEditor setFormattingStringsFilename:@"Rules"]; [_ruleEditor setCanRemoveAllRows:YES]; - // Create and attach delegate _ruleDelegate = [[RuleDelegate alloc] init]; [_ruleEditor setDelegate:_ruleDelegate]; - - // Add an initial row [_ruleEditor addRow:self]; [ruleContainer setDocumentView:_ruleEditor]; [containerView addSubview:ruleContainer]; - // Predicate Label (To show output) - var predLabel = [CPTextField labelWithTitle:@"Predicate:"]; - [predLabel setFrame:CGRectMake(10, ruleEditorHeight - 30, 60, 20)]; - //[containerView addSubview:predLabel]; // Optional - _predicateField = [[CPTextField alloc] initWithFrame:CGRectMake(10, ruleEditorHeight + 5, CGRectGetWidth(bounds) - 20, 20)]; [_predicateField setEditable:NO]; [_predicateField setFont:[CPFont systemFontOfSize:11.0]]; @@ -380,7 +369,6 @@ [_predicateField setStringValue:@"(Predicate will appear here)"]; [containerView addSubview:_predicateField]; - // Hook up notification for changes to update text [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(ruleEditorRowsDidChange:) name:CPRuleEditorRowsDidChangeNotification object:_ruleEditor]; @@ -408,7 +396,7 @@ var colAnimal = [[CPTableColumn alloc] initWithIdentifier:@"animal"]; [[colAnimal headerView] setStringValue:@"Animal"]; [colAnimal setWidth:150]; - [colAnimal setEditable:YES]; // <--- CRITICAL FOR EDITING + [colAnimal setEditable:YES]; var animalCell = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; [animalCell setEditable:YES]; @@ -420,7 +408,7 @@ var colLegs = [[CPTableColumn alloc] initWithIdentifier:@"legs"]; [[colLegs headerView] setStringValue:@"Legs"]; [colLegs setWidth:100]; - [colLegs setEditable:YES]; // <--- CRITICAL FOR EDITING + [colLegs setEditable:YES]; var legsCell = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; [legsCell setEditable:YES]; @@ -430,14 +418,16 @@ [tableScroll setDocumentView:tableView]; - // Bottom Pane: Text + // 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]; - [textView setString:@"1. Use the Rule Editor above to build a predicate.\n2. Edit table cells directly.\n3. Add/Remove rows below."]; + // --- 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]]; if (isHUD) @@ -641,7 +631,7 @@ [item setPaletteLabel:@"Colors"]; [item setImage:[CPImage imageNamed:CPImageNameColorPanel]]; [item setAlternateImage:[CPImage imageNamed:CPImageNameColorPanelHighlighted]]; - [item setTarget:self]; + [item setTarget:CPApp]; [item setAction:@selector(orderFrontColorPanel:)]; } return item; @@ -652,7 +642,6 @@ // -------------------------------------------------------------------------------- // RuleDelegate -// Implements the logic for the CPRuleEditor // -------------------------------------------------------------------------------- @implementation RuleDelegate : CPObject @@ -661,41 +650,27 @@ - (int)ruleEditor:(CPRuleEditor)editor numberOfChildrenForCriterion:(id)criterion withRowType:(CPRuleEditorRowType)rowType { - // Root Level: 2 options (Animal, Legs) - if (criterion == nil) - return 2; - - // Second Level: Operators - if (criterion == @"animal") - return 2; // contains, is - if (criterion == @"legs") - return 3; // =, >, < - - // Third Level: The Value input field (Leaf node) + if (criterion == nil) return 2; + if (criterion == @"animal") return 2; + if (criterion == @"legs") return 3; return 0; } - (id)ruleEditor:(CPRuleEditor)editor child:(int)index forCriterion:(id)criterion withRowType:(CPRuleEditorRowType)rowType { - if (criterion == nil) - { + if (criterion == nil) { if (index == 0) return @"animal"; return @"legs"; } - - if (criterion == @"animal") - { + if (criterion == @"animal") { if (index == 0) return @"contains"; - return @"is"; // like (Like) + return @"is"; } - - if (criterion == @"legs") - { + if (criterion == @"legs") { if (index == 0) return @">"; if (index == 1) return @"<"; return @"=="; } - return nil; } @@ -703,14 +678,11 @@ { if (criterion == @"animal") return @"Animal Name"; if (criterion == @"legs") return @"Leg Count"; - if (criterion == @"contains") return @"contains"; if (criterion == @"is") return @"is"; - if (criterion == @">") return @"is greater than"; if (criterion == @"<") return @"is less than"; if (criterion == @"==") return @"is equal to"; - return criterion; } @@ -726,6 +698,11 @@ CPArray windows; } +- (void)orderFrontFontPanel:(id)sender +{ + [[CPFontManager sharedFontManager] orderFrontFontPanel:self]; +} + - (void)applicationDidFinishLaunching:(CPNotification)aNotification { windows = []; @@ -750,6 +727,33 @@ [wc4 showWindow:self]; [windows addObject:wc4]; + // --- BUILD MENU FROM EXAMPLE --- + var mainMenu = [CPApp mainMenu]; + + while ([mainMenu numberOfItems] > 0) + [mainMenu removeItemAtIndex:0]; + + // Edit Menu + var item = [mainMenu insertItemWithTitle:@"Edit" action:nil keyEquivalent:nil atIndex:0], + editMenu = [[CPMenu alloc] initWithTitle:@"Edit Menu"]; + + [editMenu addItemWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; + [editMenu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; + [editMenu addItemWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; + [editMenu addItemWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; + [editMenu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; + [editMenu addItemWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; + [editMenu addItemWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; + + [mainMenu setSubmenu:editMenu forItem:item]; + + // Format Menu + item = [mainMenu insertItemWithTitle:@"Format" action:nil keyEquivalent:nil atIndex:0]; + var formatMenu = [[CPMenu alloc] initWithTitle:@"Format Menu"]; + [formatMenu addItemWithTitle:@"Font panel" action:@selector(orderFrontFontPanel:) keyEquivalent:@"f"]; + [formatMenu addItemWithTitle:@"Underline" action:@selector(underline:) keyEquivalent:@"u"]; + [mainMenu setSubmenu:formatMenu forItem:item]; + [CPMenu setMenuBarVisible:YES]; }