From 89e47e7a2a1a1500ebceeeada8cf8bf0e87a5f62 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 29 Jun 2024 07:49:26 +1000 Subject: [PATCH] iOS: Split up general interop and launcher backend --- ios/CCIOS.xcodeproj/project.pbxproj | 4 + src/Graphics_SoftGPU.c | 5 +- src/LBackend_ios.m | 709 ++++++++++++++++++++++++++++ src/interop_ios.m | 699 +-------------------------- 4 files changed, 742 insertions(+), 675 deletions(-) create mode 100644 src/LBackend_ios.m diff --git a/ios/CCIOS.xcodeproj/project.pbxproj b/ios/CCIOS.xcodeproj/project.pbxproj index 3538aa67b..e56008d96 100644 --- a/ios/CCIOS.xcodeproj/project.pbxproj +++ b/ios/CCIOS.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 9A62ADF5286D906F00E5E3DE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9A62ADF4286D906F00E5E3DE /* Assets.xcassets */; }; 9A6C79652BFDDEF200676D27 /* FancyLighting.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C79642BFDDEF100676D27 /* FancyLighting.c */; }; 9A6C79672BFDDF0700676D27 /* Queue.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C79662BFDDF0600676D27 /* Queue.c */; }; + 9A6C7DFA2C2F610C00676D27 /* LBackend_ios.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C7DF92C2F610C00676D27 /* LBackend_ios.m */; }; 9A7401D92B737D5C0040E575 /* Commands.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A7401D82B737D5B0040E575 /* Commands.c */; }; 9A7401DB2B7384060040E575 /* SSL.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A7401DA2B7384060040E575 /* SSL.c */; }; 9A89D4F227F802F600FF3F80 /* LWidgets.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D37827F802F500FF3F80 /* LWidgets.c */; }; @@ -101,6 +102,7 @@ 9A62ADF4286D906F00E5E3DE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ClassiCube/Assets.xcassets; sourceTree = ""; }; 9A6C79642BFDDEF100676D27 /* FancyLighting.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = FancyLighting.c; sourceTree = ""; }; 9A6C79662BFDDF0600676D27 /* Queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Queue.c; sourceTree = ""; }; + 9A6C7DF92C2F610C00676D27 /* LBackend_ios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBackend_ios.m; sourceTree = ""; }; 9A7401D82B737D5B0040E575 /* Commands.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Commands.c; sourceTree = ""; }; 9A7401DA2B7384060040E575 /* SSL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SSL.c; sourceTree = ""; }; 9A89D35727F802B100FF3F80 /* ClassiCube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClassiCube.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -216,6 +218,7 @@ 9A89D37727F802F500FF3F80 /* src */ = { isa = PBXGroup; children = ( + 9A6C7DF92C2F610C00676D27 /* LBackend_ios.m */, 9A6C79662BFDDF0600676D27 /* Queue.c */, 9A6C79642BFDDEF100676D27 /* FancyLighting.c */, 9A4D0C632BDD168800E1695D /* TouchUI.c */, @@ -448,6 +451,7 @@ 9A89D57427F802F600FF3F80 /* MapRenderer.c in Sources */, 9A89D57627F802F600FF3F80 /* _pshinter.c in Sources */, 9A89D56A27F802F600FF3F80 /* Physics.c in Sources */, + 9A6C7DFA2C2F610C00676D27 /* LBackend_ios.m in Sources */, 9A89D58727F802F600FF3F80 /* SelectionBox.c in Sources */, 9A89D4FE27F802F600FF3F80 /* Inventory.c in Sources */, 9A89D56527F802F600FF3F80 /* Generator.c in Sources */, diff --git a/src/Graphics_SoftGPU.c b/src/Graphics_SoftGPU.c index 565f47693..94177474b 100644 --- a/src/Graphics_SoftGPU.c +++ b/src/Graphics_SoftGPU.c @@ -443,6 +443,7 @@ static void DrawTriangle2D(Vertex* V0, Vertex* V1, Vertex* V2) { A = PackedCol_A(color); } + if (gfx_alphaTest && A < 0x80) continue; if (gfx_alphaBlend) { BitmapCol dst = colorBuffer[cb_index]; int dstR = BitmapCol_R(dst); @@ -453,7 +454,6 @@ static void DrawTriangle2D(Vertex* V0, Vertex* V1, Vertex* V2) { G = (G * A + dstG * (255 - A)) >> 8; B = (B * A + dstB * (255 - A)) >> 8; } - if (gfx_alphaTest && A < 0x80) continue; colorBuffer[cb_index] = BitmapCol_Make(R, G, B, 0xFF); } @@ -554,7 +554,9 @@ static void DrawTriangle3D(Vertex* V0, Vertex* V1, Vertex* V2) { A = PackedCol_A(color); } + if (gfx_alphaTest && A < 0x80) continue; int cb_index = y * cb_stride + x; + if (gfx_alphaBlend) { BitmapCol dst = colorBuffer[cb_index]; int dstR = BitmapCol_R(dst); @@ -565,7 +567,6 @@ static void DrawTriangle3D(Vertex* V0, Vertex* V1, Vertex* V2) { G = (G * A + dstG * (255 - A)) >> 8; B = (B * A + dstB * (255 - A)) >> 8; } - if (gfx_alphaTest && A < 0x80) continue; if (depthWrite) depthBuffer[db_index] = z; colorBuffer[cb_index] = BitmapCol_Make(R, G, B, 0xFF); diff --git a/src/LBackend_ios.m b/src/LBackend_ios.m new file mode 100644 index 000000000..c7c77f469 --- /dev/null +++ b/src/LBackend_ios.m @@ -0,0 +1,709 @@ +#include "Core.h" +#if defined CC_BUILD_IOS +#include "Bitmap.h" +#include "Input.h" +#include "Platform.h" +#include "String.h" +#include "Errors.h" +#include "Drawer2D.h" +#include "Launcher.h" +#include "LBackend.h" +#include "LWidgets.h" +#include "LScreens.h" +#include "Gui.h" +#include "LWeb.h" +#include "Funcs.h" +#include "Window.h" +#include +#include + +#ifdef TARGET_OS_TV + // NSFontAttributeName etc - iOS 6.0 + #define TEXT_ATTRIBUTE_FONT NSFontAttributeName + #define TEXT_ATTRIBUTE_COLOR NSForegroundColorAttributeName +#else + // UITextAttributeFont etc - iOS 5.0 + #define TEXT_ATTRIBUTE_FONT UITextAttributeFont + #define TEXT_ATTRIBUTE_COLOR UITextAttributeTextColor +#endif + +// shared state with interop_ios.m +extern UITextField* kb_widget; +extern CGContextRef win_ctx; +extern UIView* view_handle; + +UIColor* ToUIColor(BitmapCol color, float A); +NSString* ToNSString(const cc_string* text); +void LInput_SetKeyboardType(UITextField* fld, int flags); +void LInput_SetPlaceholder(UITextField* fld, const char* placeholder); + + +/*########################################################################################################################* + *------------------------------------------------------Common helpers--------------------------------------------------------* + *#########################################################################################################################*/ +static NSMutableAttributedString* ToAttributedString(const cc_string* text) { + // NSMutableAttributedString - iOS 3.2 + cc_string left = *text, part; + char colorCode = 'f'; + NSMutableAttributedString* str = [[NSMutableAttributedString alloc] init]; + + while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode)) + { + BitmapCol color = Drawer2D_GetColor(colorCode); + NSString* bit = ToNSString(&part); + NSDictionary* attrs = + @{ + //TEXT_ATTRIBUTE_FONT : font, + TEXT_ATTRIBUTE_COLOR : ToUIColor(color, 1.0f) + }; + NSAttributedString* attr_bit = [[NSAttributedString alloc] initWithString:bit attributes:attrs]; + [str appendAttributedString:attr_bit]; + } + return str; +} + + +static UIColor* GetStringColor(const cc_string* text) { + cc_string left = *text, part; + char colorCode = 'f'; + Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode); + + BitmapCol color = Drawer2D_GetColor(colorCode); + return ToUIColor(color, 1.0f); +} + +static NSString* GetColorlessString(const cc_string* text) { + char buffer[128]; + cc_string tmp = String_FromArray(buffer); + + String_AppendColorless(&tmp, text); + return ToNSString(&tmp); +} + + +static void FreeContents(void* info, const void* data, size_t size) { Mem_Free(data); } +// TODO probably a better way.. +static UIImage* ToUIImage(struct Bitmap* bmp) { + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); + CGDataProviderRef provider; + CGImageRef image; + + provider = CGDataProviderCreateWithData(NULL, bmp->scan0, + Bitmap_DataSize(bmp->width, bmp->height), FreeContents); + image = CGImageCreate(bmp->width, bmp->height, 8, 32, bmp->width * 4, colorspace, + kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst, provider, NULL, 0, 0); + + UIImage* img = [UIImage imageWithCGImage:image]; + + CGImageRelease(image); + CGDataProviderRelease(provider); + CGColorSpaceRelease(colorspace); + return img; +} + + +/*########################################################################################################################* + *------------------------------------------------------UI Backend--------------------------------------------------------* + *#########################################################################################################################*/ +static struct LWidget* FindWidgetForView(id obj) { + struct LScreen* s = Launcher_Active; + for (int i = 0; i < s->numWidgets; i++) + { + void* meta = s->widgets[i]->meta; + if (meta != (__bridge void*)obj) continue; + + return s->widgets[i]; + } + return NULL; +} + +static void LTable_UpdateCellColor(UIView* view, struct ServerInfo* server, int row, cc_bool selected); +static void LTable_UpdateCell(UITableView* table, UITableViewCell* cell, int row); + +static NSString* cellID = @"CC_Cell"; +@interface CCUIController : NSObject +@end + +@implementation CCUIController + +- (void)handleButtonPress:(id)sender { + struct LWidget* w = FindWidgetForView(sender); + if (!w) return; + + struct LButton* btn = (struct LButton*)w; + btn->OnClick(btn); +} + +- (void)handleTextChanged:(id)sender { + struct LWidget* w = FindWidgetForView(sender); + if (!w) return; + + UITextField* src = (UITextField*)sender; + const char* str = src.text.UTF8String; + struct LInput* ipt = (struct LInput*)w; + + ipt->text.length = 0; + String_AppendUtf8(&ipt->text, str, String_Length(str)); + if (ipt->TextChanged) ipt->TextChanged(ipt); +} + +- (void)handleValueChanged:(id)sender { + UISwitch* swt = (UISwitch*)sender; + UIView* parent = swt.superview; + struct LWidget* w = FindWidgetForView(parent); + if (!w) return; + + struct LCheckbox* cb = (struct LCheckbox*)w; + cb->value = [swt isOn]; + if (!cb->ValueChanged) return; + cb->ValueChanged(cb); +} + +// === UITableViewDataSource === +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + // cellForRowAtIndexPath - iOS 2.0 + //UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath]; + UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellID]; + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; + } + + LTable_UpdateCell(tableView, cell, (int)indexPath.row); + return cell; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + // numberOfRowsInSection - iOS 2.0 + struct LTable* w = (struct LTable*)FindWidgetForView(tableView); + return w ? w->rowsCount : 0; +} + +// === UITableViewDelegate === +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + // didSelectRowAtIndexPath - iOS 2.0 + int row = (int)indexPath.row; + struct ServerInfo* server = LTable_Get(row); + LTable_UpdateCellColor([tableView cellForRowAtIndexPath:indexPath], server, row, true); + + struct LTable* w = (struct LTable*)FindWidgetForView(tableView); + if (!w) return; + LTable_RowClick(w, row); +} + +- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { + // didDeselectRowAtIndexPath - iOS 2.0 + int row = (int)indexPath.row; + struct ServerInfo* server = LTable_Get(row); + LTable_UpdateCellColor([tableView cellForRowAtIndexPath:indexPath], server, row, false); +} + +// === UITextFieldDelegate === +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + // textFieldShouldReturn - iOS 2.0 + struct LWidget* w = FindWidgetForView(textField); + if (!w) return YES; + struct LWidget* sel = Launcher_Active->onEnterWidget; + + if (sel && !w->skipsEnter) { + sel->OnClick(sel); + } else { + [textField resignFirstResponder]; + } + return YES; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + // textFieldDidBeginEditing - iOS 2.0 + kb_widget = textField; +} + +@end + +static CCUIController* ui_controller; +void LBackend_Init(void) { + ui_controller = [[CCUIController alloc] init]; + CFBridgingRetain(ui_controller); // prevent GC TODO even needed? +} + +void LBackend_MarkDirty(void* widget) { } +void LBackend_Tick(void) { } +void LBackend_Free(void) { } +void LBackend_UpdateTitleFont(void) { } + +static void DrawText(NSAttributedString* str, struct Context2D* ctx, int x, int y) { + // CTLineCreateWithAttributedString - iOS 3.2 + CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)str); + CGRect bounds = CTLineGetImageBounds(line, win_ctx); + int centreX = (int)(ctx->width / 2.0f - bounds.size.width / 2.0f); + + CGContextSetTextPosition(win_ctx, centreX + x, ctx->height - y); + CTLineDraw(line, win_ctx); +} + +void LBackend_DrawTitle(struct Context2D* ctx, const char* title) { + if (Launcher_BitmappedText()) { + struct FontDesc font; + Launcher_MakeTitleFont(&font); + Launcher_DrawTitle(&font, title, ctx); + // bitmapped fonts don't need to be freed + return; + } + + // systemFontOfSize: - iOS 2.0 + UIFont* font = [UIFont systemFontOfSize:42]; + NSString* text = [NSString stringWithCString:title encoding:NSASCIIStringEncoding]; + + NSDictionary* attrs_bg = + @{ + TEXT_ATTRIBUTE_FONT : font, + TEXT_ATTRIBUTE_COLOR : UIColor.blackColor + }; + NSAttributedString* str_bg = [[NSAttributedString alloc] initWithString:text attributes:attrs_bg]; + DrawText(str_bg, ctx, 4, 42); + + NSDictionary* attrs_fg = + @{ + TEXT_ATTRIBUTE_FONT : font, + TEXT_ATTRIBUTE_COLOR : UIColor.whiteColor + }; + NSAttributedString* str_fg = [[NSAttributedString alloc] initWithString:text attributes:attrs_fg]; + DrawText(str_fg, ctx, 0, 38); +} + +void LBackend_InitFramebuffer(void) { } +void LBackend_FreeFramebuffer(void) { } + +void LBackend_Redraw(void) { + struct Context2D ctx; + struct Bitmap bmp; + int width = max(Window_Main.Width, 1); + int height = max(Window_Main.Height, 1); + + Window_AllocFramebuffer(&bmp, width, height); + Context2D_Wrap(&ctx, &bmp); + Launcher_Active->DrawBackground(Launcher_Active, &ctx); + Rect2D rect = { 0, 0, width, height }; + Window_DrawFramebuffer(rect, &bmp); + Window_FreeFramebuffer(&bmp); +} + +static void LBackend_ButtonUpdateBackground(struct LButton* w); +void LBackend_ThemeChanged(void) { + struct LScreen* s = Launcher_Active; + LBackend_Redraw(); + + for (int i = 0; i < s->numWidgets; i++) + { + struct LWidget* w = s->widgets[i]; + if (w->type != LWIDGET_BUTTON) continue; + LBackend_ButtonUpdateBackground((struct LButton*)w); + } +} + +/*########################################################################################################################* + *------------------------------------------------------ButtonWidget-------------------------------------------------------* + *#########################################################################################################################*/ +static void LBackend_ButtonUpdateBackground(struct LButton* w) { + UIButton* btn = (__bridge UIButton*)w->meta; + CGRect rect = [btn frame]; + int width = (int)rect.size.width; + int height = (int)rect.size.height; + // memory freeing deferred until UIImage is freed (see FreeContents) + struct Bitmap bmp1, bmp2; + struct Context2D ctx1, ctx2; + + Bitmap_Allocate(&bmp1, width, height); + Context2D_Wrap(&ctx1, &bmp1); + LButton_DrawBackground(&ctx1, 0, 0, width, height, false); + [btn setBackgroundImage:ToUIImage(&bmp1) forState:UIControlStateNormal]; + + Bitmap_Allocate(&bmp2, width, height); + Context2D_Wrap(&ctx2, &bmp2); + LButton_DrawBackground(&ctx2, 0, 0, width, height, true); + [btn setBackgroundImage:ToUIImage(&bmp2) forState:UIControlStateHighlighted]; +} + +void LBackend_ButtonInit(struct LButton* w, int width, int height) { + w->_textWidth = width; + w->_textHeight = height; +} + +static UIView* LBackend_ButtonShow(struct LButton* w) { + UIButton* btn = [[UIButton alloc] init]; + btn.frame = CGRectMake(0, 0, w->_textWidth, w->_textHeight); + [btn addTarget:ui_controller action:@selector(handleButtonPress:) forControlEvents:UIControlEventTouchUpInside]; + + w->meta = (__bridge void*)btn; + LBackend_ButtonUpdateBackground(w); + LBackend_ButtonUpdate(w); + return btn; +} + +void LBackend_ButtonUpdate(struct LButton* w) { + UIButton* btn = (__bridge UIButton*)w->meta; + + UIColor* color = GetStringColor(&w->text); + [btn setTitleColor:color forState:UIControlStateNormal]; + + NSString* str = GetColorlessString(&w->text); + [btn setTitle:str forState:UIControlStateNormal]; +} +void LBackend_ButtonDraw(struct LButton* w) { } + + +/*########################################################################################################################* + *-----------------------------------------------------CheckboxWidget------------------------------------------------------* + *#########################################################################################################################*/ +void LBackend_CheckboxInit(struct LCheckbox* w) { } + +static UIView* LBackend_CheckboxShow(struct LCheckbox* w) { + UIView* root = [[UIView alloc] init]; + CGRect frame; + + UISwitch* swt = [[UISwitch alloc] init]; + [swt addTarget:ui_controller action:@selector(handleValueChanged:) forControlEvents:UIControlEventValueChanged]; + + UILabel* lbl = [[UILabel alloc] init]; + lbl.backgroundColor = UIColor.clearColor; + lbl.textColor = UIColor.whiteColor; + lbl.text = ToNSString(&w->text); + [lbl sizeToFit]; // adjust label to fit text + + [root addSubview:swt]; + [root addSubview:lbl]; + + // label should be slightly to right of switch and vertically centred + frame = lbl.frame; + frame.origin.x = swt.frame.size.width + 10.0f; + frame.origin.y = swt.frame.size.height / 2 - frame.size.height / 2; + lbl.frame = frame; + + // adjust root view height to enclose children + frame = root.frame; + frame.size.width = lbl.frame.origin.x + lbl.frame.size.width; + frame.size.height = max(swt.frame.size.height, lbl.frame.size.height); + root.frame = frame; + + //root.userInteractionEnabled = YES; + w->meta = (__bridge void*)root; + LBackend_CheckboxUpdate(w); + return root; +} + +void LBackend_CheckboxUpdate(struct LCheckbox* w) { + UIView* root = (__bridge UIView*)w->meta; + UISwitch* swt = (UISwitch*)root.subviews[0]; + + swt.on = w->value; +} +void LBackend_CheckboxDraw(struct LCheckbox* w) { } + + +/*########################################################################################################################* + *------------------------------------------------------InputWidget--------------------------------------------------------* + *#########################################################################################################################*/ +void LInput_SetKeyboardType(UITextField* fld, int flags) { + int type = flags & 0xFF; + if (type == KEYBOARD_TYPE_INTEGER) { + fld.keyboardType = UIKeyboardTypeNumberPad; + } else if (type == KEYBOARD_TYPE_PASSWORD) { + fld.secureTextEntry = YES; + } + + if (flags & KEYBOARD_FLAG_SEND) { + fld.returnKeyType = UIReturnKeySend; + } else { + fld.returnKeyType = UIReturnKeyDone; + } +} + +void LInput_SetPlaceholder(UITextField* fld, const char* placeholder) { + if (!placeholder) return; + + cc_string hint = String_FromReadonly(placeholder); + fld.placeholder = ToNSString(&hint); +} + +void LBackend_InputInit(struct LInput* w, int width) { + w->_textHeight = width; +} + +static UIView* LBackend_InputShow(struct LInput* w) { + UITextField* fld = [[UITextField alloc] init]; + fld.frame = CGRectMake(0, 0, w->_textHeight, LINPUT_HEIGHT); + fld.borderStyle = UITextBorderStyleBezel; + fld.backgroundColor = UIColor.whiteColor; + fld.textColor = UIColor.blackColor; + fld.delegate = ui_controller; + [fld addTarget:ui_controller action:@selector(handleTextChanged:) forControlEvents:UIControlEventEditingChanged]; + + LInput_SetKeyboardType(fld, w->inputType); + LInput_SetPlaceholder(fld, w->hintText); + + w->meta = (__bridge void*)fld; + LBackend_InputUpdate(w); + return fld; +} + +void LBackend_InputUpdate(struct LInput* w) { + UITextField* fld = (__bridge UITextField*)w->meta; + fld.text = ToNSString(&w->text); +} + +void LBackend_InputDraw(struct LInput* w) { } +void LBackend_InputTick(struct LInput* w) { } +void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { } +void LBackend_InputUnselect(struct LInput* w) { } + + +/*########################################################################################################################* + *------------------------------------------------------LabelWidget--------------------------------------------------------* + *#########################################################################################################################*/ +void LBackend_LabelInit(struct LLabel* w) { } + +static UIView* LBackend_LabelShow(struct LLabel* w) { + UILabel* lbl = [[UILabel alloc] init]; + w->meta = (__bridge void*)lbl; + lbl.backgroundColor = UIColor.clearColor; + + if (w->small) lbl.font = [UIFont systemFontOfSize:14.0f]; + LBackend_LabelUpdate(w); + return lbl; +} + +void LBackend_LabelUpdate(struct LLabel* w) { + UILabel* lbl = (__bridge UILabel*)w->meta; + if (!lbl) return; + + if ([lbl respondsToSelector:@selector(attributedText)]) { + // attributedText - iOS 6.0 + lbl.attributedText = ToAttributedString(&w->text); + } else { + lbl.textColor = GetStringColor(&w->text); + lbl.text = GetColorlessString(&w->text); + } + + [lbl sizeToFit]; // adjust label to fit text +} +void LBackend_LabelDraw(struct LLabel* w) { } + + +/*########################################################################################################################* + *-------------------------------------------------------LineWidget--------------------------------------------------------* + *#########################################################################################################################*/ +void LBackend_LineInit(struct LLine* w, int width) { + w->_width = width; +} + +static UIView* LBackend_LineShow(struct LLine* w) { + UIView* view = [[UIView alloc] init]; + view.frame = CGRectMake(0, 0, w->_width, LLINE_HEIGHT); + w->meta = (__bridge void*)view; + + BitmapCol color = LLine_GetColor(); + view.backgroundColor = ToUIColor(color, 0.5f); + return view; +} +void LBackend_LineDraw(struct LLine* w) { } + + +/*########################################################################################################################* + *------------------------------------------------------SliderWidget-------------------------------------------------------* + *#########################################################################################################################*/ +void LBackend_SliderInit(struct LSlider* w, int width, int height) { + w->_width = width; + w->_height = height; +} + +static UIView* LBackend_SliderShow(struct LSlider* w) { + UIProgressView* prg = [[UIProgressView alloc] init]; + prg.frame = CGRectMake(0, 0, w->_width, w->_height); + prg.progressTintColor = ToUIColor(w->color, 1.0f); + + w->meta = (__bridge void*)prg; + return prg; +} + +void LBackend_SliderUpdate(struct LSlider* w) { + UIProgressView* prg = (__bridge UIProgressView*)w->meta; + + prg.progress = w->value / 100.0f; +} +void LBackend_SliderDraw(struct LSlider* w) { } + + +/*########################################################################################################################* + *------------------------------------------------------TableWidget-------------------------------------------------------* + *#########################################################################################################################*/ +void LBackend_TableInit(struct LTable* w) { } + +static UIView* LBackend_TableShow(struct LTable* w) { + UITableView* tbl = [[UITableView alloc] init]; + tbl.delegate = ui_controller; + tbl.dataSource = ui_controller; + tbl.editing = NO; + tbl.allowsSelection = YES; + LTable_UpdateCellColor(tbl, NULL, 1, false); + + //[tbl registerClass:UITableViewCell.class forCellReuseIdentifier:cellID]; + w->meta = (__bridge void*)tbl; + return tbl; +} + +void LBackend_TableUpdate(struct LTable* w) { + UITableView* tbl = (__bridge UITableView*)w->meta; + [tbl reloadData]; +} + +void LBackend_TableDraw(struct LTable* w) { } +void LBackend_TableReposition(struct LTable* w) { } +void LBackend_TableMouseDown(struct LTable* w, int idx) { } +void LBackend_TableMouseUp(struct LTable* w, int idx) { } +void LBackend_TableMouseMove(struct LTable* w, int idx) { } + +static void LTable_UpdateCellColor(UIView* view, struct ServerInfo* server, int row, cc_bool selected) { + BitmapCol color = LTable_RowColor(row, selected, server && server->featured); + if (color) { + view.backgroundColor = ToUIColor(color, 1.0f); + view.opaque = YES; + } else { + view.backgroundColor = UIColor.clearColor; + view.opaque = NO; + } +} + +static void LTable_UpdateCell(UITableView* table, UITableViewCell* cell, int row) { + struct ServerInfo* server = LTable_Get(row); + struct Flag* flag = Flags_Get(server); + + char descBuffer[128]; + cc_string desc = String_FromArray(descBuffer); + String_Format2(&desc, "%i/%i players, up for ", &server->players, &server->maxPlayers); + LTable_FormatUptime(&desc, server->uptime); + if (server->software.length) String_Format1(&desc, " | %s", &server->software); + + if (flag && flag->meta) + cell.imageView.image = (__bridge UIImage*)flag->meta; + + cell.textLabel.text = ToNSString(&server->name); + cell.detailTextLabel.text = ToNSString(&desc);//[ToNSString(&desc) stringByAppendingString:@"\nLine2"]; + cell.textLabel.textColor = UIColor.whiteColor; + cell.detailTextLabel.textColor = UIColor.whiteColor; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + NSIndexPath* sel = table.indexPathForSelectedRow; + cc_bool selected = sel && sel.row == row; + LTable_UpdateCellColor(cell, server, row, selected); +} + +// TODO only redraw flags +static void OnFlagsChanged(void) { + struct LScreen* s = Launcher_Active; + for (int i = 0; i < s->numWidgets; i++) + { + if (s->widgets[i]->type != LWIDGET_TABLE) continue; + UITableView* tbl = (__bridge UITableView*)s->widgets[i]->meta; + + // trying to update cell.imageView.image doesn't seem to work, + // so pointlessly reload entire table data instead + NSIndexPath* selected = [tbl indexPathForSelectedRow]; + [tbl reloadData]; + [tbl selectRowAtIndexPath:selected animated:NO scrollPosition:UITableViewScrollPositionNone]; + } +} + +/*########################################################################################################################* + *------------------------------------------------------UI Backend--------------------------------------------------------* + *#########################################################################################################################*/ +void LBackend_DecodeFlag(struct Flag* flag, cc_uint8* data, cc_uint32 len) { + NSData* ns_data = [NSData dataWithBytes:data length:len]; + UIImage* img = [UIImage imageWithData:ns_data]; + if (!img) return; + + flag->meta = CFBridgingRetain(img); + OnFlagsChanged(); +} + +static void LBackend_LayoutDimensions(struct LWidget* w, CGRect* r) { + const struct LLayout* l = w->layouts + 2; + while (l->type) + { + switch (l->type) + { + case LLAYOUT_WIDTH: + r->size.width = Window_Main.Width - (int)r->origin.x - Display_ScaleX(l->offset); + break; + case LLAYOUT_HEIGHT: + r->size.height = Window_Main.Height - (int)r->origin.y - Display_ScaleY(l->offset); + break; + } + l++; + } +} + +void LBackend_LayoutWidget(struct LWidget* w) { + const struct LLayout* l = w->layouts; + UIView* view = (__bridge UIView*)w->meta; + CGRect r = [view frame]; + int width = (int)r.size.width; + int height = (int)r.size.height; + + r.origin.x = Gui_CalcPos(l[0].type & 0xFF, Display_ScaleX(l[0].offset), width, Window_Main.Width); + r.origin.y = Gui_CalcPos(l[1].type & 0xFF, Display_ScaleY(l[1].offset), height, Window_Main.Height); + + // e.g. Table widget needs adjusts width/height based on window + if (l[1].type & LLAYOUT_EXTRA) + LBackend_LayoutDimensions(w, &r); + view.frame = r; +} + +static UIView* ShowWidget(struct LWidget* w) { + switch (w->type) + { + case LWIDGET_BUTTON: + return LBackend_ButtonShow((struct LButton*)w); + case LWIDGET_CHECKBOX: + return LBackend_CheckboxShow((struct LCheckbox*)w); + case LWIDGET_INPUT: + return LBackend_InputShow((struct LInput*)w); + case LWIDGET_LABEL: + return LBackend_LabelShow((struct LLabel*)w); + case LWIDGET_LINE: + return LBackend_LineShow((struct LLine*)w); + case LWIDGET_SLIDER: + return LBackend_SliderShow((struct LSlider*)w); + case LWIDGET_TABLE: + return LBackend_TableShow((struct LTable*)w); + } + return NULL; +} + +void LBackend_SetScreen(struct LScreen* s) { + for (int i = 0; i < s->numWidgets; i++) + { + struct LWidget* w = s->widgets[i]; + UIView* view = ShowWidget(w); + + [view_handle addSubview:view]; + } + // TODO replace with native constraints some day, maybe + s->Layout(s); +} + +void LBackend_CloseScreen(struct LScreen* s) { + if (!s) return; + + // remove reference to soon to be garbage collected views + for (int i = 0; i < s->numWidgets; i++) + { + s->widgets[i]->meta = NULL; + } + + // remove all widgets from previous screen + NSArray* elems = [view_handle subviews]; + for (UIView* view in elems) + { + [view removeFromSuperview]; + } +} +#endif diff --git a/src/interop_ios.m b/src/interop_ios.m index da777fa94..4f9c6e3d6 100644 --- a/src/interop_ios.m +++ b/src/interop_ios.m @@ -11,12 +11,8 @@ #include "Errors.h" #include "Drawer2D.h" #include "Launcher.h" -#include "LBackend.h" -#include "LWidgets.h" -#include "LScreens.h" -#include "Gui.h" -#include "LWeb.h" #include "Funcs.h" +#include "Gui.h" #include #include #include @@ -33,6 +29,17 @@ #define TEXT_ATTRIBUTE_COLOR UITextAttributeTextColor #endif +// shared state with LBackend_ios.m +UITextField* kb_widget; +CGContextRef win_ctx; +UIView* view_handle; + +UIColor* ToUIColor(BitmapCol color, float A); +NSString* ToNSString(const cc_string* text); +void LInput_SetKeyboardType(UITextField* fld, int flags); +void LInput_SetPlaceholder(UITextField* fld, const char* placeholder); + + @interface CCWindow : UIWindow @end @@ -45,7 +52,6 @@ static CCViewController* cc_controller; static UIWindow* win_handle; -static UIView* view_handle; static cc_bool launcherMode; static void AddTouch(UITouch* t) { @@ -182,7 +188,6 @@ static void DeleteExportTempFile(void) { } static cc_bool kb_active; -static UITextField* kb_widget; - (void)keyboardDidShow:(NSNotification*)notification { NSDictionary* info = notification.userInfo; if (kb_active) return; @@ -339,7 +344,7 @@ void Clipboard_SetText(const cc_string* value) { } /*########################################################################################################################* *------------------------------------------------------Common helpers--------------------------------------------------------* *#########################################################################################################################*/ -static UIColor* ToUIColor(BitmapCol color, float A) { +UIColor* ToUIColor(BitmapCol color, float A) { // colorWithRed:green:blue:alpha - iOS 2.0 float R = BitmapCol_R(color) / 255.0f; float G = BitmapCol_G(color) / 255.0f; @@ -347,72 +352,12 @@ static UIColor* ToUIColor(BitmapCol color, float A) { return [UIColor colorWithRed:R green:G blue:B alpha:A]; } -static NSString* ToNSString(const cc_string* text) { +NSString* ToNSString(const cc_string* text) { char raw[NATIVE_STR_LEN]; String_EncodeUtf8(raw, text); return [NSString stringWithUTF8String:raw]; } -static NSMutableAttributedString* ToAttributedString(const cc_string* text) { - // NSMutableAttributedString - iOS 3.2 - cc_string left = *text, part; - char colorCode = 'f'; - NSMutableAttributedString* str = [[NSMutableAttributedString alloc] init]; - - while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode)) - { - BitmapCol color = Drawer2D_GetColor(colorCode); - NSString* bit = ToNSString(&part); - NSDictionary* attrs = - @{ - //TEXT_ATTRIBUTE_FONT : font, - TEXT_ATTRIBUTE_COLOR : ToUIColor(color, 1.0f) - }; - NSAttributedString* attr_bit = [[NSAttributedString alloc] initWithString:bit attributes:attrs]; - [str appendAttributedString:attr_bit]; - } - return str; -} - - -static UIColor* GetStringColor(const cc_string* text) { - cc_string left = *text, part; - char colorCode = 'f'; - Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode); - - BitmapCol color = Drawer2D_GetColor(colorCode); - return ToUIColor(color, 1.0f); -} - -static NSString* GetColorlessString(const cc_string* text) { - char buffer[128]; - cc_string tmp = String_FromArray(buffer); - - String_AppendColorless(&tmp, text); - return ToNSString(&tmp); -} - - -static void FreeContents(void* info, const void* data, size_t size) { Mem_Free(data); } -// TODO probably a better way.. -static UIImage* ToUIImage(struct Bitmap* bmp) { - CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider; - CGImageRef image; - - provider = CGDataProviderCreateWithData(NULL, bmp->scan0, - Bitmap_DataSize(bmp->width, bmp->height), FreeContents); - image = CGImageCreate(bmp->width, bmp->height, 8, 32, bmp->width * 4, colorspace, - kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst, provider, NULL, 0, 0); - - UIImage* img = [UIImage imageWithCGImage:image]; - - CGImageRelease(image); - CGDataProviderRelease(provider); - CGColorSpaceRelease(colorspace); - return img; -} - /*########################################################################################################################* *------------------------------------------------------Logging/Time-------------------------------------------------------* @@ -564,8 +509,6 @@ void ShowDialogCore(const char* title, const char* msg) { } @end -static void LInput_SetKeyboardType(UITextField* fld, int flags); -static void LInput_SetPlaceholder(UITextField* fld, const char* placeholder); static UITextField* text_input; static CCKBController* kb_controller; @@ -1224,612 +1167,22 @@ void interop_SysTextDraw(struct DrawTextArgs* args, struct Context2D* ctx, int x #endif -/*########################################################################################################################* - *------------------------------------------------------UI Backend--------------------------------------------------------* - *#########################################################################################################################*/ -static struct LWidget* FindWidgetForView(id obj) { - struct LScreen* s = Launcher_Active; - for (int i = 0; i < s->numWidgets; i++) - { - void* meta = s->widgets[i]->meta; - if (meta != (__bridge void*)obj) continue; - - return s->widgets[i]; - } - return NULL; -} - -static void LTable_UpdateCellColor(UIView* view, struct ServerInfo* server, int row, cc_bool selected); -static void LTable_UpdateCell(UITableView* table, UITableViewCell* cell, int row); - -static NSString* cellID = @"CC_Cell"; -@interface CCUIController : NSObject -@end - -@implementation CCUIController - -- (void)handleButtonPress:(id)sender { - struct LWidget* w = FindWidgetForView(sender); - if (!w) return; - - struct LButton* btn = (struct LButton*)w; - btn->OnClick(btn); -} - -- (void)handleTextChanged:(id)sender { - struct LWidget* w = FindWidgetForView(sender); - if (!w) return; - - UITextField* src = (UITextField*)sender; - const char* str = src.text.UTF8String; - struct LInput* ipt = (struct LInput*)w; - - ipt->text.length = 0; - String_AppendUtf8(&ipt->text, str, String_Length(str)); - if (ipt->TextChanged) ipt->TextChanged(ipt); -} - -- (void)handleValueChanged:(id)sender { - UISwitch* swt = (UISwitch*)sender; - UIView* parent = swt.superview; - struct LWidget* w = FindWidgetForView(parent); - if (!w) return; - - struct LCheckbox* cb = (struct LCheckbox*)w; - cb->value = [swt isOn]; - if (!cb->ValueChanged) return; - cb->ValueChanged(cb); -} - -// === UITableViewDataSource === -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - // cellForRowAtIndexPath - iOS 2.0 - //UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath]; - UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellID]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; - } - - LTable_UpdateCell(tableView, cell, (int)indexPath.row); - return cell; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - // numberOfRowsInSection - iOS 2.0 - struct LTable* w = (struct LTable*)FindWidgetForView(tableView); - return w ? w->rowsCount : 0; -} - -// === UITableViewDelegate === -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - // didSelectRowAtIndexPath - iOS 2.0 - int row = (int)indexPath.row; - struct ServerInfo* server = LTable_Get(row); - LTable_UpdateCellColor([tableView cellForRowAtIndexPath:indexPath], server, row, true); - - struct LTable* w = (struct LTable*)FindWidgetForView(tableView); - if (!w) return; - LTable_RowClick(w, row); -} - -- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { - // didDeselectRowAtIndexPath - iOS 2.0 - int row = (int)indexPath.row; - struct ServerInfo* server = LTable_Get(row); - LTable_UpdateCellColor([tableView cellForRowAtIndexPath:indexPath], server, row, false); -} - -// === UITextFieldDelegate === -- (BOOL)textFieldShouldReturn:(UITextField *)textField { - // textFieldShouldReturn - iOS 2.0 - struct LWidget* w = FindWidgetForView(textField); - if (!w) return YES; - struct LWidget* sel = Launcher_Active->onEnterWidget; - - if (sel && !w->skipsEnter) { - sel->OnClick(sel); - } else { - [textField resignFirstResponder]; - } - return YES; -} - -- (void)textFieldDidBeginEditing:(UITextField *)textField { - // textFieldDidBeginEditing - iOS 2.0 - kb_widget = textField; -} - -@end - -static CCUIController* ui_controller; -void LBackend_Init(void) { - ui_controller = [[CCUIController alloc] init]; - CFBridgingRetain(ui_controller); // prevent GC TODO even needed? -} -static CGContextRef win_ctx; - -void LBackend_MarkDirty(void* widget) { } -void LBackend_Tick(void) { } -void LBackend_Free(void) { } -void LBackend_UpdateTitleFont(void) { } - -static void DrawText(NSAttributedString* str, struct Context2D* ctx, int x, int y) { - // CTLineCreateWithAttributedString - iOS 3.2 - CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)str); - CGRect bounds = CTLineGetImageBounds(line, win_ctx); - int centreX = (int)(ctx->width / 2.0f - bounds.size.width / 2.0f); - - CGContextSetTextPosition(win_ctx, centreX + x, ctx->height - y); - CTLineDraw(line, win_ctx); -} - -void LBackend_DrawTitle(struct Context2D* ctx, const char* title) { - if (Launcher_BitmappedText()) { - struct FontDesc font; - Launcher_MakeTitleFont(&font); - Launcher_DrawTitle(&font, title, ctx); - // bitmapped fonts don't need to be freed - return; - } - - // systemFontOfSize: - iOS 2.0 - UIFont* font = [UIFont systemFontOfSize:42]; - NSString* text = [NSString stringWithCString:title encoding:NSASCIIStringEncoding]; - - NSDictionary* attrs_bg = - @{ - TEXT_ATTRIBUTE_FONT : font, - TEXT_ATTRIBUTE_COLOR : UIColor.blackColor - }; - NSAttributedString* str_bg = [[NSAttributedString alloc] initWithString:text attributes:attrs_bg]; - DrawText(str_bg, ctx, 4, 42); - - NSDictionary* attrs_fg = - @{ - TEXT_ATTRIBUTE_FONT : font, - TEXT_ATTRIBUTE_COLOR : UIColor.whiteColor - }; - NSAttributedString* str_fg = [[NSAttributedString alloc] initWithString:text attributes:attrs_fg]; - DrawText(str_fg, ctx, 0, 38); -} - -void LBackend_InitFramebuffer(void) { } -void LBackend_FreeFramebuffer(void) { } - -void LBackend_Redraw(void) { - struct Context2D ctx; - struct Bitmap bmp; - bmp.width = max(Window_Main.Width, 1); - bmp.height = max(Window_Main.Height, 1); - bmp.scan0 = (BitmapCol*)Mem_Alloc(bmp.width * bmp.height, BITMAPCOLOR_SIZE, "window pixels"); - - Context2D_Wrap(&ctx, &bmp); - win_ctx = CGBitmapContextCreate(bmp.scan0, bmp.width, bmp.height, 8, bmp.width * 4, - CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst); - Launcher_Active->DrawBackground(Launcher_Active, &ctx); - - view_handle.layer.contents = CFBridgingRelease(CGBitmapContextCreateImage(win_ctx)); - Mem_Free(bmp.scan0); // TODO Context2D_Free - CGContextRelease(win_ctx); -} - -static void LBackend_ButtonUpdateBackground(struct LButton* w); -void LBackend_ThemeChanged(void) { - struct LScreen* s = Launcher_Active; - LBackend_Redraw(); - - for (int i = 0; i < s->numWidgets; i++) - { - struct LWidget* w = s->widgets[i]; - if (w->type != LWIDGET_BUTTON) continue; - LBackend_ButtonUpdateBackground((struct LButton*)w); - } -} - -/*########################################################################################################################* - *------------------------------------------------------ButtonWidget-------------------------------------------------------* - *#########################################################################################################################*/ -static void LBackend_ButtonUpdateBackground(struct LButton* w) { - UIButton* btn = (__bridge UIButton*)w->meta; - CGRect rect = [btn frame]; - int width = (int)rect.size.width; - int height = (int)rect.size.height; - // memory freeing deferred until UIImage is freed (see FreeContents) - struct Bitmap bmp1, bmp2; - struct Context2D ctx1, ctx2; - - Bitmap_Allocate(&bmp1, width, height); - Context2D_Wrap(&ctx1, &bmp1); - LButton_DrawBackground(&ctx1, 0, 0, width, height, false); - [btn setBackgroundImage:ToUIImage(&bmp1) forState:UIControlStateNormal]; - - Bitmap_Allocate(&bmp2, width, height); - Context2D_Wrap(&ctx2, &bmp2); - LButton_DrawBackground(&ctx2, 0, 0, width, height, true); - [btn setBackgroundImage:ToUIImage(&bmp2) forState:UIControlStateHighlighted]; -} - -void LBackend_ButtonInit(struct LButton* w, int width, int height) { - w->_textWidth = width; - w->_textHeight = height; -} - -static UIView* LBackend_ButtonShow(struct LButton* w) { - UIButton* btn = [[UIButton alloc] init]; - btn.frame = CGRectMake(0, 0, w->_textWidth, w->_textHeight); - [btn addTarget:ui_controller action:@selector(handleButtonPress:) forControlEvents:UIControlEventTouchUpInside]; - - w->meta = (__bridge void*)btn; - LBackend_ButtonUpdateBackground(w); - LBackend_ButtonUpdate(w); - return btn; -} - -void LBackend_ButtonUpdate(struct LButton* w) { - UIButton* btn = (__bridge UIButton*)w->meta; - - UIColor* color = GetStringColor(&w->text); - [btn setTitleColor:color forState:UIControlStateNormal]; - - NSString* str = GetColorlessString(&w->text); - [btn setTitle:str forState:UIControlStateNormal]; -} -void LBackend_ButtonDraw(struct LButton* w) { } - - -/*########################################################################################################################* - *-----------------------------------------------------CheckboxWidget------------------------------------------------------* - *#########################################################################################################################*/ -void LBackend_CheckboxInit(struct LCheckbox* w) { } - -static UIView* LBackend_CheckboxShow(struct LCheckbox* w) { - UIView* root = [[UIView alloc] init]; - CGRect frame; - - UISwitch* swt = [[UISwitch alloc] init]; - [swt addTarget:ui_controller action:@selector(handleValueChanged:) forControlEvents:UIControlEventValueChanged]; - - UILabel* lbl = [[UILabel alloc] init]; - lbl.backgroundColor = UIColor.clearColor; - lbl.textColor = UIColor.whiteColor; - lbl.text = ToNSString(&w->text); - [lbl sizeToFit]; // adjust label to fit text - - [root addSubview:swt]; - [root addSubview:lbl]; - - // label should be slightly to right of switch and vertically centred - frame = lbl.frame; - frame.origin.x = swt.frame.size.width + 10.0f; - frame.origin.y = swt.frame.size.height / 2 - frame.size.height / 2; - lbl.frame = frame; - - // adjust root view height to enclose children - frame = root.frame; - frame.size.width = lbl.frame.origin.x + lbl.frame.size.width; - frame.size.height = max(swt.frame.size.height, lbl.frame.size.height); - root.frame = frame; - - //root.userInteractionEnabled = YES; - w->meta = (__bridge void*)root; - LBackend_CheckboxUpdate(w); - return root; -} - -void LBackend_CheckboxUpdate(struct LCheckbox* w) { - UIView* root = (__bridge UIView*)w->meta; - UISwitch* swt = (UISwitch*)root.subviews[0]; - - swt.on = w->value; -} -void LBackend_CheckboxDraw(struct LCheckbox* w) { } - - -/*########################################################################################################################* - *------------------------------------------------------InputWidget--------------------------------------------------------* - *#########################################################################################################################*/ -static void LInput_SetKeyboardType(UITextField* fld, int flags) { - int type = flags & 0xFF; - if (type == KEYBOARD_TYPE_INTEGER) { - fld.keyboardType = UIKeyboardTypeNumberPad; - } else if (type == KEYBOARD_TYPE_PASSWORD) { - fld.secureTextEntry = YES; - } - - if (flags & KEYBOARD_FLAG_SEND) { - fld.returnKeyType = UIReturnKeySend; - } else { - fld.returnKeyType = UIReturnKeyDone; - } -} - -static void LInput_SetPlaceholder(UITextField* fld, const char* placeholder) { - if (!placeholder) return; - - cc_string hint = String_FromReadonly(placeholder); - fld.placeholder = ToNSString(&hint); -} - -void LBackend_InputInit(struct LInput* w, int width) { - w->_textHeight = width; -} - -static UIView* LBackend_InputShow(struct LInput* w) { - UITextField* fld = [[UITextField alloc] init]; - fld.frame = CGRectMake(0, 0, w->_textHeight, LINPUT_HEIGHT); - fld.borderStyle = UITextBorderStyleBezel; - fld.backgroundColor = UIColor.whiteColor; - fld.textColor = UIColor.blackColor; - fld.delegate = ui_controller; - [fld addTarget:ui_controller action:@selector(handleTextChanged:) forControlEvents:UIControlEventEditingChanged]; - - LInput_SetKeyboardType(fld, w->inputType); - LInput_SetPlaceholder(fld, w->hintText); - - w->meta = (__bridge void*)fld; - LBackend_InputUpdate(w); - return fld; -} - -void LBackend_InputUpdate(struct LInput* w) { - UITextField* fld = (__bridge UITextField*)w->meta; - fld.text = ToNSString(&w->text); -} - -void LBackend_InputDraw(struct LInput* w) { } -void LBackend_InputTick(struct LInput* w) { } -void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { } -void LBackend_InputUnselect(struct LInput* w) { } - - -/*########################################################################################################################* - *------------------------------------------------------LabelWidget--------------------------------------------------------* - *#########################################################################################################################*/ -void LBackend_LabelInit(struct LLabel* w) { } - -static UIView* LBackend_LabelShow(struct LLabel* w) { - UILabel* lbl = [[UILabel alloc] init]; - w->meta = (__bridge void*)lbl; - lbl.backgroundColor = UIColor.clearColor; - - if (w->small) lbl.font = [UIFont systemFontOfSize:14.0f]; - LBackend_LabelUpdate(w); - return lbl; -} - -void LBackend_LabelUpdate(struct LLabel* w) { - UILabel* lbl = (__bridge UILabel*)w->meta; - if (!lbl) return; - - if ([lbl respondsToSelector:@selector(attributedText)]) { - // attributedText - iOS 6.0 - lbl.attributedText = ToAttributedString(&w->text); - } else { - lbl.textColor = GetStringColor(&w->text); - lbl.text = GetColorlessString(&w->text); - } - - [lbl sizeToFit]; // adjust label to fit text -} -void LBackend_LabelDraw(struct LLabel* w) { } - - -/*########################################################################################################################* - *-------------------------------------------------------LineWidget--------------------------------------------------------* - *#########################################################################################################################*/ -void LBackend_LineInit(struct LLine* w, int width) { - w->_width = width; -} - -static UIView* LBackend_LineShow(struct LLine* w) { - UIView* view = [[UIView alloc] init]; - view.frame = CGRectMake(0, 0, w->_width, LLINE_HEIGHT); - w->meta = (__bridge void*)view; - - BitmapCol color = LLine_GetColor(); - view.backgroundColor = ToUIColor(color, 0.5f); - return view; -} -void LBackend_LineDraw(struct LLine* w) { } - - -/*########################################################################################################################* - *------------------------------------------------------SliderWidget-------------------------------------------------------* - *#########################################################################################################################*/ -void LBackend_SliderInit(struct LSlider* w, int width, int height) { - w->_width = width; - w->_height = height; -} - -static UIView* LBackend_SliderShow(struct LSlider* w) { - UIProgressView* prg = [[UIProgressView alloc] init]; - prg.frame = CGRectMake(0, 0, w->_width, w->_height); - prg.progressTintColor = ToUIColor(w->color, 1.0f); - - w->meta = (__bridge void*)prg; - return prg; -} - -void LBackend_SliderUpdate(struct LSlider* w) { - UIProgressView* prg = (__bridge UIProgressView*)w->meta; - - prg.progress = w->value / 100.0f; -} -void LBackend_SliderDraw(struct LSlider* w) { } - - -/*########################################################################################################################* - *------------------------------------------------------TableWidget-------------------------------------------------------* - *#########################################################################################################################*/ -void LBackend_TableInit(struct LTable* w) { } - -static UIView* LBackend_TableShow(struct LTable* w) { - UITableView* tbl = [[UITableView alloc] init]; - tbl.delegate = ui_controller; - tbl.dataSource = ui_controller; - tbl.editing = NO; - tbl.allowsSelection = YES; - LTable_UpdateCellColor(tbl, NULL, 1, false); - - //[tbl registerClass:UITableViewCell.class forCellReuseIdentifier:cellID]; - w->meta = (__bridge void*)tbl; - return tbl; -} - -void LBackend_TableUpdate(struct LTable* w) { - UITableView* tbl = (__bridge UITableView*)w->meta; - [tbl reloadData]; -} - -void LBackend_TableDraw(struct LTable* w) { } -void LBackend_TableReposition(struct LTable* w) { } -void LBackend_TableMouseDown(struct LTable* w, int idx) { } -void LBackend_TableMouseUp(struct LTable* w, int idx) { } -void LBackend_TableMouseMove(struct LTable* w, int idx) { } - -static void LTable_UpdateCellColor(UIView* view, struct ServerInfo* server, int row, cc_bool selected) { - BitmapCol color = LTable_RowColor(row, selected, server && server->featured); - if (color) { - view.backgroundColor = ToUIColor(color, 1.0f); - view.opaque = YES; - } else { - view.backgroundColor = UIColor.clearColor; - view.opaque = NO; - } -} - -static void LTable_UpdateCell(UITableView* table, UITableViewCell* cell, int row) { - struct ServerInfo* server = LTable_Get(row); - struct Flag* flag = Flags_Get(server); - - char descBuffer[128]; - cc_string desc = String_FromArray(descBuffer); - String_Format2(&desc, "%i/%i players, up for ", &server->players, &server->maxPlayers); - LTable_FormatUptime(&desc, server->uptime); - if (server->software.length) String_Format1(&desc, " | %s", &server->software); - - if (flag && flag->meta) - cell.imageView.image = (__bridge UIImage*)flag->meta; - - cell.textLabel.text = ToNSString(&server->name); - cell.detailTextLabel.text = ToNSString(&desc);//[ToNSString(&desc) stringByAppendingString:@"\nLine2"]; - cell.textLabel.textColor = UIColor.whiteColor; - cell.detailTextLabel.textColor = UIColor.whiteColor; - cell.selectionStyle = UITableViewCellSelectionStyleNone; - - NSIndexPath* sel = table.indexPathForSelectedRow; - cc_bool selected = sel && sel.row == row; - LTable_UpdateCellColor(cell, server, row, selected); -} - -// TODO only redraw flags -static void OnFlagsChanged(void) { - struct LScreen* s = Launcher_Active; - for (int i = 0; i < s->numWidgets; i++) - { - if (s->widgets[i]->type != LWIDGET_TABLE) continue; - UITableView* tbl = (__bridge UITableView*)s->widgets[i]->meta; - - // trying to update cell.imageView.image doesn't seem to work, - // so pointlessly reload entire table data instead - NSIndexPath* selected = [tbl indexPathForSelectedRow]; - [tbl reloadData]; - [tbl selectRowAtIndexPath:selected animated:NO scrollPosition:UITableViewScrollPositionNone]; - } -} - -/*########################################################################################################################* - *------------------------------------------------------UI Backend--------------------------------------------------------* - *#########################################################################################################################*/ -void LBackend_DecodeFlag(struct Flag* flag, cc_uint8* data, cc_uint32 len) { - NSData* ns_data = [NSData dataWithBytes:data length:len]; - UIImage* img = [UIImage imageWithData:ns_data]; - if (!img) return; +void Window_AllocFramebuffer(struct Bitmap* bmp, int width, int height) { + bmp->width = width; + bmp->height = height; + bmp->scan0 = (BitmapCol*)Mem_Alloc(width * height, BITMAPCOLOR_SIZE, "window pixels"); - flag->meta = CFBridgingRetain(img); - OnFlagsChanged(); + win_ctx = CGBitmapContextCreate(bmp->scan0, width, height, 8, width * 4, + CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst); } -static void LBackend_LayoutDimensions(struct LWidget* w, CGRect* r) { - const struct LLayout* l = w->layouts + 2; - while (l->type) - { - switch (l->type) - { - case LLAYOUT_WIDTH: - r->size.width = Window_Main.Width - (int)r->origin.x - Display_ScaleX(l->offset); - break; - case LLAYOUT_HEIGHT: - r->size.height = Window_Main.Height - (int)r->origin.y - Display_ScaleY(l->offset); - break; - } - l++; - } +void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) { + CGImageRef image = CGBitmapContextCreateImage(win_ctx); + view_handle.layer.contents = CFBridgingRelease(image); } -void LBackend_LayoutWidget(struct LWidget* w) { - const struct LLayout* l = w->layouts; - UIView* view = (__bridge UIView*)w->meta; - CGRect r = [view frame]; - int width = (int)r.size.width; - int height = (int)r.size.height; - - r.origin.x = Gui_CalcPos(l[0].type & 0xFF, Display_ScaleX(l[0].offset), width, Window_Main.Width); - r.origin.y = Gui_CalcPos(l[1].type & 0xFF, Display_ScaleY(l[1].offset), height, Window_Main.Height); - - // e.g. Table widget needs adjusts width/height based on window - if (l[1].type & LLAYOUT_EXTRA) - LBackend_LayoutDimensions(w, &r); - view.frame = r; -} - -static UIView* ShowWidget(struct LWidget* w) { - switch (w->type) - { - case LWIDGET_BUTTON: - return LBackend_ButtonShow((struct LButton*)w); - case LWIDGET_CHECKBOX: - return LBackend_CheckboxShow((struct LCheckbox*)w); - case LWIDGET_INPUT: - return LBackend_InputShow((struct LInput*)w); - case LWIDGET_LABEL: - return LBackend_LabelShow((struct LLabel*)w); - case LWIDGET_LINE: - return LBackend_LineShow((struct LLine*)w); - case LWIDGET_SLIDER: - return LBackend_SliderShow((struct LSlider*)w); - case LWIDGET_TABLE: - return LBackend_TableShow((struct LTable*)w); - } - return NULL; -} - -void LBackend_SetScreen(struct LScreen* s) { - for (int i = 0; i < s->numWidgets; i++) - { - struct LWidget* w = s->widgets[i]; - UIView* view = ShowWidget(w); - - [view_handle addSubview:view]; - } - // TODO replace with native constraints some day, maybe - s->Layout(s); -} - -void LBackend_CloseScreen(struct LScreen* s) { - if (!s) return; - - // remove reference to soon to be garbage collected views - for (int i = 0; i < s->numWidgets; i++) - { - s->widgets[i]->meta = NULL; - } - - // remove all widgets from previous screen - NSArray* elems = [view_handle subviews]; - for (UIView* view in elems) - { - [view removeFromSuperview]; - } +void Window_FreeFramebuffer(struct Bitmap* bmp) { + Mem_Free(bmp->scan0); + CGContextRelease(win_ctx); } #endif