diff --git a/Tests/Manual/ThemeKitchenSinkA3/AppController.j b/Tests/Manual/ThemeKitchenSinkA3/AppController.j new file mode 100644 index 000000000..3968fa37b --- /dev/null +++ b/Tests/Manual/ThemeKitchenSinkA3/AppController.j @@ -0,0 +1,312 @@ +/* + * AppController.j + * KitchenSink in Code + * + * Created by Daniel Böhringer 2026. + */ + +@import +@import + +// -------------------------------------------------------------------------------- +// KitchenSinkWindowController +// Manages a single window instance, its specific style (HUD/Standard), and content state. +// -------------------------------------------------------------------------------- + +@implementation KitchenSinkWindowController : CPWindowController +{ + BOOL _isHUD; + BOOL _areControlsEnabled; +} + +- (id)initWithContentRect:(CGRect)aRect isHUD:(BOOL)isHUD enabled:(BOOL)isEnabled +{ + // 1. Determine Style Mask + var styleMask = CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask; + + if (isHUD) + styleMask |= CPHUDBackgroundWindowMask; + + // 2. Create Window + var theWindow = [[CPWindow alloc] initWithContentRect:aRect styleMask:styleMask]; + + self = [super initWithWindow:theWindow]; + + if (self) + { + _isHUD = isHUD; + _areControlsEnabled = isEnabled; + + var title = (isHUD ? @"HUD Theme" : @"Aqua Theme") + (isEnabled ? @" (Enabled)" : @" (Disabled)"); + [theWindow setTitle:title]; + + // 3. Setup Toolbar + var toolbar = [[CPToolbar alloc] initWithIdentifier:@"KitchenSinkToolbar" + (isHUD ? @"HUD" : @"Aqua")]; + [toolbar setDelegate:self]; + [toolbar setVisible:YES]; + [theWindow setToolbar:toolbar]; + + // 4. Build UI + [self _buildInterface]; + + // 5. Apply Enabled State + if (!isEnabled) + [self _disableAllControls]; + } + + return self; +} + +- (void)_buildInterface +{ + var window = [self window], + contentView = [window contentView], + col1X = 20.0, + col2X = 200.0, + startY = 20.0, + gapY = 35.0, + width = 150.0; + + // --- COLUMN 1 --- + + // Push Button + var pushButton = [[CPButton alloc] initWithFrame:CGRectMake(col1X, startY, width, 24)]; + [pushButton setTitle:@"Push Button"]; + [contentView addSubview:pushButton]; + + // Gradient Button + var gradientButton = [[CPButton alloc] initWithFrame:CGRectMake(col1X, startY + gapY, width, 24)]; + [gradientButton setTitle:@"Gradient Button"]; + [contentView addSubview:gradientButton]; + + // Round Rect Button + var roundRectButton = [[CPButton alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 2), width, 24)]; + [roundRectButton setTitle:@"Round Rect Button"]; + [roundRectButton setBezelStyle:CPRoundedBezelStyle]; + [contentView addSubview:roundRectButton]; + + // Placeholder TextField + var placeholderField = [[CPTextField alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 3), width, 29)]; + [placeholderField setEditable:YES]; + [placeholderField setBezeled:YES]; + [placeholderField setPlaceholderString:@"Placeholder"]; + [contentView addSubview:placeholderField]; + + // Normal TextField + var textField = [[CPTextField alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 4), width, 29)]; + [textField setEditable:YES]; + [textField setBezeled:YES]; + [textField setStringValue:@"Text Field"]; + [contentView addSubview:textField]; + + // Search Field + var searchField = [[CPSearchField alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 5), width, 30)]; + [searchField setPlaceholderString:@"Search..."]; + [contentView addSubview:searchField]; + + // Token Field + var tokenField = [[CPTokenField alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 6), width, 30)]; + [tokenField setObjectValue:["Token", "Field"]]; + [contentView addSubview:tokenField]; + + // Combo Box + var comboBox = [[CPComboBox alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 7), width, 29)]; + [comboBox setPlaceholderString:@"Combo Box"]; + [comboBox addItemsWithObjectValues:["Alpha", "Beta", "Gamma"]]; + [contentView addSubview:comboBox]; + + // Slider + var bottomSlider = [[CPSlider alloc] initWithFrame:CGRectMake(col1X, startY + (gapY * 8.5), width, 24)]; + [contentView addSubview:bottomSlider]; + + + // --- COLUMN 2 --- + + // Date Picker + var datePicker = [[CPDatePicker alloc] initWithFrame:CGRectMake(col2X, startY, 140, 28)]; + [datePicker setDatePickerStyle:CPTextFieldAndStepperDatePickerStyle]; + [datePicker setDateValue:[CPDate date]]; + [contentView addSubview:datePicker]; + + // Checkboxes (Grouped visually) + var cbY = startY + gapY; + var cbOn = [CPCheckBox checkBoxWithTitle:@"On"]; + [cbOn setFrameOrigin:CGPointMake(col2X, cbY)]; + [cbOn setState:CPOnState]; + [contentView addSubview:cbOn]; + + var cbOff = [CPCheckBox checkBoxWithTitle:@"Off"]; + [cbOff setFrameOrigin:CGPointMake(col2X + 50, cbY)]; + [cbOff setState:CPOffState]; + [contentView addSubview:cbOff]; + + var cbBoth = [CPCheckBox checkBoxWithTitle:@"Mixed"]; + [cbBoth setFrameOrigin:CGPointMake(col2X + 100, cbY)]; + [cbBoth setState:CPMixedState]; + [contentView addSubview:cbBoth]; + + // Pop Up Button + var popUp = [[CPPopUpButton alloc] initWithFrame:CGRectMake(col2X, startY + (gapY * 2), 150, 24) pullsDown:NO]; + [popUp addItemWithTitle:@"Item 1"]; + [popUp addItemWithTitle:@"Item 2"]; + [contentView addSubview:popUp]; + + // Segmented Control + var seg = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(col2X, startY + (gapY * 3), 160, 24)]; + [seg setSegmentCount:3]; + [seg setLabel:@"One" forSegment:0]; + [seg setLabel:@"Two" forSegment:1]; + [seg setLabel:@"Three" forSegment:2]; + [seg setWidth:50 forSegment:0]; + [seg setSelectedSegment:0]; + [contentView addSubview:seg]; + + // Radio Buttons + var radioY = startY + (gapY * 4); + var radio1 = [CPRadio radioWithTitle:@"Radio A"]; + [radio1 setFrameOrigin:CGPointMake(col2X, radioY)]; + [radio1 setState:CPOnState]; + [contentView addSubview:radio1]; + + var radio2 = [CPRadio radioWithTitle:@"Radio B"]; + [radio2 setFrameOrigin:CGPointMake(col2X, radioY + 22)]; + [contentView addSubview:radio2]; + + // Link radios target + [radio1 setTarget:self]; [radio1 setAction:@selector(dummyAction:)]; + [radio2 setTarget:self]; [radio2 setAction:@selector(dummyAction:)]; + + // Level Indicator + var levelInd = [[CPLevelIndicator alloc] initWithFrame:CGRectMake(col2X, startY + (gapY * 5.5), 150, 18)]; + [levelInd setMaxValue:5]; + [levelInd setDoubleValue:3]; + [levelInd setLevelIndicatorStyle:CPDiscreteCapacityLevelIndicatorStyle]; + [contentView addSubview:levelInd]; + + // Tick Slider + var tickSlider = [[CPSlider alloc] initWithFrame:CGRectMake(col2X, startY + (gapY * 6.5), 110, 24)]; + //[tickSlider setNumberOfTickMarks:5]; + [contentView addSubview:tickSlider]; + + // Vertical Slider + var vSlider = [[CPSlider alloc] initWithFrame:CGRectMake(col2X + 130, startY + (gapY * 6.5), 24, 70)]; + [contentView addSubview:vSlider]; + + // Circular Slider + var knob = [[CPSlider alloc] initWithFrame:CGRectMake(col2X, startY + (gapY * 7.5), 32, 32)]; + [knob setSliderType:CPCircularSlider]; + [contentView addSubview:knob]; +} + +- (void)_disableAllControls +{ + var contentView = [[self window] contentView], + subviews = [contentView subviews], + count = [subviews count]; + + for (var i = 0; i < count; i++) + { + var view = subviews[i]; + if ([view respondsToSelector:@selector(setEnabled:)]) + { + [view setEnabled:NO]; + } + + // Handle specific case for labels usually associated with controls + // (Not strictly necessary as CPTextField disables nicely, but good for completeness) + if ([view isKindOfClass:[CPTextField class]] && ![view isEditable]) + { + //[view setTextColor:[CPColor disabledControlTextColor]]; + } + } +} + +- (void)dummyAction:(id)sender +{ + // Placeholder action for controls +} + +// --- TOOLBAR DELEGATE --- + +- (CPArray)toolbarAllowedItemIdentifiers:(CPToolbar)aToolbar +{ + return [@"ColorsItem", CPToolbarFlexibleSpaceItemIdentifier]; +} + +- (CPArray)toolbarDefaultItemIdentifiers:(CPToolbar)aToolbar +{ + return [CPToolbarFlexibleSpaceItemIdentifier, @"ColorsItem"]; +} + +- (CPToolbarItem)toolbar:(CPToolbar)aToolbar itemForItemIdentifier:(CPString)anItemIdentifier willBeInsertedIntoToolbar:(BOOL)flag +{ + var item = [[CPToolbarItem alloc] initWithItemIdentifier:anItemIdentifier]; + + if (anItemIdentifier == @"ColorsItem") + { + [item setLabel:@"Colors"]; + [item setPaletteLabel:@"Colors"]; + + // Simulating the Color Wheel icon via a drawing block or placeholder resource + // Since we don't have the PNG, we assume standard bundle presence or generic text + [item setImage:[[CPImage alloc] initWithContentsOfFile:@"Resources/ColorWheel.png" size:CGSizeMake(32, 32)]]; + + [item setTarget:self]; + [item setAction:@selector(orderFrontColorPanel:)]; + } + + return item; +} + +@end + + +// -------------------------------------------------------------------------------- +// AppController +// Orchestrates the creation of the 4 windows. +// -------------------------------------------------------------------------------- + +@implementation AppController : CPObject +{ + CPArray windows; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + windows = []; + + var winWidth = 400.0, + winHeight = 420.0, + padding = 20.0; + + // 1. Top Left: Normal, Enabled + var wc1 = [[KitchenSinkWindowController alloc] initWithContentRect:CGRectMake(50, 50, winWidth, winHeight) + isHUD:NO + enabled:YES]; + [wc1 showWindow:self]; + [windows addObject:wc1]; + + // 2. Top Right: Normal, Disabled + var wc2 = [[KitchenSinkWindowController alloc] initWithContentRect:CGRectMake(50 + winWidth + padding, 50, winWidth, winHeight) + isHUD:NO + enabled:NO]; + [wc2 showWindow:self]; + [windows addObject:wc2]; + + // 3. Bottom Left: HUD, Enabled + var wc3 = [[KitchenSinkWindowController alloc] initWithContentRect:CGRectMake(50, 50 + winHeight + padding + 30, winWidth, winHeight) + isHUD:YES + enabled:YES]; + [wc3 showWindow:self]; + [windows addObject:wc3]; + + // 4. Bottom Right: HUD, Disabled + var wc4 = [[KitchenSinkWindowController alloc] initWithContentRect:CGRectMake(50 + winWidth + padding, 50 + winHeight + padding + 30, winWidth, winHeight) + isHUD:YES + enabled:NO]; + [wc4 showWindow:self]; + [windows addObject:wc4]; + +} + +@end diff --git a/Tests/Manual/ThemeKitchenSinkA3/Info.plist b/Tests/Manual/ThemeKitchenSinkA3/Info.plist new file mode 100644 index 000000000..49388bc02 --- /dev/null +++ b/Tests/Manual/ThemeKitchenSinkA3/Info.plist @@ -0,0 +1,14 @@ + + + + + CPDefaultTheme + Aristo3 + CPApplicationDelegateClass + AppController + CPBundleName + kitchensink_a3 + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/ThemeKitchenSinkA3/Jakefile b/Tests/Manual/ThemeKitchenSinkA3/Jakefile new file mode 100644 index 000000000..32168ab86 --- /dev/null +++ b/Tests/Manual/ThemeKitchenSinkA3/Jakefile @@ -0,0 +1,182 @@ +/* + * Jakefile + * ThemeKitchenSinkA3 + * + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"), + projectName = "ThemeKitchenSinkA3"; + +app (projectName, function(task) +{ + ENV["OBJJ_INCLUDE_PATHS"] = "Frameworks"; + + if (configuration === "Debug") + ENV["OBJJ_INCLUDE_PATHS"] = FILE.join(ENV["OBJJ_INCLUDE_PATHS"], configuration); + + task.setBuildIntermediatesPath(FILE.join("Build", "ThemeKitchenSinkA3.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("ThemeKitchenSinkA3"); + task.setIdentifier("com.yourcompany.ThemeKitchenSinkA3"); + task.setVersion("1.0"); + task.setAuthor("SlevenBits, Ltd."); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("ThemeKitchenSinkA3"); + task.setSources(new FileList("**/*.j").exclude(FILE.join("Build", "**")).exclude(FILE.join("Frameworks", "Source", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", [projectName], function() +{ + printResults(configuration); +}); + +task ("build", ["default"], function() +{ + updateApplicationSize(); +}); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", projectName, "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", projectName, "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", projectName)); + OS.system(["press", "-f", FILE.join("Build", "Release", projectName), FILE.join("Build", "Deployment", projectName)]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", projectName)); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", projectName), FILE.join("Build", "Desktop", projectName, "ThemeKitchenSinkA3.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", projectName, "ThemeKitchenSinkA3.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, projectName)); + print("----------------------------"); +} + +function updateApplicationSize() +{ + print("Calculating application file sizes..."); + + var contents = FILE.read(FILE.join("Build", ENV["CONFIGURATION"], projectName, "Info.plist"), { charset:"UTF-8" }), + format = CFPropertyList.sniffedFormatOfString(contents), + plist = CFPropertyList.propertyListFromString(contents), + totalBytes = {executable:0, data:0, mhtml:0}; + + // Get the size of all framework executables and sprite data + var frameworksDir = "Frameworks"; + + if (ENV["CONFIGURATION"] === "Debug") + frameworksDir = FILE.join(frameworksDir, "Debug"); + + var frameworks = FILE.list(frameworksDir); + + frameworks.forEach(function(framework) + { + if (framework !== "Source") + addBundleFileSizes(FILE.join(frameworksDir, framework), totalBytes); + }); + + // Read in the default theme name, and attempt to get its size + var themeName = plist.valueForKey("CPDefaultTheme") || "Aristo2", + themePath = nil; + + if (themeName === "Aristo" || themeName === "Aristo2") + themePath = FILE.join(frameworksDir, "AppKit", "Resources", themeName + ".blend"); + else + themePath = FILE.join("Frameworks", "Resources", themeName + ".blend"); + + if (FILE.isDirectory(themePath)) + addBundleFileSizes(themePath, totalBytes); + + // Add sizes for the app + addBundleFileSizes(FILE.join("Build", ENV["CONFIGURATION"], projectName), totalBytes); + + print("Executables: " + totalBytes.executable + ", sprite data: " + totalBytes.data + ", total: " + (totalBytes.executable + totalBytes.data)); + + var dict = new CFMutableDictionary(); + + dict.setValueForKey("executable", totalBytes.executable); + dict.setValueForKey("data", totalBytes.data); + dict.setValueForKey("mhtml", totalBytes.mhtml); + + plist.setValueForKey("CPApplicationSize", dict); + + FILE.write(FILE.join("Build", ENV["CONFIGURATION"], projectName, "Info.plist"), CFPropertyList.stringFromPropertyList(plist, format), { charset:"UTF-8" }); +} + +function addBundleFileSizes(bundlePath, totalBytes) +{ + var bundleName = FILE.basename(bundlePath), + environment = bundleName === "Foundation" ? "Objj" : "Browser", + bundlePath = FILE.join(bundlePath, environment + ".environment"); + + if (FILE.isDirectory(bundlePath)) + { + var filename = bundleName + ".sj", + filePath = new FILE.Path(FILE.join(bundlePath, filename)); + + if (filePath.exists()) + totalBytes.executable += filePath.size(); + + filePath = new FILE.Path(FILE.join(bundlePath, "dataURLs.txt")); + + if (filePath.exists()) + totalBytes.data += filePath.size(); + + filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLData.txt")); + + if (filePath.exists()) + totalBytes.mhtml += filePath.size(); + + filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLPaths.txt")); + + if (filePath.exists()) + totalBytes.mhtml += filePath.size(); + } +} diff --git a/Tests/Manual/ThemeKitchenSinkA3/index-debug.html b/Tests/Manual/ThemeKitchenSinkA3/index-debug.html new file mode 100644 index 000000000..a36b1d3b9 --- /dev/null +++ b/Tests/Manual/ThemeKitchenSinkA3/index-debug.html @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + __project.name__ + + + + + + + + + + + + + + +
+
+
+ +
+
+ +
+ + diff --git a/Tests/Manual/ThemeKitchenSinkA3/index.html b/Tests/Manual/ThemeKitchenSinkA3/index.html new file mode 100644 index 000000000..ac42c98a7 --- /dev/null +++ b/Tests/Manual/ThemeKitchenSinkA3/index.html @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + __project.name__ + + + + + + + + + + + + +
+
+
+ +
+
+ +
+ + diff --git a/Tests/Manual/ThemeKitchenSinkA3/main.j b/Tests/Manual/ThemeKitchenSinkA3/main.j new file mode 100644 index 000000000..690218ac0 --- /dev/null +++ b/Tests/Manual/ThemeKitchenSinkA3/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * ThemeKitchenSink + * + * Created by Alexander Ljungberg on May 11, 2014. + * Copyright 2014, SlevenBits, Ltd. All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +}