changeset 2383:a410d42d9e36

iOS: Implement classes for encapsulating immutable UIImage and UIMenu so the handles don't change when we recreate these objects. More conversion to iOS.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Sun, 21 Mar 2021 21:48:19 +0000
parents 41a04e6b3e8e
children 32ebd33be56b
files ios/dw.m
diffstat 1 files changed, 261 insertions(+), 372 deletions(-) [+]
line wrap: on
line diff
--- a/ios/dw.m	Sun Mar 21 02:37:37 2021 +0000
+++ b/ios/dw.m	Sun Mar 21 21:48:19 2021 +0000
@@ -588,15 +588,30 @@
 @end
 
 API_AVAILABLE(ios(13.0))
-@interface DWMenuItem : UIMenuItem
+@interface DWMenuItem : UICommand
 {
     int check;
+    unsigned long tag;
 }
 -(void)setCheck:(int)input;
+-(void)setTag:(unsigned long)input;
 -(int)check;
+-(unsigned long)tag;
 -(void)dealloc;
 @end
 
+API_AVAILABLE(ios(13.0))
+@interface DWMenu : NSObject
+{
+    UIMenu *menu;
+}
+-(void)setMenu:(UIMenu *)input;
+-(UIMenu *)menu;
+-(DWMenuItem *)itemWithTag:(unsigned long)tag;
+-(void)dealloc;
+@end
+
+
 /* So basically to implement our event handlers...
  * it looks like we are going to have to subclass
  * basically everything.  Was hoping to add methods
@@ -622,7 +637,7 @@
 -(BOOL)isFlipped;
 -(void)mouseDown:(UIEvent *)theEvent;
 -(void)mouseUp:(UIEvent *)theEvent;
--(UIMenu *)menuForEvent:(UIEvent *)theEvent;
+-(DWMenu *)menuForEvent:(UIEvent *)theEvent;
 -(void)rightMouseUp:(UIEvent *)theEvent;
 -(void)otherMouseDown:(UIEvent *)theEvent;
 -(void)otherMouseUp:(UIEvent *)theEvent;
@@ -669,7 +684,7 @@
 -(BOOL)isFlipped { return YES; }
 -(void)mouseDown:(UIEvent *)theEvent { _event_handler(self, (void *)1, 3); }
 -(void)mouseUp:(UIEvent *)theEvent { _event_handler(self, (void *)1, 4); }
--(UIMenu *)menuForEvent:(UIEvent *)theEvent { _event_handler(self, (void *)2, 3); return nil; }
+-(DWMenu *)menuForEvent:(UIEvent *)theEvent { _event_handler(self, (void *)2, 3); return nil; }
 -(void)rightMouseUp:(UIEvent *)theEvent { _event_handler(self, (void *)2, 4); }
 -(void)otherMouseDown:(UIEvent *)theEvent { _event_handler(self, (void *)3, 3); }
 -(void)otherMouseUp:(UIEvent *)theEvent { _event_handler(self, (void *)3, 4); }
@@ -747,7 +762,7 @@
 -(UIImage *)cachedDrawingRep;
 -(void)mouseDown:(UIEvent *)theEvent;
 -(void)mouseUp:(UIEvent *)theEvent;
--(UIMenu *)menuForEvent:(UIEvent *)theEvent;
+-(DWMenu *)menuForEvent:(UIEvent *)theEvent;
 -(void)rightMouseUp:(UIEvent *)theEvent;
 -(void)otherMouseDown:(UIEvent *)theEvent;
 -(void)otherMouseUp:(UIEvent *)theEvent;
@@ -795,7 +810,7 @@
         _event_handler(self, theEvent, 3);
 }
 -(void)mouseUp:(UIEvent *)theEvent { _event_handler(self, theEvent, 4); }
--(UIMenu *)menuForEvent:(UIEvent *)theEvent { _event_handler(self, theEvent, 3); return nil; }
+-(DWMenu *)menuForEvent:(UIEvent *)theEvent { _event_handler(self, theEvent, 3); return nil; }
 -(void)rightMouseUp:(UIEvent *)theEvent { _event_handler(self, theEvent, 4); }
 -(void)otherMouseDown:(UIEvent *)theEvent { _event_handler(self, theEvent, 3); }
 -(void)otherMouseUp:(UIEvent *)theEvent { _event_handler(self, theEvent, 4); }
@@ -981,11 +996,11 @@
 /* Subclass for a top-level window */
 @interface DWView : DWBox /* <UIWindowDelegate> */
 {
-    UIMenu *windowmenu;
+    DWMenu *windowmenu;
     CGSize oldsize;
 }
 -(BOOL)windowShouldClose:(id)sender;
--(void)setMenu:(UIMenu *)input;
+-(void)setMenu:(DWMenu *)input;
 -(void)windowDidBecomeMain:(id)sender;
 -(void)menuHandler:(id)sender;
 @end
@@ -1042,7 +1057,7 @@
 {
     _event_handler([self window], nil, 13);
 }
--(void)setMenu:(UIMenu *)input { windowmenu = input; [windowmenu retain]; }
+-(void)setMenu:(DWMenu *)input { windowmenu = input; [windowmenu retain]; }
 -(void)menuHandler:(id)sender
 {
     [DWObj menuHandler:sender];
@@ -1095,9 +1110,52 @@
 /* Subclass for a menu item type */
 @implementation DWMenuItem
 -(void)setCheck:(int)input { check = input; }
+-(void)setTag:(unsigned long)input { tag = input; }
 -(int)check { return check; }
+-(unsigned long)tag { return tag; }
 -(void)dealloc { dw_signal_disconnect_by_window(self); [super dealloc]; }
 @end
+/*
+ * Encapsulate immutable objects in our own containers,
+ * so we can recreate the immutable subobjects as needed.
+ * Currently in this category: DWMenu and DWImage
+ */
+@implementation DWMenu
+-(void)setMenu:(UIMenu *)input { menu = input; }
+-(UIMenu *)menu { return menu; }
+-(DWMenuItem *)itemWithTag:(unsigned long)tag
+{
+    NSArray *children = [menu children];
+    
+    for(DWMenuItem *menuitem in children)
+    {
+        if([menuitem tag] == tag)
+            return menuitem;
+    }
+    return nil;
+}
+-(void)dealloc { [super dealloc]; }
+@end
+
+@interface DWImage : NSObject
+{
+    UIImage *image;
+    CGImageRef cgimage;
+}
+-(void)setImage:(UIImage *)input;
+-(void)setCGImage:(CGImageRef)input;
+-(UIImage *)image;
+-(CGImageRef)cgimage;
+-(void)dealloc;
+@end
+
+@implementation DWImage
+-(void)setImage:(UIImage *)input { image = input; }
+-(void)setCGImage:(CGImageRef)input { cgimage = input; }
+-(UIImage *)image { return image; }
+-(CGImageRef)cgimage { return cgimage; }
+-(void)dealloc { if(cgimage) CGImageRelease(cgimage); if(image) [image release]; [super dealloc]; }
+@end
 
 /* Subclass for a scrollbox type */
 @interface DWScrollBox : UIScrollView
@@ -1190,20 +1248,6 @@
 -(void)dealloc { UserData *root = userdata; _remove_userdata(&root, NULL, TRUE); [super dealloc]; }
 @end
 
-
-/* Subclass for a Notebook control type */
-@interface DWNotebook : UISegmentedControl
-{
-    void *userdata;
-    int pageid;
-}
--(void *)userdata;
--(void)setUserdata:(void *)input;
--(int)pageid;
--(void)setPageid:(int)input;
--(void)pageChanged:(id)sender;
-@end
-
 /* Subclass for a Notebook page type */
 @interface DWNotebookPage : UIView
 {
@@ -1216,11 +1260,27 @@
 -(void)setPageid:(int)input;
 @end
 
+/* Subclass for a Notebook control type */
+@interface DWNotebook : UISegmentedControl
+{
+    void *userdata;
+    int pageid;
+    NSMutableArray<DWNotebookPage *> *views;
+}
+-(void *)userdata;
+-(void)setUserdata:(void *)input;
+-(int)pageid;
+-(void)setPageid:(int)input;
+-(NSMutableArray<DWNotebookPage *> *)views;
+-(void)pageChanged:(id)sender;
+@end
+
 @implementation DWNotebook
 -(void *)userdata { return userdata; }
 -(void)setUserdata:(void *)input { userdata = input; }
 -(int)pageid { return pageid; }
 -(void)setPageid:(int)input { pageid = input; }
+-(NSMutableArray<DWNotebookPage *> *)views { return views; };
 -(void)pageChanged:(id)sender
 {
 #if 0 /* TODO: Implement page/segment changed handler */
@@ -1250,7 +1310,7 @@
 @end
 
 /* Subclass for a splitbar type */
-@interface DWSplitBar : UISplitViewController
+@interface DWSplitBar : UISplitViewController <UISplitViewControllerDelegate>
 {
     void *userdata;
     float percent;
@@ -1410,7 +1470,7 @@
 -(void)setForegroundColor:(UIColor *)input;
 -(void)doubleClicked:(id)sender;
 -(void)selectionChanged:(id)sender;
--(UIMenu *)menuForEvent:(UIEvent *)event;
+-(DWMenu *)menuForEvent:(UIEvent *)event;
 @end
 
 @implementation DWContainer
@@ -1801,7 +1861,7 @@
     /* Handler for listbox class */
     _event_handler(self, DW_INT_TO_POINTER((int)[self indexPathForSelectedRow].row), 11);
 }
--(UIMenu *)menuForEvent:(UIEvent *)event
+-(DWMenu *)menuForEvent:(UIEvent *)event
 {
 #if 0 /* TODO: Fix this */
     int row;
@@ -3411,11 +3471,13 @@
  */
 void API dw_entryfield_set_limit(HWND handle, ULONG limit)
 {
+#if 0 /* TODO: Implment this via textField:shouldChangeCharactersInRange:replacementString: */
     DWEntryField *entry = handle;
     DWEntryFieldFormatter *formatter = [[[DWEntryFieldFormatter alloc] init] autorelease];
 
     [formatter setMaximumLength:(int)limit];
     [entry setFormatter:formatter];
+#endif
 }
 
 /*
@@ -4694,7 +4756,7 @@
         [color set];
 
         [aPath moveToPoint:CGPointMake(x1 + 0.5, y1 + 0.5)];
-        [aPath lineToPoint:CGPointMake(x2 + 0.5, y2 + 0.5)];
+        [aPath addLineToPoint:CGPointMake(x2 + 0.5, y2 + 0.5)];
         [aPath stroke];
     }
 
@@ -5887,11 +5949,11 @@
 
         if(thistext && strcmp(thistext, text) == 0)
         {
-            NSIndexSet *selected = [[NSIndexSet alloc] initWithIndex:(NSUInteger)x];
-
-            [cont selectRowIndexes:selected byExtendingSelection:YES];
-            [selected release];
-            [cont scrollRowToVisible:x];
+            NSIndexPath *ip = [NSIndexPath indexPathForRow:(NSUInteger)x inSection:0];
+
+            [cont selectRowAtIndexPath:ip
+                              animated:NO
+                        scrollPosition:UITableViewScrollPositionNone];
             x=count;
             break;
         }
@@ -5923,11 +5985,11 @@
 
         if(thisdata == data)
         {
-            NSIndexSet *selected = [[NSIndexSet alloc] initWithIndex:(NSUInteger)x];
-
-            [cont selectRowIndexes:selected byExtendingSelection:YES];
-            [selected release];
-            [cont scrollRowToVisible:x];
+            NSIndexPath *ip = [NSIndexPath indexPathForRow:(NSUInteger)x inSection:0];
+
+            [cont selectRowAtIndexPath:ip
+                              animated:NO
+                        scrollPosition:UITableViewScrollPositionNone];
             x=count;
             break;
         }
@@ -6041,7 +6103,9 @@
                 size.width = 24;
             if(size.height > 24)
                 size.height = 24;
+#if 0 /* TODO: UIImage is immutable, duplicate? */
             [image setSize:size];
+#endif
         }
     }
 }
@@ -6147,30 +6211,37 @@
  * Returns:
  *       A handle to a splitbar window or NULL on failure.
  */
-DW_FUNCTION_DEFINITION(dw_splitbar_new, HWND, int type, HWND topleft, HWND bottomright, unsigned long cid)
+DW_FUNCTION_DEFINITION(dw_splitbar_new, HWND, DW_UNUSED(int type), HWND topleft, HWND bottomright, unsigned long cid)
 DW_FUNCTION_ADD_PARAM4(type, topleft, bottomright, cid)
 DW_FUNCTION_RETURN(dw_splitbar_new, HWND)
-DW_FUNCTION_RESTORE_PARAM4(type, int, topleft, HWND, bottomright, HWND, cid, unsigned long)
+DW_FUNCTION_RESTORE_PARAM4(DW_UNUSED(type), int, topleft, HWND, bottomright, HWND, cid, unsigned long)
 {
     DW_FUNCTION_INIT;
     id tmpbox = dw_box_new(DW_VERT, 0);
     DWSplitBar *split = [[DWSplitBar alloc] init];
+    UIViewController *vc = [[UIViewController alloc] init];
     [split setDelegate:split];
     dw_box_pack_start(tmpbox, topleft, 0, 0, TRUE, TRUE, 0);
-    [split addSubview:tmpbox];
+    [vc setView:tmpbox];
+    if (@available(iOS 14.0, *)) {
+        [split setViewController:vc forColumn:UISplitViewControllerColumnPrimary];
+    } else {
+        [split addChildViewController:vc];
+    }
     [tmpbox autorelease];
     tmpbox = dw_box_new(DW_VERT, 0);
     dw_box_pack_start(tmpbox, bottomright, 0, 0, TRUE, TRUE, 0);
-    [split addSubview:tmpbox];
+    vc = [[UIViewController alloc] init];
+    [vc setView:tmpbox];
+    if (@available(iOS 14.0, *)) {
+        [split setViewController:vc forColumn:UISplitViewControllerColumnSecondary];
+    } else {
+        [split addChildViewController:vc];
+    }
     [tmpbox autorelease];
-    if(type == DW_VERT)
-    {
-        [split setVertical:NO];
-    }
-    else
-    {
-        [split setVertical:YES];
-    }
+#if 0 /* TODO: All iOS splitbars are vertical */
+    [split setVertical:(type == DW_VERT ? YES : NO)];
+#endif
     /* Set the default percent to 50% split */
     [split setPercent:50.0];
     [split setTag:cid];
@@ -6190,20 +6261,22 @@
 {
     DW_FUNCTION_INIT;
     DWSplitBar *split = handle;
-    CGRect rect = [split frame];
+    CGSize size = [split preferredContentSize];
     float pos;
     /* Calculate the position based on the size */
-    if([split isVertical])
-    {
-        pos = rect.size.width * (percent / 100.0);
-    }
+#if 0 /* TODO: iOS split views are always vertical */
+    if(![split isVertical])
+        pos = size.height * (percent / 100.0);
     else
-    {
-        pos = rect.size.height * (percent / 100.0);
-    }
+#endif
+        pos = size.width * (percent / 100.0);
     if(pos > 0)
     {
-        [split setPosition:pos ofDividerAtIndex:0];
+        if (@available(iOS 14.0, *)) {
+            [split setPreferredPrimaryColumnWidth:pos];
+        } else {
+            /* TODO: Is this possible on earlier versions? */
+        }
     }
     else
     {
@@ -6224,24 +6297,14 @@
 float API dw_splitbar_get(HWND handle)
 {
     DWSplitBar *split = handle;
-    CGRect rect1 = [split frame];
-    NSArray *subviews = [split subviews];
-    UIView *view = [subviews firstObject];
-    CGRect rect2 = [view frame];
-    float pos, total, retval = 0.0;
-    if([split isVertical])
-    {
-        total = rect1.size.width;
-        pos = rect2.size.width;
-    }
-    else
-    {
-        total = rect1.size.height;
-        pos = rect2.size.height;
-    }
-    if(total > 0)
-    {
-        retval = pos / total;
+    float retval = 50.0;
+
+    if (@available(iOS 14.0, *)) {
+        float primary = [split primaryColumnWidth];
+        float supplementary = [split supplementaryColumnWidth];
+        retval = (primary / (primary + supplementary)) * 100.0;
+    } else {
+        /* TODO: If possible*/
     }
     return retval;
 }
@@ -6599,11 +6662,9 @@
 HWND API dw_calendar_new(ULONG cid)
 {
     DWCalendar *calendar = [[DWCalendar alloc] init];
-    [calendar setDatePickerMode:DWDatePickerModeSingle];
-    [calendar setDatePickerStyle:DWDatePickerStyleClockAndCalendar];
-    [calendar setDatePickerElements:DWDatePickerElementFlagYearMonthDay];
+    [calendar setDatePickerMode:UIDatePickerModeDate];
     [calendar setTag:cid];
-    [calendar setDateValue:[NSDate date]];
+    [calendar setDate:[NSDate date]];
     return calendar;
 }
 
@@ -6626,7 +6687,7 @@
     dateFormatter.dateFormat = @"yyyy-MM-dd";
 
     date = [dateFormatter dateFromString:[NSString stringWithUTF8String:buffer]];
-    [calendar setDateValue:date];
+    [calendar setDate:date];
     [date release];
     DW_LOCAL_POOL_OUT;
 }
@@ -6640,9 +6701,9 @@
 {
     DWCalendar *calendar = handle;
     DW_LOCAL_POOL_IN;
-    NSCalendar *mycalendar = [[NSCalendar alloc] initWithCalendarIdentifier:DWCalendarIdentifierGregorian];
-    NSDate *date = [calendar dateValue];
-    NSDateComponents* components = [mycalendar components:DWCalendarUnitDay|DWCalendarUnitMonth|DWCalendarUnitYear fromDate:date];
+    NSCalendar *mycalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+    NSDate *date = [calendar date];
+    NSDateComponents* components = [mycalendar components:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
     *day = (unsigned int)[components day];
     *month = (unsigned int)[components month];
     *year = (unsigned int)[components year];
@@ -6673,10 +6734,10 @@
         case DW_HTML_SEARCH:
             break;
         case DW_HTML_RELOAD:
-            [html reload:html];
+            [html reload];
             break;
         case DW_HTML_STOP:
-            [html stopLoading:html];
+            [html stopLoading];
             break;
         case DW_HTML_PRINT:
             break;
@@ -6766,15 +6827,13 @@
  */
 void API dw_pointer_query_pos(long *x, long *y)
 {
-    CGPoint mouseLoc;
-    mouseLoc = [UIEvent mouseLocation];
     if(x)
     {
-        *x = mouseLoc.x;
+        *x = 0;
     }
     if(y)
     {
-        *y = [[NSScreen mainScreen] frame].size.height - mouseLoc.y;
+        *y = 0;
     }
 }
 
@@ -6797,8 +6856,7 @@
  */
 HMENUI API dw_menu_new(ULONG cid)
 {
-    UIMenu *menu = [[UIMenu alloc] init];
-    [menu setAutoenablesItems:NO];
+    DWMenu *menu = [[DWMenu alloc] init];
     /* [menu setTag:cid]; Why doesn't this work? */
     return menu;
 }
@@ -6810,10 +6868,8 @@
  */
 HMENUI API dw_menubar_new(HWND location)
 {
-    UIWindow *window = location;
-    UIMenu *windowmenu = _generate_main_menu();
-    [[window contentView] setMenu:windowmenu];
-    return (HMENUI)windowmenu;
+    /* TODO: Implement this with UIMenuSystem */
+    return NULL;
 }
 
 /*
@@ -6823,38 +6879,12 @@
  */
 void API dw_menu_destroy(HMENUI *menu)
 {
-    UIMenu *thismenu = *menu;
+    DWMenu *thismenu = *menu;
     DW_LOCAL_POOL_IN;
     [thismenu release];
     DW_LOCAL_POOL_OUT;
 }
 
-/* Handle deprecation of convertScreenToBase in 10.10 yet still supporting
- * 10.6 and earlier since convertRectFromScreen was introduced in 10.7.
- */
-CGPoint _windowPointFromScreen(id window, CGPoint p)
-{
-    SEL crfs = NSSelectorFromString(@"convertRectFromScreen:");
-
-    if([window respondsToSelector:crfs])
-    {
-        CGRect (* icrfs)(id, SEL, CGRect) = (CGRect (*)(id, SEL, CGRect))[window methodForSelector:crfs];
-        CGRect rect = icrfs(window, crfs, NSMakeRect(p.x, p.y, 1, 1));
-        return rect.origin;
-    }
-    else
-    {
-        SEL cstb = NSSelectorFromString(@"convertScreenToBase:");
-
-        if([window respondsToSelector:cstb])
-        {
-            CGPoint (* icstb)(id, SEL, CGPoint) = (CGPoint (*)(id, SEL, CGPoint))[window methodForSelector:cstb];
-            return icstb(window, cstb, p);
-        }
-    }
-    return NSMakePoint(0,0);
-}
-
 /*
  * Pops up a context menu at given x and y coordinates.
  * Parameters:
@@ -6865,25 +6895,15 @@
  */
 void API dw_menu_popup(HMENUI *menu, HWND parent, int x, int y)
 {
-    UIMenu *thismenu = (UIMenu *)*menu;
+#if 0 /* TODO: Figure out how to do this */
+    DWMenu *thismenu = (DWMenu *)*menu;
     id object = parent;
     UIView *view = [object isKindOfClass:[UIWindow class]] ? [object contentView] : parent;
     UIWindow *window = [view window];
-    UIEvent *event = [DWApp currentEvent];
-    if(!window)
-        window = [event window];
     [thismenu autorelease];
-    CGPoint p = NSMakePoint(x, [[NSScreen mainScreen] frame].size.height - y);
-    UIEvent* fake = [UIEvent mouseEventWithType:DWEventTypeRightMouseDown
-                                       location:_windowPointFromScreen(window, p)
-                                  modifierFlags:0
-                                      timestamp:[event timestamp]
-                                   windowNumber:[window windowNumber]
-                                        context:[NSGraphicsContext currentContext]
-                                    eventNumber:1
-                                     clickCount:1
-                                       pressure:0.0];
-    [UIMenu popUpContextMenu:thismenu withEvent:fake forView:view];
+    CGPoint p = CGPointMake(x, y);
+    [UIContextMenuInteraction a
+#endif
 }
 
 char _removetilde(char *dest, const char *src)
@@ -6921,49 +6941,68 @@
  */
 HWND API dw_menu_append_item(HMENUI menux, const char *title, ULONG itemid, ULONG flags, int end, int check, HMENUI submenux)
 {
-    UIMenu *menu = menux;
-    UIMenu *submenu = submenux;
+    DWMenu *menu = menux;
     DWMenuItem *item = NULL;
     if(strlen(title) == 0)
     {
+#if 0 /* TODO: Not sure if separators exist in iOS */
         [menu addItem:[DWMenuItem separatorItem]];
+#endif
     }
     else
     {
         char accel[2];
         char *newtitle = malloc(strlen(title)+1);
         NSString *nstr;
-
+        UIMenu *newmenu, *oldmenu = [menu menu];
+        NSArray *newchildren, *oldchildren = [oldmenu children];
+        
         accel[0] = _removetilde(newtitle, title);
         accel[1] = 0;
 
         nstr = [ NSString stringWithUTF8String:newtitle ];
         free(newtitle);
 
-        item = [[[DWMenuItem alloc] initWithTitle:nstr
-                            action:@selector(menuHandler:)
-                            keyEquivalent:[ NSString stringWithUTF8String:accel ]] autorelease];
-        [menu addItem:item];
-
+        item = [[DWMenuItem commandWithTitle:nstr image:nil
+                                      action:@selector(menuHandler:)
+                                propertyList:nil] autorelease];
+        newchildren = [oldchildren arrayByAddingObjectsFromArray:@[item]];
+        if(oldmenu)
+        {
+            newmenu = [oldmenu menuByReplacingChildren:newchildren];
+            [oldmenu release];
+        }
+        else if(@available(iOS 14.0, *))
+            newmenu = [UIMenu menuWithChildren:newchildren];
+        else
+            newmenu = [UIMenu menuWithTitle:@"" children:newchildren];
+        [menu setMenu:newmenu];
+        
         [item setTag:itemid];
         if(check)
         {
             [item setCheck:YES];
             if(flags & DW_MIS_CHECKED)
             {
-                [item setState:DWControlStateValueOn];
+                [item setState:UIMenuElementStateOn];
             }
         }
+#if 0 /* TODO: Disabled items not supported on iOS */
         if(flags & DW_MIS_DISABLED)
         {
             [item setEnabled:NO];
         }
-
+#endif
+
+#if 0 /* TODO: iOS may not support submenus... but may be able to cascade... with defered menus */
         if(submenux)
         {
+            DWMenu *submenu = submenux;
+
             [submenu setTitle:nstr];
             [menu setSubmenu:submenu forItem:item];
         }
+#endif
         return item;
     }
     return item;
@@ -6980,17 +7019,17 @@
 void API dw_menu_item_set_check(HMENUI menux, unsigned long itemid, int check)
 {
     id menu = menux;
-    UIMenuItem *menuitem = (UIMenuItem *)[menu itemWithTag:itemid];
+    DWMenuItem *menuitem = [menu itemWithTag:itemid];
 
     if(menuitem != nil)
     {
         if(check)
         {
-            [menuitem setState:DWControlStateValueOn];
+            [menuitem setState:UIMenuElementStateOn];
         }
         else
         {
-            [menuitem setState:DWControlStateValueOff];
+            [menuitem setState:UIMenuElementStateOff];
         }
     }
 }
@@ -7005,14 +7044,16 @@
  */
 int API dw_menu_delete_item(HMENUI menux, unsigned long itemid)
 {
+#if 0 /* TODO: Remove item from the children array */
     id menu = menux;
-    UIMenuItem *menuitem = (UIMenuItem *)[menu itemWithTag:itemid];
+    DWMenuItem *menuitem = [menu itemWithTag:itemid];
 
     if(menuitem != nil)
     {
         [menu removeItem:menuitem];
         return DW_ERROR_NONE;
     }
+#endif
     return DW_ERROR_UNKNOWN;
 }
 
@@ -7027,18 +7068,19 @@
 void API dw_menu_item_set_state(HMENUI menux, unsigned long itemid, unsigned long state)
 {
     id menu = menux;
-    UIMenuItem *menuitem = (UIMenuItem *)[menu itemWithTag:itemid];
+    DWMenuItem *menuitem = [menu itemWithTag:itemid];
 
     if(menuitem != nil)
     {
         if(state & DW_MIS_CHECKED)
         {
-            [menuitem setState:DWControlStateValueOn];
+            [menuitem setState:UIMenuElementStateOn];
         }
         else if(state & DW_MIS_UNCHECKED)
         {
-            [menuitem setState:DWControlStateValueOff];
-        }
+            [menuitem setState:UIMenuElementStateOff];
+        }
+#if 0 /* TODO: Disabled items not supported on iOS */
         if(state & DW_MIS_ENABLED)
         {
             [menuitem setEnabled:YES];
@@ -7047,13 +7089,14 @@
         {
             [menuitem setEnabled:NO];
         }
+#endif
     }
 }
 
 /* Gets the notebook page from associated ID */
 DWNotebookPage *_notepage_from_id(DWNotebook *notebook, unsigned long pageid)
 {
-    NSArray *pages = [notebook tabViewItems];
+    NSArray *pages = [notebook views];
     for(DWNotebookPage *notepage in pages)
     {
         if([notepage pageid] == pageid)
@@ -7073,7 +7116,6 @@
 HWND API dw_notebook_new(ULONG cid, int top)
 {
     DWNotebook *notebook = [[DWNotebook alloc] init];
-    [notebook setDelegate:notebook];
     [notebook addTarget:notebook
                  action:@selector(pageChanged:)
        forControlEvents:UIControlEventValueChanged];
@@ -7092,15 +7134,18 @@
 {
     DWNotebook *notebook = handle;
     NSInteger page = [notebook pageid];
-    DWNotebookPage *notepage = [[DWNotebookPage alloc] initWithIdentifier:[NSString stringWithFormat: @"pageid:%d", (int)page]];
+    DWNotebookPage *notepage = [[DWNotebookPage alloc] init];
+    NSMutableArray<DWNotebookPage *> *views = [notebook views];
     [notepage setPageid:(int)page];
     if(front)
     {
-        [notebook insertTabViewItem:notepage atIndex:(NSInteger)0];
+        [notebook insertSegmentWithTitle:@"" atIndex:(NSInteger)0 animated:NO];
+        [views addObject:notepage];
     }
     else
     {
-        [notebook addTabViewItem:notepage];
+        [notebook insertSegmentWithTitle:@"" atIndex:[notebook numberOfSegments] animated:NO];
+        [views addObject:notepage];
     }
     [notepage autorelease];
     [notebook setPageid:(int)(page+1)];
@@ -7121,7 +7166,15 @@
 
     if(notepage != nil)
     {
-        [notebook removeTabViewItem:notepage];
+        NSMutableArray<DWNotebookPage *> *views = [notebook views];
+        NSUInteger index = [views indexOfObject:notepage];
+
+        if(index != NSNotFound)
+        {
+            [notebook removeSegmentAtIndex:index animated:NO];
+            [views removeObject:notepage];
+            [notepage release];
+        }
     }
     DW_LOCAL_POOL_OUT;
 }
@@ -7134,7 +7187,9 @@
 unsigned long API dw_notebook_page_get(HWND handle)
 {
     DWNotebook *notebook = handle;
-    DWNotebookPage *notepage = (DWNotebookPage *)[notebook selectedTabViewItem];
+    NSInteger index = [notebook selectedSegmentIndex];
+    NSMutableArray<DWNotebookPage *> *views = [notebook views];
+    DWNotebookPage *notepage = [views objectAtIndex:index];
     return [notepage pageid];
 }
 
@@ -7146,13 +7201,21 @@
  */
 void API dw_notebook_page_set(HWND handle, unsigned int pageid)
 {
+#if 0 /* TODO: Don't see a method to select a tab */
     DWNotebook *notebook = handle;
     DWNotebookPage *notepage = _notepage_from_id(notebook, pageid);
 
     if(notepage != nil)
     {
-        [notebook selectTabViewItem:notepage];
-    }
+        NSMutableArray<DWNotebookPage *> *views = [notebook views];
+        NSUInteger index = [views indexOfObject:notepage];
+
+        if(index != NSNotFound)
+        {
+            [notebook selectTabViewItem:notepage];
+        }
+    }
+#endif
 }
 
 /*
@@ -7165,12 +7228,8 @@
 void API dw_notebook_page_set_text(HWND handle, ULONG pageid, const char *text)
 {
     DWNotebook *notebook = handle;
-    DWNotebookPage *notepage = _notepage_from_id(notebook, pageid);
-
-    if(notepage != nil)
-    {
-        [notepage setLabel:[ NSString stringWithUTF8String:text ]];
-    }
+
+    [notebook setTitle:[NSString stringWithUTF8String:text] forSegmentAtIndex:pageid];
 }
 
 /*
@@ -7194,6 +7253,7 @@
  */
 void API dw_notebook_pack(HWND handle, ULONG pageid, HWND page)
 {
+#if 0 /* TODO: Haven't implemented the content yet since UISegmentedControl is just the buttons */
     DWNotebook *notebook = handle;
     DWNotebookPage *notepage = _notepage_from_id(notebook, pageid);
 
@@ -7206,6 +7266,7 @@
         [notepage setView:box];
         [box autorelease];
     }
+#endif
 }
 
 /*
@@ -7221,42 +7282,11 @@
 DW_FUNCTION_RESTORE_PARAM3(hwndOwner, HWND, title, char *, flStyle, ULONG)
 {
     DW_FUNCTION_INIT;
-    CGRect frame = NSMakeRect(1,1,1,1);
-    DWWindow *window = [[DWWindow alloc]
-                        initWithContentRect:frame
-                        styleMask:(flStyle)
-                        backing:NSBackingStoreBuffered
-                        defer:false];
-
-    [window setTitle:[ NSString stringWithUTF8String:title ]];
-
-    DWView *view = [[DWView alloc] init];
-
-    [window setContentView:view];
-    [window setDelegate:view];
-    [window setAutorecalculatesKeyViewLoop:YES];
-    [window setAcceptsMouseMovedEvents:YES];
-    [window setReleasedWhenClosed:YES];
-    [view autorelease];
-
-    /* Enable full screen mode on resizeable windows */
-    if(flStyle & DW_FCF_SIZEBORDER)
-    {
-        [window setCollectionBehavior:UIWindowCollectionBehaviorFullScreenPrimary];
-    }
-
-    /* If it isn't a toplevel window... */
-    if(hwndOwner)
-    {
-        id object = hwndOwner;
-
-        /* Check to see if the parent is an MDI window */
-        if([object isMemberOfClass:[DWMDI class]])
-        {
-            /* Set the window level to be floating */
-            [window setLevel:NSFloatingWindowLevel];
-            [window setHidesOnDeactivate:YES];
-        }
+    DWWindow *window = [[DWWindow alloc] init];
+
+    /* TODO: Handle style flags */
+    if(@available(iOS 13.0, *)) {
+        [window setLargeContentTitle:[NSString stringWithUTF8String:title]];
     }
     DW_FUNCTION_RETURN_THIS(window);
 }
@@ -7290,25 +7320,7 @@
  */
 void API dw_window_set_pointer(HWND handle, int pointertype)
 {
-    id object = handle;
-
-    if([ object isKindOfClass:[ UIView class ] ])
-    {
-        UIView *view = handle;
-
-        if(pointertype == DW_POINTER_DEFAULT)
-        {
-            [view discardCursorRects];
-        }
-        else if(pointertype == DW_POINTER_ARROW)
-        {
-            CGRect rect = [view frame];
-            NSCursor *cursor = [NSCursor arrowCursor];
-
-            [view addCursorRect:rect cursor:cursor];
-        }
-        /* No cursor for DW_POINTER_CLOCK? */
-    }
+    /* TODO: Only might be possible on Catalyst */
 }
 
 /*
@@ -7323,59 +7335,16 @@
     if([ object isMemberOfClass:[ DWWindow class ] ])
     {
         DWWindow *window = handle;
-        CGRect rect = [[window contentView] frame];
-        id defaultitem = [window initialFirstResponder];
-
-        if([window isMiniaturized])
-        {
-            [window deminiaturize:nil];
-        }
+        CGRect rect = [window frame];
+
         /* If we haven't been sized by a call.. */
         if(rect.size.width <= 1 || rect.size.height <= 1)
         {
             /* Determine the contents size */
             dw_window_set_size(handle, 0, 0);
         }
-        /* If the position was not set... generate a default
-         * default one in a similar pattern to SHELLPOSITION.
-         */
         if(![window shown])
-        {
-            static int defaultx = 0, defaulty = 0;
-            int cx = dw_screen_width(), cy = dw_screen_height();
-            int maxx = cx / 4, maxy = cy / 4;
-            CGPoint point;
-
-            rect = [window frame];
-
-            defaultx += 20;
-            defaulty += 20;
-            if(defaultx > maxx)
-                defaultx = 20;
-            if(defaulty > maxy)
-                defaulty = 20;
-
-            point.x = defaultx;
-            /* Take into account menu bar and inverted Y */
-            point.y = cy - defaulty - (int)rect.size.height - 22;
-
-            [window setFrameOrigin:point];
             [window setShown:YES];
-        }
-        [[window contentView] showWindow];
-        [window makeKeyAndOrderFront:nil];
-
-        if(!([window styleMask] & DWWindowStyleMaskResizable))
-        {
-            /* Fix incorrect repeat in displaying textured windows */
-            [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge];
-            [window setContentBorderThickness:0.0 forEdge:NSMinYEdge];
-        }
-        if(defaultitem)
-        {
-            /* If there is a default item set, make it first responder */
-            [window makeFirstResponder:defaultitem];
-        }
     }
     return 0;
 }
@@ -7393,7 +7362,7 @@
     {
         UIWindow *window = handle;
 
-        [window orderOut:nil];
+        [window setHidden:YES];
     }
     return 0;
 }
@@ -7416,11 +7385,11 @@
     /* Get the UIColor for non-default colors */
     if(fore != DW_CLR_DEFAULT)
     {
-        fg = [UIColor colorWithDeviceRed: DW_RED_VALUE(_fore)/255.0 green: DW_GREEN_VALUE(_fore)/255.0 blue: DW_BLUE_VALUE(_fore)/255.0 alpha: 1];
+        fg = [UIColor colorWithRed: DW_RED_VALUE(_fore)/255.0 green: DW_GREEN_VALUE(_fore)/255.0 blue: DW_BLUE_VALUE(_fore)/255.0 alpha: 1];
     }
     if(back != DW_CLR_DEFAULT)
     {
-        bg = [UIColor colorWithDeviceRed: DW_RED_VALUE(_back)/255.0 green: DW_GREEN_VALUE(_back)/255.0 blue: DW_BLUE_VALUE(_back)/255.0 alpha: 1];
+        bg = [UIColor colorWithRed: DW_RED_VALUE(_back)/255.0 green: DW_GREEN_VALUE(_back)/255.0 blue: DW_BLUE_VALUE(_back)/255.0 alpha: 1];
     }
 
     /* Get the textfield from the spinbutton */
@@ -7431,20 +7400,15 @@
     /* Get the cell on classes using NSCell */
     if([object isKindOfClass:[UITextField class]])
     {
-        id cell = [object cell];
-
-        [object setTextColor:(fg ? fg : [UIColor controlTextColor])];
-        [cell setTextColor:(fg ? fg : [UIColor controlTextColor])];
+        [object setTextColor:(fg ? fg : [UIColor labelColor])];
     }
     if([object isMemberOfClass:[DWButton class]])
     {
-        [object setTitleColor:(fg ? fg : [UIColor controlTextColor])];
+        [object setTextColor:(fg ? fg : [UIColor labelColor])];
     }
     if([object isKindOfClass:[UITextField class]] || [object isKindOfClass:[UIButton class]])
     {
-        id cell = [object cell];
-
-        [cell setBackgroundColor:(bg ? bg : [UIColor controlColor])];
+        [object setBackgroundColor:(bg ? bg : [UIColor systemBackgroundColor])];
     }
     else if([object isMemberOfClass:[DWBox class]])
     {
@@ -7452,19 +7416,18 @@
 
         [box setColor:_back];
     }
-    else if([object isKindOfClass:[NSTableView class]])
+    else if([object isKindOfClass:[UITableView class]])
     {
         DWContainer *cont = handle;
 
-        [cont setBackgroundColor:(bg ? bg : [UIColor controlBackgroundColor])];
-        [cont setForegroundColor:(fg ? fg : [UIColor controlTextColor])];
+        [cont setBackgroundColor:(bg ? bg : [UIColor systemBackgroundColor])];
+        [cont setForegroundColor:(fg ? fg : [UIColor labelColor])];
     }
     else if([object isMemberOfClass:[DWMLE class]])
     {
         DWMLE *mle = handle;
-        [mle setBackgroundColor:(bg ? bg : [UIColor controlBackgroundColor])];
-        NSTextStorage *ts = [mle textStorage];
-        [ts setForegroundColor:(fg ? fg : [UIColor controlTextColor])];
+        [mle setBackgroundColor:(bg ? bg : [UIColor systemBackgroundColor])];
+        [mle setTextColor:(fg ? fg : [UIColor labelColor])];
     }
     return 0;
 }
@@ -7491,25 +7454,7 @@
 {
     id object = _text_handle(handle);
 
-    if([object isMemberOfClass:[DWWindow class]])
-    {
-        DWWindow *window = object;
-        SEL sssm = NSSelectorFromString(@"setStyleMask");
-
-        if([window respondsToSelector:sssm])
-        {
-            DWIMP issm = (DWIMP)[window methodForSelector:sssm];
-            int currentstyle = (int)[window styleMask];
-            int tmp;
-
-            tmp = currentstyle | (int)mask;
-            tmp ^= mask;
-            tmp |= style;
-
-            issm(window, sssm, tmp);
-        }
-    }
-    else if([object isKindOfClass:[UILabel class]])
+    if([object isKindOfClass:[UILabel class]])
     {
         UILabel *label = object;
 
@@ -7528,31 +7473,21 @@
                 [label setLineBreakMode:NSLineBreakByTruncatingTail];
         }
     }
+#if 0 /* TODO: See if we can change alignment... */
     else if([object isMemberOfClass:[UITextView class]])
     {
         UITextView *tv = handle;
         [tv setAlignment:(style & mask)];
     }
-    else if([object isMemberOfClass:[DWButton class]])
-    {
-        DWButton *button = handle;
-
-        if(mask & DW_BS_NOBORDER)
-        {
-            if(style & DW_BS_NOBORDER)
-                [button setBordered:NO];
-            else
-                [button setBordered:YES];
-        }
-    }
+#endif
     else if([object isMemberOfClass:[DWMenuItem class]])
     {
         if(mask & (DW_MIS_CHECKED | DW_MIS_UNCHECKED))
         {
             if(style & DW_MIS_CHECKED)
-                [object setState:DWControlStateValueOn];
+                [object setState:UIMenuElementStateOn];
             else if(style & DW_MIS_UNCHECKED)
-                [object setState:DWControlStateValueOff];
+                [object setState:UIMenuElementStateOff];
         }
         if(mask & (DW_MIS_ENABLED | DW_MIS_DISABLED))
         {
@@ -7575,7 +7510,7 @@
 {
     id object = handle;
 
-    [[object window] makeFirstResponder:object];
+    [object becomeFirstResponder];
 }
 
 /*
@@ -7593,7 +7528,7 @@
 
     if([window isKindOfClass:[UIWindow class]] && [object isKindOfClass:[UIControl class]])
     {
-        [window setInitialFirstResponder:defaultitem];
+        [object becomeFirstResponder];
     }
 }
 
@@ -7605,26 +7540,7 @@
  */
 void API dw_window_click_default(HWND handle, HWND next)
 {
-    id object = handle;
-    id control = next;
-
-    if([object isMemberOfClass:[DWWindow class]])
-    {
-        if([control isMemberOfClass:[DWButton class]])
-        {
-            UIWindow *window = object;
-
-            [window setDefaultButtonCell:[control cell]];
-        }
-    }
-    else
-    {
-        if([control isMemberOfClass:[DWSpinButton class]])
-        {
-            control = [control textfield];
-        }
-        [object setClickDefault:control];
-    }
+    /* TODO: Figure out how to do this if we should */
 }
 
 /*
@@ -7653,34 +7569,7 @@
  */
 void API dw_window_reparent(HWND handle, HWND newparent)
 {
-    id object = handle;
-
-    if([object isMemberOfClass:[DWWindow class]])
-    {
-        /* We can't actually reparent on MacOS but if the
-         * new parent is an MDI window, change to be a
-         * floating window... otherwise set it to normal.
-         */
-        UIWindow *window = handle;
-
-        /* If it isn't a toplevel window... */
-        if(newparent)
-        {
-            object = newparent;
-
-            /* Check to see if the parent is an MDI window */
-            if([object isMemberOfClass:[DWMDI class]])
-            {
-                /* Set the window level to be floating */
-                [window setLevel:NSFloatingWindowLevel];
-                [window setHidesOnDeactivate:YES];
-                return;
-            }
-        }
-        /* Set the window back to a normal window */
-        [window setLevel:NSNormalWindowLevel];
-        [window setHidesOnDeactivate:NO];
-    }
+    /* TODO: Not sure if we should even bother with this */
 }
 
 /* Allows the user to choose a font using the system's font chooser dialog.
@@ -7870,7 +7759,7 @@
     /* Handle removing menu items from menus */
     else if([ object isKindOfClass:[UIMenuItem class]])
     {
-        UIMenu *menu = [object menu];
+        DWMenu *menu = [object menu];
 
         [menu removeItem:object];
     }
@@ -8091,7 +7980,7 @@
         UIScrollView *sv = handle;
         object = [sv documentView];
     }
-    if([object isKindOfClass:[UIControl class]] || [object isKindOfClass:[UIMenuItem class]])
+    if([object isKindOfClass:[UIControl class]] /* TODO: || [object isKindOfClass:[UIMenuItem class]] */)
     {
         [object setEnabled:YES];
     }