more sourcelist styling (Cocoa)

Sat, 11 Oct 2025 10:23:22 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 11 Oct 2025 10:23:22 +0200
changeset 831
32a4415dc69a
parent 830
13896bdaa151
child 832
7adbd6b7bf7c

more sourcelist styling (Cocoa)

make/xcode/toolkit/toolkit/main.m file | annotate | diff | comparison | revisions
ui/cocoa/list.h file | annotate | diff | comparison | revisions
ui/cocoa/list.m file | annotate | diff | comparison | revisions
--- a/make/xcode/toolkit/toolkit/main.m	Fri Oct 10 15:54:01 2025 +0200
+++ b/make/xcode/toolkit/toolkit/main.m	Sat Oct 11 10:23:22 2025 +0200
@@ -40,6 +40,7 @@
     UiList *list1;
     UiList *list2;
     UiList *sidebar_list;
+    UiList *sidebar_list2;
 } MyDocument;
 
 MyDocument* create_doc(void) {
@@ -69,6 +70,12 @@
     ui_list_append(doc->sidebar_list, "Item 5");
     ui_list_append(doc->sidebar_list, "Item 6");
     
+    doc->sidebar_list2 = ui_list_new(ctx, "sidebar_list2");
+    ui_list_append(doc->sidebar_list2, "Item 1");
+    ui_list_append(doc->sidebar_list2, "Item 2");
+    ui_list_append(doc->sidebar_list2, "Item 3");
+    ui_list_append(doc->sidebar_list2, "Item 4");
+    
     return doc;
 }
 
@@ -114,11 +121,19 @@
     MyDocument *doc = create_doc();
     ui_attach_document(obj->ctx, doc);
     
-    UiSubList sublist = {0};
-    sublist.header = "Test";
-    sublist.value = doc->sidebar_list;
+    UiSubList sublist[2];
+    sublist[0].header = "iCloud";
+    sublist[0].value = doc->sidebar_list;
+    sublist[0].separator = FALSE;
+    sublist[0].varname = NULL;
+    sublist[0].userdata = NULL;
+    sublist[1].header = "Tags";
+    sublist[1].value = doc->sidebar_list2;
+    sublist[1].separator = FALSE;
+    sublist[1].varname = NULL;
+    sublist[1].userdata = NULL;
     ui_sidebar(obj) {
-        ui_sourcelist(obj, .fill = TRUE, .sublists = &sublist, .numsublists = 1);
+        ui_sourcelist(obj, .fill = TRUE, .sublists = sublist, .numsublists = 2);
     }
     
     
@@ -144,23 +159,6 @@
     ui_grid(obj, .columnspacing = 10, .rowspacing = 10) {
         ui_table(obj, .fill = UI_ON, .varname = "list1", .model = model, .getvalue = table_getvalue, .onactivate = action_list_activate, .onselection = action_list_selection, .multiselection = TRUE);
     }
-    
-    
-    ui_vbox(obj, .spacing = 0, .fill = UI_OFF) {
-        ui_combobox(obj, .varname = "list2");
-        
-        ui_llabel(obj, .label = "Left Label");
-        ui_rlabel(obj, .label = "Right Label");
-        
-        ui_textfield(obj, .varname = "textfield1");
-        ui_radiobutton(obj, .label = "V Button 1", .varname = "radio1");
-        ui_radiobutton(obj, .label = "V Button 2", .varname = "radio1");
-        ui_radiobutton(obj, .label = "V Button 3", .varname = "radio1");
-        
-        ui_radiobutton(obj, .label = "V Button 1 R2", .varname = "radio2");
-        ui_radiobutton(obj, .label = "V Button 2 R2", .varname = "radio2");
-        ui_radiobutton(obj, .label = "V Button 3 R2", .varname = "radio2");
-    }
     */
     
     ui_show(obj);
--- a/ui/cocoa/list.h	Fri Oct 10 15:54:01 2025 +0200
+++ b/ui/cocoa/list.h	Sat Oct 11 10:23:22 2025 +0200
@@ -107,6 +107,9 @@
 
 @interface UiSourceListRow : NSTableRowView
 
+@property NSTrackingArea *trackingArea;
+@property NSView *disclosureButton;
+@property BOOL hover;
 
 @end
 
--- a/ui/cocoa/list.m	Fri Oct 10 15:54:01 2025 +0200
+++ b/ui/cocoa/list.m	Sat Oct 11 10:23:22 2025 +0200
@@ -416,20 +416,12 @@
     outline.rowSizeStyle = NSTableViewRowSizeStyleDefault;
     outline.usesAutomaticRowHeights = YES;
     outline.indentationPerLevel = 0;
-    outline.indentationMarkerFollowsCell = NO;
-    
-    outline.floatsGroupRows = NO;
-    outline.indentationPerLevel = 0.0;
-    outline.indentationMarkerFollowsCell = NO;
-    outline.selectionHighlightStyle = NSTableViewSelectionHighlightStyleRegular;
-
-    // Hide the disclosure triangle
-    //outline.disclosureButtonImage = nil;
     
     outline.style = NSTableViewStyleSourceList;
-
+    outline.selectionHighlightStyle = NSTableViewSelectionHighlightStyleSourceList;
+    
     // Make background transparent so vibrancy shows through
-    outline.backgroundColor = [NSColor clearColor];
+    //outline.backgroundColor = [NSColor clearColor];
     scrollview.drawsBackground = NO;
 
     scrollview.documentView = outline;
@@ -458,6 +450,8 @@
     outline.dataSource = data;
     outline.delegate = data;
     
+    [data update:-1];
+    
     objc_setAssociatedObject(outline, "ui_datasource", data, OBJC_ASSOCIATION_RETAIN);
     
     return (__bridge void*)scrollview;
@@ -498,6 +492,7 @@
     }
     
     [_outlineView reloadData];
+    [_outlineView expandItem:nil expandChildren:YES];
 }
 
 // NSOutlineViewDataSource implementation
@@ -525,10 +520,12 @@
 }
 
 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
+    /*
     UiSourceListItem *i = item;
     if([[tableColumn identifier] isEqualToString:@"label"]) {
         return i.label;
     }
+    */
     return nil;
 }
 
@@ -557,22 +554,50 @@
     cell.imageView = iconView;
 
     // Label
-    NSTextField *textField = [NSTextField labelWithString:@""];
+    //NSTextField *textField = [NSTextField labelWithString:@""];
+    NSTextField *textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
     textField.translatesAutoresizingMaskIntoConstraints = NO;
+    textField.bezeled = NO;
+    textField.editable = NO;
+    textField.drawsBackground = NO;
+    textField.selectable = NO;
+    textField.lineBreakMode = NSLineBreakByTruncatingTail;
+    
+    
     [cell addSubview:textField];
     cell.textField = textField;
-
-    // Layout constraints
-    [NSLayoutConstraint activateConstraints:@[
-        [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
-        [iconView.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+    
+    if([i isSection]) {
+        NSFont *font = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]*0.85];
+        //NSFont *font = [NSFont preferredFontForTextStyle:NSFontTextStyleCaption1 options:@{}];
+        NSDictionary *attrs = @{
+            NSFontAttributeName: font,
+            NSForegroundColorAttributeName: [NSColor tertiaryLabelColor]
+        };
+        textField.attributedStringValue = [[NSAttributedString alloc] initWithString:i.label attributes:attrs];
+        
+        // Layout constraints
+        [NSLayoutConstraint activateConstraints:@[
+            [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [iconView.bottomAnchor constraintEqualToAnchor:cell.bottomAnchor constant:-1],
 
-        [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
-        [textField.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
-        [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
-    ]];
-    
-    textField.stringValue = i.label;
+            [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [textField.bottomAnchor constraintEqualToAnchor:cell.bottomAnchor constant:-1],
+            [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
+        ]];
+    } else {
+        textField.stringValue = i.label;
+        
+        // Layout constraints
+        [NSLayoutConstraint activateConstraints:@[
+            [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [iconView.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+
+            [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [textField.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+            [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
+        ]];
+    }
     
     return cell;
 }
@@ -590,6 +615,21 @@
     return [i isSection] ? NO : YES;
 }
 
+- (CGFloat) outlineView:(NSOutlineView *) outlineView
+      heightOfRowByItem:(id) item
+{
+    UiSourceListItem *i = item;
+    CGFloat rowHeight = outlineView.rowHeight;
+    if([i isSection]) {
+        if(i.index == 0) {
+            rowHeight -= 12;
+        } else {
+            rowHeight += 4;
+        }
+    }
+    return rowHeight;
+}
+
 - (void) outlineViewSelectionDidChange:(NSNotification *) notification {
     UiEvent event;
     event.obj = _obj;
@@ -690,6 +730,21 @@
             NSRect frame = subview.frame;
             frame.origin.x = self.bounds.size.width - frame.size.width - 16.0;
             subview.frame = frame;
+            
+            if(!_hover) {
+                subview.hidden = YES;
+            }
+            
+            if(subview != _disclosureButton) {
+                // init disclosure button
+                _disclosureButton = (NSButton*)subview;
+                if ([subview isKindOfClass:[NSButton class]]) {
+                    NSButton *button = (NSButton*)subview;
+                    button.contentTintColor = [NSColor tertiaryLabelColor];
+                }
+            }
+            
+            
         } else if ([subview.identifier isEqualToString:@"cell"]) {
             NSRect frame = subview.frame;
             frame.origin.x = 16;
@@ -698,4 +753,28 @@
     }
 }
 
+- (void)updateTrackingAreas {
+    [super updateTrackingAreas];
+    if(_trackingArea != nil) {
+        [self removeTrackingArea:_trackingArea];
+    }
+    _trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds
+                                                 options:NSTrackingMouseEnteredAndExited |
+                                                         NSTrackingActiveInActiveApp |
+                                                         NSTrackingInVisibleRect
+                                                   owner:self
+                                                userInfo:nil];
+    [self addTrackingArea:_trackingArea];
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+    _hover = YES;
+    _disclosureButton.hidden = NO;
+}
+
+- (void)mouseExited:(NSEvent *)event {
+    _hover = NO;
+    _disclosureButton.hidden = YES;
+}
+
 @end

mercurial