├── .gitignore ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── koolui-test ├── build.gradle.kts ├── gradle.properties ├── src │ ├── androidMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ └── test │ │ │ └── MainActivity.kt │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ └── test │ │ │ ├── AlphaTestVG.kt │ │ │ ├── CanvasTestVG.kt │ │ │ ├── ControlsVG.kt │ │ │ ├── DialogTestVG.kt │ │ │ ├── FilesTestVG.kt │ │ │ ├── FrameVG.kt │ │ │ ├── GeolocationTestVG.kt │ │ │ ├── HorizontalVG.kt │ │ │ ├── HttpCallTestVG.kt │ │ │ ├── IconsTestVG.kt │ │ │ ├── MainVG.kt │ │ │ ├── NotificationTestVG.kt │ │ │ ├── OpenUriTestVG.kt │ │ │ ├── OriginalTestVG.kt │ │ │ ├── PagesVG.kt │ │ │ ├── PentagameTestVG.kt │ │ │ ├── SelectorVG.kt │ │ │ ├── SpaceTestVG.kt │ │ │ ├── SwapTestVG.kt │ │ │ ├── UrlImageTestVG.kt │ │ │ ├── VerticalTestVG.kt │ │ │ └── settings.kt │ ├── commonTest │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── kotlin │ │ │ └── crossplatform │ │ │ └── view │ │ │ └── console │ │ │ ├── TestVirtuals.kt │ │ │ └── VirtualGenerator.kt │ ├── iosMain │ │ └── kotlin │ │ │ ├── Main.kt │ │ │ ├── MainUIViewController.kt │ │ │ └── MyClosureSleeve.kt │ ├── javafxMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ └── test │ │ │ └── Main.kt │ ├── jsMain │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── lightningkite │ │ │ │ └── koolui │ │ │ │ └── test │ │ │ │ └── Main.kt │ │ └── web │ │ │ ├── index.html │ │ │ └── normalize.css │ └── main │ │ ├── AndroidManifest.xml │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_notifications.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml ├── versions.properties └── xcode │ ├── FromKotlin │ ├── FromKotlin.h │ └── Info.plist │ ├── FromKotlinTests │ ├── FromKotlinTests.swift │ └── Info.plist │ ├── koolui-test.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── josephivie.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── josephivie.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── koolui-test │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift │ ├── koolui-testTests │ ├── Info.plist │ └── koolui_testTests.swift │ └── koolui-testUITests │ ├── Info.plist │ └── koolui_testUITests.swift ├── koolui ├── build.gradle.kts ├── gradle.properties ├── src │ ├── androidMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── Location.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── android │ │ │ ├── Align.ext.kt │ │ │ ├── AndroidLayoutAdapter.kt │ │ │ ├── Animation.ext.kt │ │ │ ├── AnimationSet.kt │ │ │ ├── BackgroundResources.kt │ │ │ ├── CanvasView.kt │ │ │ ├── CircleDrawable.kt │ │ │ ├── ColorSet.ext.kt │ │ │ ├── DesiredSizes.kt │ │ │ ├── Drawable.ext.kt │ │ │ ├── IntrinsicDimensionLayouts.kt │ │ │ ├── LayoutToAndroidView.kt │ │ │ ├── ManualLayout.kt │ │ │ ├── NotificationHandlerService.kt │ │ │ ├── NumberInputType.kt │ │ │ ├── TextInputType.ext.kt │ │ │ ├── TextSize.ext.kt │ │ │ ├── View.touch.kt │ │ │ ├── ViewLifecycle.kt │ │ │ ├── ViewSwapManager.kt │ │ │ ├── access │ │ │ │ ├── AccessibleActivity.kt │ │ │ │ ├── ActivityAccess.ext.kt │ │ │ │ ├── ActivityAccess.kt │ │ │ │ └── IntentRequests.kt │ │ │ └── dialog │ │ │ │ ├── GenericDialogActivity.kt │ │ │ │ └── Launchers.kt │ │ │ ├── async │ │ │ └── UI.kt │ │ │ ├── canvas │ │ │ └── AndroidCanvas.kt │ │ │ ├── image │ │ │ └── Image.kt │ │ │ ├── implementationhelpers │ │ │ ├── AnyDesiredMargins.kt │ │ │ └── AnyLifecycles.kt │ │ │ ├── notification │ │ │ ├── MyFirebaseMessagingService.kt │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── view │ │ │ ├── HasActivityAccess.kt │ │ │ ├── LayoutAndroidWrapper.kt │ │ │ ├── MaterialAndroidViewFactory.kt │ │ │ ├── basic │ │ │ └── LayoutAndroidBasic.kt │ │ │ ├── graphics │ │ │ └── LayoutAndroidGraphics.kt │ │ │ ├── interactive │ │ │ └── LayoutAndroidInteractive.kt │ │ │ ├── layout │ │ │ └── LayoutAndroidLayout.kt │ │ │ └── navigation │ │ │ └── LayoutAndroidNavigation.kt │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.ext.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── Location.ext.kt │ │ │ ├── Location.kt │ │ │ ├── LocationResult.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── async │ │ │ ├── Lifecycle.scope.kt │ │ │ ├── suspend.observable.kt │ │ │ ├── suspendingTransform.kt │ │ │ └── ui.kt │ │ │ ├── canvas │ │ │ └── Canvas.kt │ │ │ ├── color │ │ │ ├── Color.kt │ │ │ ├── ColorSet.kt │ │ │ ├── HSVColor.kt │ │ │ └── Theme.kt │ │ │ ├── concepts │ │ │ ├── Animation.kt │ │ │ ├── Importance.kt │ │ │ ├── NumberInputType.kt │ │ │ ├── StackObservableProperty.Animation.ext.kt │ │ │ ├── TabItem.kt │ │ │ ├── TextInputType.kt │ │ │ └── TextSize.kt │ │ │ ├── geometry │ │ │ ├── Align.kt │ │ │ ├── AlignPair.kt │ │ │ ├── Direction.kt │ │ │ ├── LinearPlacement.kt │ │ │ └── Measurement.kt │ │ │ ├── image │ │ │ ├── Image.ext.kt │ │ │ ├── Image.kt │ │ │ ├── ImageScaleType.kt │ │ │ ├── ImageWithOptions.kt │ │ │ └── MaterialIcon.kt │ │ │ ├── implementationhelpers │ │ │ ├── Defaults.kt │ │ │ └── TreeObservableProperty.kt │ │ │ ├── layout │ │ │ ├── AlignDimensionLayout.kt │ │ │ ├── BaseDimensionLayout.kt │ │ │ ├── DimensionLayout.kt │ │ │ ├── DimensionLayout.withNeedsLayoutCallback.kt │ │ │ ├── DynamicAlignDimensionLayout.kt │ │ │ ├── ForceMarginsDimensionLayout.kt │ │ │ ├── ForceSizeDimensionLayout.kt │ │ │ ├── FrameDimensionLayout.kt │ │ │ ├── Layout.ext.kt │ │ │ ├── Layout.kt │ │ │ ├── LayoutViewFactory.kt │ │ │ ├── LeafDimensionLayout.kt │ │ │ ├── LeafDimensionLayouts.kt │ │ │ ├── LinearDimensionLayout.kt │ │ │ ├── MaxSizeDimensionLayout.kt │ │ │ ├── MinSizeDimensionLayout.kt │ │ │ ├── PassOnDimensionLayout.kt │ │ │ ├── ScrollDimensionLayout.kt │ │ │ ├── SwapDimensionLayout.kt │ │ │ ├── ViewAdapter.kt │ │ │ └── views │ │ │ │ ├── LayoutVFLayout.kt │ │ │ │ ├── LayoutVFRootAndDialogs.kt │ │ │ │ └── LayoutViewWrapper.kt │ │ │ ├── notification │ │ │ ├── Notification.kt │ │ │ ├── PushNotificationToken.kt │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── views │ │ │ ├── Themed.kt │ │ │ ├── Touch.kt │ │ │ ├── ViewFactory.kt │ │ │ ├── ViewGenerator.kt │ │ │ ├── basic │ │ │ ├── ViewFactoryBasic.ext.kt │ │ │ └── ViewFactoryBasic.kt │ │ │ ├── dialogs │ │ │ ├── ViewFactoryDialogs.ext.kt │ │ │ ├── ViewFactoryDialogs.kt │ │ │ └── ViewFactoryDialogsDefault.kt │ │ │ ├── graphics │ │ │ └── ViewFactoryGraphics.kt │ │ │ ├── interactive │ │ │ ├── KeyboardType.kt │ │ │ ├── ViewFactoryInteractive.ext.kt │ │ │ ├── ViewFactoryInteractive.kt │ │ │ └── ViewFactoryInteractiveDefault.kt │ │ │ ├── layout │ │ │ ├── ViewFactoryLayout.ext.kt │ │ │ └── ViewFactoryLayout.kt │ │ │ ├── navigation │ │ │ ├── ViewFactoryNavigation.ext.kt │ │ │ ├── ViewFactoryNavigation.kt │ │ │ └── ViewFactoryNavigationDefault.kt │ │ │ ├── root │ │ │ ├── ViewFactoryRoot.ext.kt │ │ │ └── ViewFactoryRoot.kt │ │ │ └── web │ │ │ └── ViewFactoryWeb.kt │ ├── commonTest │ │ └── kotlin │ │ │ └── LayoutsTest.kt │ ├── iosMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── Location.kt │ │ │ ├── NDate.ext.kt │ │ │ ├── NSData.ext.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── async │ │ │ └── UI.kt │ │ │ ├── image │ │ │ └── Image.kt │ │ │ ├── notification │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── views │ │ │ └── ios │ │ │ ├── Anchors.kt │ │ │ ├── CGRect.ext.kt │ │ │ ├── ClosureSleeve.kt │ │ │ ├── Color.ext.kt │ │ │ ├── IntrinsicLayoutDimensions.kt │ │ │ ├── LayoutRootView.kt │ │ │ ├── ListDataSource.kt │ │ │ ├── TextSize.ext.kt │ │ │ ├── UIControl.doneButton.kt │ │ │ ├── UIKitViewFactory.kt │ │ │ ├── UILabelWithVerticalAlignment.kt │ │ │ ├── UITextFieldDoneDelegate.kt │ │ │ ├── UIView.focus.kt │ │ │ ├── UIViewAdapter.kt │ │ │ └── makeUIPickerView.kt │ ├── javafxMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── Location.kt │ │ │ ├── MousePosition.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── async │ │ │ └── UI.kt │ │ │ ├── image │ │ │ ├── BufferedImageTranscoder.kt │ │ │ ├── Image.kt │ │ │ └── SVGRenderer.kt │ │ │ ├── implementationhelpers │ │ │ ├── AnyDesiredMargins.kt │ │ │ └── AnyLifecycles.kt │ │ │ ├── notification │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── views │ │ │ ├── EmptySelectionModel.kt │ │ │ ├── JavaFxLayoutWrapper.kt │ │ │ ├── LayoutJavaFxViewFactory.kt │ │ │ ├── MaterialJavaFxViewFactory.kt │ │ │ ├── Node.touch.kt │ │ │ ├── ObservableList.ext.kt │ │ │ ├── TextSize.javafx.kt │ │ │ ├── basic │ │ │ └── LayoutJavaFxBasic.kt │ │ │ ├── graphics │ │ │ ├── JavaFXCanvas.kt │ │ │ └── LayoutJavaFxGraphics.kt │ │ │ ├── interactive │ │ │ └── LayoutJavaFxInteractive.kt │ │ │ ├── javafx.kt │ │ │ └── layout │ │ │ └── LayoutJavaFxLayout.kt │ ├── jsMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── Align.ext.kt │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── JSExtensions.kt │ │ │ ├── Lifecycle.kt │ │ │ ├── Location.kt │ │ │ ├── UIBuildingTools.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── async │ │ │ └── ui.kt │ │ │ ├── canvas │ │ │ └── HtmlCanvas.kt │ │ │ ├── image │ │ │ └── Image.kt │ │ │ ├── implementationhelpers │ │ │ ├── AnyDesiredMargins.kt │ │ │ └── AnyLifecycles.kt │ │ │ ├── notification │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── views │ │ │ ├── HTMLElement.touch.kt │ │ │ ├── HtmlViewFactory.kt │ │ │ ├── Importance.toCssClass.kt │ │ │ ├── LayoutHtmlViewFactory.kt │ │ │ ├── basic │ │ │ └── LayoutHtmlBasic.kt │ │ │ ├── graphics │ │ │ └── LayoutHtmlGraphics.kt │ │ │ ├── interactive │ │ │ └── LayoutHtmlInteractive.kt │ │ │ └── layout │ │ │ └── LayoutHtmlLayout.kt │ ├── jvmVirtualMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── Location.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── async │ │ │ └── UI.kt │ │ │ ├── image │ │ │ └── Image.kt │ │ │ ├── notification │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── views │ │ │ └── virtual │ │ │ ├── AbstractViews.kt │ │ │ └── VirtualViewFactory.kt │ ├── lanternaMain │ │ └── kotlin │ │ │ └── com │ │ │ └── lightningkite │ │ │ └── koolui │ │ │ ├── ApplicationAccess.kt │ │ │ ├── ExternalAccess.kt │ │ │ ├── Location.kt │ │ │ ├── UIPlatform.kt │ │ │ ├── async │ │ │ └── UI.kt │ │ │ ├── image │ │ │ └── Image.kt │ │ │ ├── notification │ │ │ └── PushNotifications.kt │ │ │ ├── preferences │ │ │ └── Preferences.kt │ │ │ ├── resources │ │ │ └── Resources.kt │ │ │ └── views │ │ │ └── LanternaViewFactory.kt │ └── main │ │ ├── AndroidManifest.xml │ │ └── res │ │ ├── drawable │ │ └── ic_arrow_back.xml │ │ ├── layout │ │ ├── horizontal_recycler_view_scrollbar.xml │ │ └── vertical_recycler_view_scrollbar.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml └── versions.properties ├── settings.gradle.kts └── versions.properties /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | google() 5 | } 6 | 7 | dependencies { 8 | classpath("com.android.tools.build:gradle:3.4.0") 9 | } 10 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | websiteUrl=https://github.com/lightningkite/koolui 3 | vcsUrl=https://github.com/lightningkite/koolui.git 4 | issuesUrl=https://github.com/lightningkite/koolui/issues 5 | bintrayOrganization=lightningkite 6 | bintrayRepository=com.lightningkite.krosslin 7 | bintrayLicense=MIT 8 | bintrayPublish=false 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /koolui-test/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | android.useAndroidX=true 3 | android.enableJetifier=true -------------------------------------------------------------------------------- /koolui-test/src/androidMain/kotlin/com/lightningkite/koolui/test/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import com.lightningkite.koolui.ApplicationAccess 6 | import com.lightningkite.koolui.android.* 7 | import com.lightningkite.koolui.android.access.AccessibleActivity 8 | import com.lightningkite.koolui.color.ColorSet 9 | import com.lightningkite.koolui.views.ViewFactory 10 | import com.lightningkite.koolui.layout.Layout 11 | import com.lightningkite.koolui.view.MaterialAndroidViewFactory 12 | 13 | class MainActivity : AccessibleActivity() { 14 | 15 | companion object { 16 | val main = MainVG>() 17 | } 18 | 19 | class Factory( 20 | val activity: AccessibleActivity, 21 | colorSet: ColorSet = theme.main 22 | ) : MyViewFactory>, ViewFactory> by MaterialAndroidViewFactory(activity, myTheme, colorSet) 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | 27 | ApplicationAccess.init(this, R.drawable.ic_notifications) 28 | 29 | // val rootLayout = Factory(this).contentRoot(OriginalTestVG()) 30 | val factory = Factory(this) 31 | val rootLayout = factory.contentRoot(main.generate(factory)) 32 | setContentView(LayoutToAndroidView(this).apply { layout = rootLayout }) 33 | println("Laid out") 34 | // rootLayout.layout(Rectangle(0f,0f,400f,400f)) 35 | // setContentView(rootLayout.viewAdapter.viewAsBase) 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/AlphaTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.reacktive.property.ConstantObservableProperty 4 | import com.lightningkite.reacktive.property.StandardObservableProperty 5 | import com.lightningkite.koolui.views.* 6 | import com.lightningkite.koolui.views.basic.* 7 | import com.lightningkite.koolui.views.interactive.* 8 | import com.lightningkite.koolui.views.layout.* 9 | import com.lightningkite.koolui.concepts.TextSize 10 | 11 | class AlphaTestVG() : MyViewGenerator { 12 | override val title: String = "Alpha" 13 | val alpha = StandardObservableProperty(0f) 14 | 15 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 16 | 17 | vertical { 18 | -button(label = ConstantObservableProperty("Change Alpha"), onClick = { 19 | alpha.value = if (alpha.value < .5f) 1f else 0f 20 | }) 21 | -text(text = "Header", size = TextSize.Header).alpha(alpha) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/CanvasTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.canvas.Canvas 5 | import com.lightningkite.koolui.color.Color 6 | import com.lightningkite.koolui.geometry.Align 7 | import com.lightningkite.reacktive.property.ConstantObservableProperty 8 | import com.lightningkite.reacktive.property.StandardObservableProperty 9 | import com.lightningkite.reacktive.property.lifecycle.listen 10 | import com.lightningkite.reacktive.property.transform 11 | import com.lightningkite.recktangle.Point 12 | 13 | class CanvasTestVG : MyViewGenerator { 14 | val frameNumber = StandardObservableProperty(0) 15 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 16 | println("Making canvas...") 17 | canvas(frameNumber.transform { 18 | fun Canvas.() { 19 | val w = size.x / 100 20 | val h = size.y / 100 21 | 22 | val xPos = (it % 120)-20 23 | beginPath() 24 | move((xPos + 0) * w, 0 * h) 25 | line((xPos + 20) * w, 20 * h) 26 | move((xPos + 20) * w, 0 * h) 27 | line((xPos + 0) * w, 20 * h) 28 | stroke(Color.green, 1f) 29 | 30 | beginPath() 31 | move(40 * w, 40 * h) 32 | line(60 * w, 40 * h) 33 | line(60 * w, 60 * h) 34 | line(40 * w, 60 * h) 35 | close() 36 | fill(Color.red) 37 | 38 | text("Frame: ${it}", 50 * w, Align.Center, 90 * h, h * 5, Color.white) 39 | } 40 | }).background(Color.gray).apply { 41 | lifecycle.listen(ApplicationAccess.onAnimationFrame) { 42 | frameNumber.value++ 43 | } 44 | } 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/DialogTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.views.basic.* 4 | import com.lightningkite.koolui.views.interactive.* 5 | import com.lightningkite.koolui.views.layout.* 6 | 7 | class DialogTestVG() : MyViewGenerator { 8 | override val title: String = "Dialogs" 9 | 10 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 11 | scrollVertical(vertical { 12 | -button(label = "Launch Dialog") { 13 | launchDialog { dismiss -> 14 | card(vertical { 15 | -text(text = "Hello world!") 16 | -button(label = "I'm good now") { 17 | println("I was clicked!") 18 | dismiss() 19 | } 20 | }) 21 | } 22 | } 23 | -button(label = "Launch Dialog B") { 24 | launchDialog { dismiss -> 25 | text(text = "HELLO.") 26 | } 27 | } 28 | -button(label = "Launch Selector") { 29 | launchSelector("Select something!", listOf( 30 | "First" to { println("First") }, 31 | "Second" to { println("Second") }, 32 | "Third" to { println("Third") } 33 | )) 34 | } 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/FilesTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.kommon.string.MediaTypeWithDescription 4 | import com.lightningkite.koolui.ExternalAccess 5 | import com.lightningkite.koolui.views.basic.* 6 | import com.lightningkite.koolui.views.interactive.* 7 | import com.lightningkite.koolui.views.layout.* 8 | import com.lightningkite.reacktive.property.StandardObservableProperty 9 | import kotlinx.io.core.toByteArray 10 | 11 | class FilesTestVG() : MyViewGenerator { 12 | override val title: String = "Files Test" 13 | 14 | val data = StandardObservableProperty("") 15 | 16 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 17 | vertical { 18 | -text(text = "File contents:") 19 | -textArea(data) 20 | -button("Load") { 21 | ExternalAccess.loadFromChosenFile { 22 | data.value = it?.let { kotlinx.io.core.String(it) } ?: "" 23 | } 24 | } 25 | -button("Save"){ 26 | ExternalAccess.saveToChosenFile("Test", MediaTypeWithDescription.txt, data.value.toByteArray()) 27 | } 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/FrameVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.views.basic.* 4 | import com.lightningkite.koolui.views.interactive.* 5 | import com.lightningkite.koolui.views.layout.* 6 | import com.lightningkite.koolui.concepts.TextSize 7 | import com.lightningkite.koolui.geometry.AlignPair 8 | 9 | class FrameVG() : MyViewGenerator { 10 | override val title: String = "Frame" 11 | 12 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 13 | align( 14 | AlignPair.TopLeft to text(text = "Top Left", size = TextSize.Body), 15 | AlignPair.CenterLeft to text(text = "Center Left", size = TextSize.Body), 16 | AlignPair.BottomLeft to text(text = "Bottom Left", size = TextSize.Body), 17 | AlignPair.TopCenter to text(text = "Top Center", size = TextSize.Body), 18 | AlignPair.CenterCenter to text(text = "Center Center", size = TextSize.Body), 19 | AlignPair.FillFill to text(text = "Fill", size = TextSize.Header, align = AlignPair.TopLeft), 20 | AlignPair.BottomCenter to text(text = "Bottom Center", size = TextSize.Body), 21 | AlignPair.TopRight to text(text = "Top Right", size = TextSize.Body), 22 | AlignPair.CenterRight to text(text = "Center Right", size = TextSize.Body), 23 | AlignPair.BottomRight to text(text = "Bottom Right", size = TextSize.Body) 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/HorizontalVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.views.basic.* 4 | import com.lightningkite.koolui.views.interactive.* 5 | import com.lightningkite.koolui.views.layout.* 6 | import com.lightningkite.koolui.geometry.AlignPair 7 | import com.lightningkite.koolui.geometry.LinearPlacement 8 | 9 | class HorizontalVG() : MyViewGenerator { 10 | override val title: String = "Horizontal" 11 | 12 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 13 | horizontal( 14 | LinearPlacement.fillStart to text(text = "left", align = AlignPair.CenterCenter), 15 | LinearPlacement.fillFill to text(text = "fill", align = AlignPair.CenterCenter), 16 | LinearPlacement.fillEnd to text(text = "right", align = AlignPair.CenterCenter) 17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/IconsTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.recktangle.Point 4 | import com.lightningkite.reacktive.list.WrapperObservableList 5 | import com.lightningkite.reacktive.property.transform 6 | import com.lightningkite.koolui.views.layout.* 7 | import com.lightningkite.koolui.views.navigation.* 8 | import com.lightningkite.koolui.color.Color 9 | import com.lightningkite.koolui.image.MaterialIcon 10 | import com.lightningkite.koolui.image.withOptions 11 | import com.lightningkite.koolui.image.color 12 | 13 | class IconsTestVG() : MyViewGenerator { 14 | override val title: String = "IconsTest" 15 | 16 | val tests = WrapperObservableList(MaterialIcon.values().toMutableList()) 17 | 18 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 19 | list(data = tests, makeView = { itemObs -> 20 | horizontal { 21 | -image(itemObs.transform { it.color(Color.blue).withOptions(Point(48f, 48f)) }) 22 | +text(itemObs.transform { it.name }) 23 | } 24 | 25 | }).margin(8f) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/MainVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.kommon.collection.* 4 | import com.lightningkite.reacktive.list.observableListOf 5 | import com.lightningkite.reacktive.list.WrapperObservableList 6 | import com.lightningkite.koolui.views.ViewFactory 7 | import com.lightningkite.koolui.views.ViewGenerator 8 | 9 | class MainVG() : MyViewGenerator { 10 | override val title: String = "KotlinX UI Test" 11 | 12 | val stack = WrapperObservableList>() 13 | 14 | init { 15 | //Startup 16 | stack.push(SelectorVG(stack)) 17 | } 18 | 19 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 20 | window( 21 | dependency = dependency, 22 | stack = stack, 23 | tabs = listOf() 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/NotificationTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.lokalize.* 4 | import com.lightningkite.lokalize.time.* 5 | import com.lightningkite.reacktive.property.StandardObservableProperty 6 | import com.lightningkite.koolui.ApplicationAccess 7 | import com.lightningkite.koolui.views.basic.* 8 | import com.lightningkite.koolui.views.interactive.* 9 | import com.lightningkite.koolui.views.layout.* 10 | import com.lightningkite.koolui.notification.Notification 11 | import kotlin.native.concurrent.ThreadLocal 12 | 13 | class NotificationTestVG() : MyViewGenerator { 14 | override val title: String = "Notifications" 15 | 16 | @ThreadLocal 17 | companion object { 18 | val lastActionObs = StandardObservableProperty("No actions taken yet.") 19 | 20 | init { 21 | ApplicationAccess.onNotificationAction += { 22 | lastActionObs.value = "Action '$it' taken at ${Locale.default.renderTimeStamp(TimeStamp.now())}" 23 | false 24 | } 25 | } 26 | } 27 | 28 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 29 | vertical { 30 | -text(text = "Press below to fire a notification test notification off.") 31 | -button(label = "Get Notified!") { 32 | ApplicationAccess.showNotification(Notification( 33 | title = "Test Notification", 34 | content = "This is a test notification.", 35 | priority = .5f, 36 | action = "view", 37 | actions = mapOf( 38 | "Silence" to "silence", 39 | "Yell" to "yell" 40 | ) 41 | )) 42 | } 43 | -text(text = lastActionObs) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/OpenUriTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | import com.lightningkite.koolui.ExternalAccess 5 | import com.lightningkite.koolui.views.basic.* 6 | import com.lightningkite.koolui.views.interactive.* 7 | import com.lightningkite.koolui.views.layout.* 8 | 9 | class OpenUriTestVG() : MyViewGenerator { 10 | override val title: String = "Open URI Test" 11 | 12 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 13 | vertical { 14 | -text(text = "Let's test opening Google!") 15 | -button("Google") { 16 | ExternalAccess.openUri(Uri("https://google.com")) 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/PagesVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.reacktive.property.StandardObservableProperty 4 | import com.lightningkite.koolui.views.basic.* 5 | import com.lightningkite.koolui.views.navigation.* 6 | import com.lightningkite.koolui.color.Color 7 | import com.lightningkite.koolui.concepts.TextSize 8 | import com.lightningkite.koolui.geometry.AlignPair 9 | 10 | class PagesVG() : MyViewGenerator { 11 | override val title: String = "Pages" 12 | 13 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 14 | pagesEmbedded( 15 | dependency, 16 | StandardObservableProperty(0), 17 | { 18 | text(text = "First page", size = TextSize.Header, align = AlignPair.CenterCenter).background(Color.black) 19 | }, 20 | { 21 | text(text = "Second page", size = TextSize.Header, align = AlignPair.CenterCenter).background(Color.black) 22 | }, 23 | { 24 | text(text = "Third page", size = TextSize.Header, align = AlignPair.CenterCenter).background(Color.black) 25 | }, 26 | { 27 | text(text = "Last page", size = TextSize.Header, align = AlignPair.CenterCenter).background(Color.black) 28 | } 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/SpaceTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.views.ViewFactory 4 | import com.lightningkite.koolui.views.ViewGenerator 5 | import com.lightningkite.recktangle.Point 6 | 7 | class SpaceTestVG() : MyViewGenerator { 8 | override val title: String = "Space" 9 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 10 | space(Point(32f, 32f)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/UrlImageTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.views.basic.* 4 | import com.lightningkite.koolui.views.layout.* 5 | import com.lightningkite.koolui.views.graphics.* 6 | import com.lightningkite.koolui.image.Image 7 | import com.lightningkite.koolui.image.withOptions 8 | import io.ktor.client.HttpClient 9 | import io.ktor.client.call.call 10 | import io.ktor.client.response.readBytes 11 | 12 | class UrlImageTestVG() : MyViewGenerator { 13 | override val title: String = "URL imageWithOptions Test" 14 | 15 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 16 | vertical { 17 | -text(text = "An imageWithOptions will be loaded here from 'https://picsum.photos/200/300'.") 18 | -loadingImage { Image.fromByteArray(HttpClient().call("https://picsum.photos/200/300").response.readBytes()).withOptions() } 19 | } 20 | } 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/VerticalTestVG.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.views.basic.* 4 | import com.lightningkite.koolui.color.Color 5 | import com.lightningkite.koolui.geometry.AlignPair 6 | import com.lightningkite.koolui.geometry.LinearPlacement 7 | 8 | class VerticalTestVG() : MyViewGenerator { 9 | override val title: String = "Vertical" 10 | 11 | override fun generate(dependency: MyViewFactory): VIEW = with(dependency) { 12 | vertical( 13 | LinearPlacement.wrapStart to text(text = "left", align = AlignPair.CenterCenter).background(Color.red), 14 | LinearPlacement.wrapFill to text(text = "fill", align = AlignPair.CenterCenter).background(Color.red), 15 | LinearPlacement.wrapEnd to text(text = "right", align = AlignPair.CenterCenter).background(Color.red), 16 | LinearPlacement.wrapStart to text(text = "left", align = AlignPair.CenterCenter).background(Color.red), 17 | LinearPlacement.wrapFill to text(text = "fill", align = AlignPair.CenterCenter).background(Color.red), 18 | LinearPlacement.wrapEnd to text(text = "right", align = AlignPair.CenterCenter).background(Color.red), 19 | LinearPlacement.wrapStart to text(text = "left", align = AlignPair.CenterCenter).background(Color.red), 20 | LinearPlacement.wrapFill to text(text = "fill", align = AlignPair.CenterCenter).background(Color.red), 21 | LinearPlacement.wrapEnd to text(text = "right", align = AlignPair.CenterCenter).background(Color.red) 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /koolui-test/src/commonMain/kotlin/com/lightningkite/koolui/test/settings.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.color.Theme 4 | import com.lightningkite.koolui.views.ViewFactory 5 | import com.lightningkite.koolui.views.ViewGenerator 6 | 7 | val myTheme = Theme.dark() 8 | interface MyViewFactory : ViewFactory 9 | typealias MyViewGenerator = ViewGenerator, VIEW> 10 | -------------------------------------------------------------------------------- /koolui-test/src/commonTest/kotlin/com/lightningkite/kotlin/crossplatform/view/console/TestVirtuals.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.kotlin.crossplatform.view.console 2 | 3 | class TestVirtuals { 4 | 5 | // class TestContent( 6 | // var image: Image = Image.Bundled(""), 7 | // var title:String = "Title", 8 | // var body:String = "This is some body" 9 | // ) 10 | // 11 | // fun ViewFactory.testView(content:ObservableProperty) = vertical( 12 | // 0f, 13 | // Gravity.TopCenter to header(content.transform { it.title }), 14 | // Gravity.TopFill to image(Point(200f, 200f), content.transform { it.image }), 15 | // Gravity.BottomFill to horizontal( 16 | // 0f, 17 | // Gravity.FillRight to button("Next"){ 18 | // println("Hello next world!") 19 | // }, 20 | // Gravity.FillLeft to button("Previous"){ 21 | // println("Hello previous world!") 22 | // } 23 | // ), 24 | // Gravity.Fill to scrollVertical(body(content.transform { it.body })) 25 | // ) 26 | // 27 | // @Test 28 | // fun test(){ 29 | // val content = StandardObservableProperty(TestContent()) 30 | // val virtualView = VirtualViewFactory.testView(content) as VerticalView 31 | // virtualView.recursiveViews().forEach { 32 | // println(it) 33 | // } 34 | // } 35 | } -------------------------------------------------------------------------------- /koolui-test/src/commonTest/kotlin/com/lightningkite/kotlin/crossplatform/view/console/VirtualGenerator.kt: -------------------------------------------------------------------------------- 1 | //package com.lightningkite.kotlin.crossplatform.view.console 2 | // 3 | //import lk.kotlin.crossplatform.view.ViewFactory 4 | //import org.junit.Test 5 | //import kotlin.reflect.full.declaredFunctions 6 | // 7 | //class VirtualGenerator{ 8 | // @Test fun generateVirtual(){ 9 | // //Let us reflect upon the view factory. 10 | // 11 | // for(function in ViewFactory::class.declaredFunctions){ 12 | // val className = function.name.capitalize() + "View" 13 | // val typeParameters = function.typeParameters.let{ 14 | // if(it.isEmpty()) "" 15 | // else it.joinToString(", ", "<", ">"){ it.name } 16 | // } 17 | // val classMembers = function.parameters.asSequence().drop(1).joinToString { "var ${it.name}: ${it.type}" } 18 | // val functionParameters = function.parameters.asSequence().drop(1).joinToString { "${it.name}: ${it.type}" } 19 | // val functionParametersPassing = function.parameters.asSequence().drop(1).joinToString { it.name ?: "null" } 20 | // val newClass = """class $className$typeParameters($classMembers): View()""" 21 | // val newFunction = """override fun $typeParameters ${function.name}($functionParameters):$className$typeParameters = $className($functionParametersPassing)""" 22 | // println(newClass.replace("VIEW", "View")) 23 | // println(newFunction.replace("VIEW", "View")) 24 | // } 25 | // } 26 | //} -------------------------------------------------------------------------------- /koolui-test/src/iosMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import kotlinx.cinterop.* 4 | import platform.UIKit.UIViewController 5 | import platform.darwin.NSObject 6 | import platform.objc.* 7 | import kotlin.native.concurrent.ThreadLocal 8 | 9 | fun makeMainVC(): UIViewController = MainUIViewController(null, null) 10 | -------------------------------------------------------------------------------- /koolui-test/src/iosMain/kotlin/MyClosureSleeve.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import kotlinx.cinterop.ObjCAction 4 | import platform.darwin.NSObject 5 | 6 | class MyClosureSleeve(val closure: () -> Unit) : NSObject() { 7 | @ObjCAction 8 | fun runContainedClosure() = closure() 9 | } 10 | -------------------------------------------------------------------------------- /koolui-test/src/jsMain/kotlin/com/lightningkite/koolui/test/Main.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.test 2 | 3 | import com.lightningkite.koolui.color.* 4 | import com.lightningkite.koolui.layout.Layout 5 | import com.lightningkite.koolui.views.* 6 | import com.lightningkite.koolui.views.root.contentRoot 7 | import org.w3c.dom.HTMLElement 8 | import kotlin.browser.document 9 | import kotlin.browser.window 10 | 11 | 12 | class LayoutFactory( 13 | val underlying: LayoutHtmlViewFactory = LayoutHtmlViewFactory(theme, theme.main) 14 | ) : MyViewFactory>, ViewFactory> by underlying 15 | 16 | class Factory( 17 | colorSet: ColorSet = theme.main 18 | ) : MyViewFactory, ViewFactory by HtmlViewFactory(myTheme, colorSet) 19 | 20 | fun main(args: Array) { 21 | window.onload = { 22 | document.body!!.appendChild( 23 | // LayoutFactory().run { 24 | // underlying.nativeViewAdapter(contentRoot(MainVG())) 25 | // } 26 | Factory().contentRoot(MainVG()) 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /koolui-test/src/jsMain/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test project 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /koolui-test/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /koolui-test/src/main/res/drawable/ic_notifications.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /koolui-test/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /koolui-test/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android Test 3 | 4 | -------------------------------------------------------------------------------- /koolui-test/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /koolui-test/versions.properties: -------------------------------------------------------------------------------- 1 | kommon=0.1.8 2 | koolui=0.1.6 3 | lokalize=0.1.6 4 | mirror=0.1.9 5 | mirror-form=0.1.9 6 | recktangle=0.1.6 7 | reacktive=0.1.6 8 | kabinet=0.0.1 9 | konvenience=0.0.8 10 | kotlin=1.3.50-eap-5 11 | kotlinx_serialization=0.11.1 12 | kotlinx_coroutines=1.2.2 13 | kotlinx_io=0.1.13-1.3.50-eap-5 14 | ktor=1.2.2 15 | # mirror-archive-api = 0.0.3 16 | # mirror-archive-nitrite = 0.0.3 17 | # mirror-archive-postgres = 0.0.3 18 | # mirror-archive-influxdb = 0.0.3 19 | # mirror-archive-redis = 0.0.3 20 | # mirror-form = 0.0.3 21 | -------------------------------------------------------------------------------- /koolui-test/xcode/FromKotlin/FromKotlin.h: -------------------------------------------------------------------------------- 1 | // 2 | // FromKotlin.h 3 | // FromKotlin 4 | // 5 | // Created by Joseph Ivie on 7/4/19. 6 | // Copyright © 2019 Joseph Ivie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for FromKotlin. 12 | FOUNDATION_EXPORT double FromKotlinVersionNumber; 13 | 14 | //! Project version string for FromKotlin. 15 | FOUNDATION_EXPORT const unsigned char FromKotlinVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /koolui-test/xcode/FromKotlin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /koolui-test/xcode/FromKotlinTests/FromKotlinTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FromKotlinTests.swift 3 | // FromKotlinTests 4 | // 5 | // Created by Joseph Ivie on 4/8/19. 6 | // Copyright © 2019 Joseph Ivie. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import FromKotlin 11 | 12 | class FromKotlinTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /koolui-test/xcode/FromKotlinTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test.xcodeproj/project.xcworkspace/xcuserdata/josephivie.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningkite/koolui/ee0c5e25a686d33db51bd6797a0e7e2d8ac480d0/koolui-test/xcode/koolui-test.xcodeproj/project.xcworkspace/xcuserdata/josephivie.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test.xcodeproj/xcuserdata/josephivie.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FromKotlin.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | koolui-test.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | koolui_test.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 2 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSLocationWhenInUseUsageDescription 6 | To automatically pull your location for GPS input, you must give us access to the GPS. 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-test/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // koolui-test 4 | // 5 | // Created by Joseph Ivie on 4/8/19. 6 | // Copyright © 2019 Joseph Ivie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FromKotlin 11 | 12 | class ViewController: UIViewController { 13 | 14 | @IBOutlet weak var helloWorldLabel: UILabel! 15 | 16 | var sleeve: Any? = nil 17 | weak var button: UIButton? = nil 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | helloWorldLabel.text = UIPlatform.ios.name 23 | } 24 | 25 | override var preferredStatusBarStyle: UIStatusBarStyle { 26 | return UIStatusBarStyle.lightContent 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-testTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-testTests/koolui_testTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // koolui_testTests.swift 3 | // koolui-testTests 4 | // 5 | // Created by Joseph Ivie on 4/8/19. 6 | // Copyright © 2019 Joseph Ivie. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import koolui_test 11 | 12 | class koolui_testTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-testUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /koolui-test/xcode/koolui-testUITests/koolui_testUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // koolui_testUITests.swift 3 | // koolui-testUITests 4 | // 5 | // Created by Joseph Ivie on 4/8/19. 6 | // Copyright © 2019 Joseph Ivie. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class koolui_testUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /koolui/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | android.useAndroidX=true 3 | android.enableJetifier=true -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | actual val UIPlatform.Companion.current: UIPlatform get() = UIPlatform.Android -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/AndroidLayoutAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import com.lightningkite.koolui.layout.Layout 7 | import com.lightningkite.koolui.layout.ViewAdapter 8 | import com.lightningkite.recktangle.Rectangle 9 | import kotlin.math.ceil 10 | 11 | class AndroidLayoutAdapter(override val view: SPECIFIC, val addView: Boolean) : ViewAdapter { 12 | override val viewAsBase: View = view 13 | val layoutParams = ManualLayout.LayoutParams(0, 0, 0, 0) 14 | 15 | init { 16 | view.layoutParams = layoutParams 17 | } 18 | 19 | override fun updatePlacementX(start: Float, end: Float) { 20 | this.viewAsBase.layoutParams.width = (end - start).times(dip).toInt() 21 | (this.viewAsBase.layoutParams as? ManualLayout.LayoutParams)?.let { 22 | it.left = ceil(dip * start).toInt() 23 | it.right = ceil(dip * end).toInt() 24 | viewAsBase.requestLayout() 25 | } 26 | } 27 | 28 | override fun updatePlacementY(start: Float, end: Float) { 29 | this.viewAsBase.layoutParams.height = (end - start).times(dip).toInt() 30 | (this.viewAsBase.layoutParams as? ManualLayout.LayoutParams)?.let { 31 | it.top = ceil(dip * start).toInt() 32 | it.bottom = ceil(dip * end).toInt() 33 | viewAsBase.requestLayout() 34 | } 35 | } 36 | 37 | override fun onAddChild(layout: Layout<*, View>) { 38 | if (addView && view is ViewGroup) { 39 | view.addView(layout.viewAdapter.viewAsBase) 40 | } 41 | } 42 | 43 | override fun onRemoveChild(layout: Layout<*, View>) { 44 | if (addView && view is ViewGroup) { 45 | view.removeView(layout.viewAdapter.viewAsBase) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/Animation.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import com.lightningkite.koolui.concepts.Animation 4 | 5 | 6 | fun Animation.android(): AnimationSet = when (this) { 7 | Animation.None -> AnimationSet({ animate().alpha(1f).setDuration(1) }, { animate().alpha(0f).setDuration(1) }) 8 | Animation.Push -> AnimationSet.slidePush 9 | Animation.Pop -> AnimationSet.slidePop 10 | Animation.MoveUp -> AnimationSet.slideDown 11 | Animation.MoveDown -> AnimationSet.slideUp 12 | Animation.Fade -> AnimationSet.fade 13 | Animation.Flip -> AnimationSet.flipVertical 14 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/BackgroundResources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.os.Build 4 | import android.util.TypedValue 5 | import android.view.View 6 | 7 | /** 8 | * Returns the default, clear background for selectable items. Reacts when touched. 9 | */ 10 | val View.selectableItemBackgroundResource: Int 11 | get() { 12 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 13 | // If we're running on Honeycomb or newer, then we can use the Theme's 14 | // selectableItemBackground to ensure that the View has a pressed state 15 | val outValue = TypedValue() 16 | context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) 17 | return outValue.resourceId 18 | } 19 | return 0 20 | } 21 | 22 | /** 23 | * Returns the default, clear background for selectable items without a border. Reacts when touched. 24 | */ 25 | val View.selectableItemBackgroundBorderlessResource: Int 26 | get() { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 28 | // If we're running on Honeycomb or newer, then we can use the Theme's 29 | // selectableItemBackground to ensure that the View has a pressed state 30 | val outValue = TypedValue() 31 | context.theme.resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, outValue, true) 32 | return outValue.resourceId 33 | } 34 | return 0 35 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/CircleDrawable.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.ColorFilter 5 | import android.graphics.Paint 6 | import android.graphics.PixelFormat 7 | import android.graphics.drawable.Drawable 8 | 9 | class CircleDrawable(color: Int) : Drawable() { 10 | 11 | val paint = Paint().apply { 12 | this.style = Paint.Style.FILL 13 | this.color = color 14 | } 15 | 16 | override fun draw(canvas: Canvas) { 17 | val minDimen = Math.min(bounds.width(), bounds.height()) 18 | canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), minDimen / 2f, paint) 19 | } 20 | 21 | override fun setAlpha(alpha: Int) {} 22 | 23 | override fun getOpacity(): Int = PixelFormat.TRANSPARENT 24 | 25 | override fun setColorFilter(colorFilter: ColorFilter?) { 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/ColorSet.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.content.res.ColorStateList 4 | import com.lightningkite.koolui.color.Color 5 | import com.lightningkite.koolui.color.ColorSet 6 | 7 | fun ColorSet.androidForeground() = ColorStateList( 8 | arrayOf( 9 | intArrayOf(-android.R.attr.state_enabled), 10 | intArrayOf(android.R.attr.state_pressed), 11 | intArrayOf(android.R.attr.state_selected), 12 | intArrayOf(android.R.attr.state_hovered), 13 | intArrayOf() 14 | ), 15 | intArrayOf( 16 | foregroundDisabled.toInt(), 17 | foregroundHighlighted.toInt(), 18 | foregroundHighlighted.toInt(), 19 | foregroundHighlighted.toInt(), 20 | foreground.toInt() 21 | ) 22 | ) 23 | 24 | fun ColorSet.androidForegroundOverlay() = ColorStateList( 25 | arrayOf( 26 | intArrayOf(-android.R.attr.state_enabled), 27 | intArrayOf(android.R.attr.state_pressed), 28 | intArrayOf(android.R.attr.state_selected), 29 | intArrayOf(android.R.attr.state_hovered), 30 | intArrayOf() 31 | ), 32 | intArrayOf( 33 | foregroundDisabled.toInt(), 34 | foregroundHighlighted.toInt(), 35 | foregroundHighlighted.toInt(), 36 | foregroundHighlighted.toInt(), 37 | Color.transparent.toInt() 38 | ) 39 | ) 40 | 41 | fun ColorSet.androidBackground() = ColorStateList( 42 | arrayOf( 43 | intArrayOf(-android.R.attr.state_enabled), 44 | intArrayOf(android.R.attr.state_pressed), 45 | intArrayOf(android.R.attr.state_selected), 46 | intArrayOf(android.R.attr.state_hovered), 47 | intArrayOf() 48 | ), 49 | intArrayOf( 50 | backgroundDisabled.toInt(), 51 | backgroundHighlighted.toInt(), 52 | backgroundHighlighted.toInt(), 53 | backgroundHighlighted.toInt(), 54 | background.toInt() 55 | ) 56 | ) -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/DesiredSizes.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import androidx.cardview.widget.CardView 4 | import androidx.recyclerview.widget.RecyclerView 5 | import android.view.View 6 | import android.widget.FrameLayout 7 | import android.widget.LinearLayout 8 | import java.util.* 9 | 10 | private val ViewDesiredHeight = WeakHashMap() 11 | var View.desiredHeight: Int? 12 | get() = ViewDesiredHeight[this] 13 | set(value) { 14 | ViewDesiredHeight[this] = value 15 | } 16 | private val ViewDesiredWidth = WeakHashMap() 17 | var View.desiredWidth: Int? 18 | get() = ViewDesiredWidth[this] 19 | set(value) { 20 | ViewDesiredWidth[this] = value 21 | } 22 | 23 | var dip = 1f 24 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/Drawable.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.graphics.drawable.Drawable 4 | import com.lightningkite.koolui.image.ImageWithOptions 5 | 6 | private val NumberRegex = Regex("\\d+\\.?\\d*") 7 | fun ImageWithOptions.android(): Drawable = this.image.drawable 8 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/IntrinsicDimensionLayouts.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.view.View 4 | import com.lightningkite.koolui.async.UI 5 | import com.lightningkite.koolui.geometry.Measurement 6 | import com.lightningkite.koolui.layout.LeafDimensionLayouts 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.GlobalScope 9 | import kotlinx.coroutines.delay 10 | import kotlinx.coroutines.launch 11 | import kotlin.math.ceil 12 | 13 | class IntrinsicDimensionLayouts(val view: View): LeafDimensionLayouts() { 14 | 15 | // init { 16 | // GlobalScope.launch(Dispatchers.UI) { 17 | // delay(1) 18 | // println("IntrinsicDimensionLayouts for ${generateSequence(view){ it.parent as? View }.take(8).joinToString("->") { it::class.java.simpleName }}: ${this@IntrinsicDimensionLayouts}") 19 | // } 20 | // } 21 | 22 | override fun measureX(output: Measurement) { 23 | view.measure( 24 | View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), 25 | View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) 26 | ) 27 | output.size = view.measuredWidth.toFloat() / dip + .1f 28 | output.startMargin = 16f 29 | output.endMargin = 16f 30 | } 31 | 32 | override fun measureY(xSize: Float, output: Measurement) { 33 | view.measure( 34 | View.MeasureSpec.makeMeasureSpec(ceil(xSize * dip).toInt(), View.MeasureSpec.EXACTLY), 35 | View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) 36 | ) 37 | output.size = view.measuredHeight.toFloat() / dip + .1f 38 | output.startMargin = 16f 39 | output.endMargin = 16f 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/NotificationHandlerService.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.app.IntentService 4 | import android.content.Intent 5 | import com.lightningkite.koolui.ApplicationAccess 6 | import com.lightningkite.koolui.async.UI 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.GlobalScope 9 | import kotlinx.coroutines.launch 10 | import java.util.* 11 | 12 | class NotificationHandlerService : IntentService("Notification Handler Service") { 13 | 14 | companion object { 15 | const val EXTRA_ACTION = "action" 16 | } 17 | 18 | override fun onHandleIntent(intent: Intent) { 19 | val action = intent.getStringExtra(EXTRA_ACTION) ?: return 20 | GlobalScope.launch(Dispatchers.UI){ 21 | ApplicationAccess.onNotificationAction.asReversed().firstOrNull { it.invoke(action) } 22 | } 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/NumberInputType.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.text.InputType 4 | import com.lightningkite.koolui.concepts.NumberInputType 5 | 6 | fun NumberInputType.android(): Int = when (this) { 7 | NumberInputType.Integer -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED 8 | NumberInputType.Float -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED or InputType.TYPE_NUMBER_FLAG_DECIMAL 9 | NumberInputType.PositiveInteger -> InputType.TYPE_CLASS_NUMBER 10 | NumberInputType.PositiveFloat -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL 11 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/TextInputType.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.text.InputType 4 | import com.lightningkite.koolui.concepts.TextInputType 5 | 6 | fun TextInputType.android(): Int = when (this) { 7 | TextInputType.Paragraph -> InputType.TYPE_CLASS_TEXT or 8 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or 9 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT or 10 | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE or 11 | InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE or 12 | InputType.TYPE_TEXT_FLAG_MULTI_LINE 13 | TextInputType.Name -> InputType.TYPE_CLASS_TEXT or 14 | InputType.TYPE_TEXT_FLAG_CAP_WORDS or 15 | InputType.TYPE_TEXT_VARIATION_PERSON_NAME 16 | TextInputType.Password -> InputType.TYPE_CLASS_TEXT or 17 | InputType.TYPE_TEXT_VARIATION_PASSWORD 18 | TextInputType.Sentence -> InputType.TYPE_CLASS_TEXT or 19 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or 20 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT or 21 | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE or 22 | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE 23 | TextInputType.CapitalizedIdentifier -> InputType.TYPE_CLASS_TEXT or 24 | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS 25 | TextInputType.URL -> InputType.TYPE_CLASS_TEXT or 26 | InputType.TYPE_TEXT_VARIATION_URI 27 | TextInputType.Email -> InputType.TYPE_CLASS_TEXT or 28 | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS 29 | TextInputType.Phone -> InputType.TYPE_CLASS_PHONE 30 | TextInputType.Address -> InputType.TYPE_CLASS_TEXT or 31 | InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS 32 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/TextSize.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import com.lightningkite.koolui.concepts.TextSize 4 | 5 | 6 | fun TextSize.sp(): Float = when (this) { 7 | TextSize.Tiny -> 12f 8 | TextSize.Body -> 16f 9 | TextSize.Subheader -> 20f 10 | TextSize.Header -> 24f 11 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/View.touch.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.util.SparseArray 4 | import android.view.MotionEvent 5 | import android.view.View 6 | import com.lightningkite.koolui.views.Touch 7 | import com.lightningkite.reacktive.invokeAll 8 | import com.lightningkite.reacktive.property.update 9 | 10 | fun View.onNewTouch(handler: (Touch) -> Unit) { 11 | val touches = SparseArray() 12 | this.setOnTouchListener { v, event -> 13 | val identifier = event.getPointerId(event.actionIndex) 14 | when (event.actionMasked) { 15 | MotionEvent.ACTION_DOWN -> { 16 | val entry = Touch.Impl() 17 | touches.put(identifier, entry) 18 | entry.position.value.x = event.x 19 | entry.position.value.y = event.y 20 | entry.position.update() 21 | } 22 | MotionEvent.ACTION_MOVE -> { 23 | val entry = touches[identifier] ?: return@setOnTouchListener true 24 | entry.position.value.x = event.x 25 | entry.position.value.y = event.y 26 | } 27 | MotionEvent.ACTION_CANCEL, 28 | MotionEvent.ACTION_UP -> { 29 | val entry = touches[identifier] ?: return@setOnTouchListener true 30 | touches.removeAt(identifier) 31 | entry.position.value.x = event.x 32 | entry.position.value.y = event.y 33 | entry.position.update() 34 | entry.onRelease.invokeAll() 35 | } 36 | } 37 | true 38 | } 39 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/ViewLifecycle.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | import android.view.View 4 | import com.lightningkite.koolui.R 5 | import com.lightningkite.koolui.implementationhelpers.AnyLifecycles 6 | import com.lightningkite.koolui.implementationhelpers.TreeObservableProperty 7 | 8 | var View.lifecycle: TreeObservableProperty 9 | get() { 10 | val existing = getTag(R.id.lifecycle) as? TreeObservableProperty 11 | if (existing != null) return existing 12 | val new = TreeObservableProperty() 13 | setTag(R.id.lifecycle, new) 14 | return new 15 | } 16 | set(value) { 17 | setTag(R.id.lifecycle, value) 18 | } 19 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/ViewSwapManager.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android 2 | 3 | 4 | import android.view.View 5 | import android.view.ViewGroup 6 | 7 | /** 8 | * Helps one swap views in and out of a container. 9 | * 10 | * Created by joseph on 1/19/18. 11 | */ 12 | class ViewSwapManager( 13 | /** 14 | * The parent view to manage. 15 | */ 16 | val parent: VG, 17 | /** 18 | * A lambda that generates the default layout parameters for views being swapped in. 19 | */ 20 | val generateLayoutParams: () -> ViewGroup.LayoutParams 21 | ) { 22 | var currentView: View? = null 23 | 24 | fun swap(newView: View, animation: AnimationSet = AnimationSet.fade) { 25 | val oldView = currentView 26 | if (newView == oldView) return 27 | 28 | parent.addView(newView, generateLayoutParams()) 29 | if (oldView != null) { 30 | //animate out old view 31 | animation.animateOut(oldView, parent).withEndAction { parent.removeView(oldView) }.start() 32 | 33 | //animate in new view 34 | animation.animateIn(newView, parent).start() 35 | } 36 | currentView = newView 37 | } 38 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/access/ActivityAccess.ext.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("LkAndroidActivityAccess") 2 | @file:JvmMultifileClass 3 | 4 | package com.lightningkite.koolui.android.access 5 | 6 | 7 | import android.content.Intent 8 | import android.os.Bundle 9 | import com.lightningkite.koolui.android.access.ActivityAccess 10 | 11 | /** 12 | * Starts an intent with a direct callback. 13 | */ 14 | fun ActivityAccess.startIntent( 15 | intent: Intent, 16 | options: Bundle = Bundle(), 17 | onResult: (Int, Intent?) -> Unit = { _, _ -> } 18 | ) { 19 | activity?.startActivityForResult(intent, prepareOnResult(onResult = onResult), options) 20 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/access/ActivityAccess.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android.access 2 | 3 | 4 | import android.app.Activity 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.os.Bundle 8 | 9 | /** 10 | * An interface for accessing activities in a decentralized way, where multiple listeners can listen 11 | * to event like [onPause], [onResume], etc, and most importantly, can use [prepareOnResult] to set 12 | * up lambdas to occur when a result code is received. 13 | * 14 | * Created by joseph on 6/9/17. 15 | */ 16 | interface ActivityAccess { 17 | val activity: Activity? 18 | val context: Context 19 | 20 | val onResume: MutableCollection<() -> Unit> 21 | val onPause: MutableCollection<() -> Unit> 22 | val onSaveInstanceState: MutableCollection<(outState: Bundle) -> Unit> 23 | val onLowMemory: MutableCollection<() -> Unit> 24 | val onDestroy: MutableCollection<() -> Unit> 25 | val onActivityResult: MutableCollection<(request: Int, result: Int, data: Intent?) -> Unit> 26 | val onNewIntent: MutableCollection<(Intent) -> Unit> 27 | 28 | /** 29 | * When the back button is pressed, the lambdas in the list are invoked in reverse order until 30 | * one of the lambdas returns true, indicating that the button press has been handled. 31 | * 32 | * It is suggested that one add a lambda to the top of the list upon creation of a view that 33 | * needs its back button handled, and then to remove that lambda when the view goes away. 34 | */ 35 | val onBackPressed: MutableList<() -> Boolean> 36 | 37 | fun prepareOnResult( 38 | presetCode: Int = (Math.random() * Short.MAX_VALUE).toInt(), 39 | onResult: (Int, Intent?) -> Unit = { a, b -> } 40 | ): Int 41 | 42 | fun requestPermissions(permission: Array, onResult: (Map) -> Unit) 43 | fun requestPermission(permission: String, onResult: (Boolean) -> Unit) 44 | 45 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/android/dialog/Launchers.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.android.dialog 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.view.View 6 | import android.view.Window 7 | import android.view.WindowManager 8 | import com.lightningkite.koolui.android.access.ActivityAccess 9 | 10 | fun Context.dialog( 11 | dismissible: Boolean = true, 12 | windowModifier: Window.() -> Unit = {}, 13 | layoutParamModifier: WindowManager.LayoutParams.() -> Unit = {}, 14 | theme: Int? = null, 15 | viewGenerator: (ActivityAccess) -> View 16 | ) { 17 | val id: Int = viewGenerator.hashCode() 18 | GenericDialogActivity.containers[id] = 19 | GenericDialogActivity.ContainerData(viewGenerator, layoutParamModifier, windowModifier, theme) 20 | startActivity(Intent(this, GenericDialogActivity::class.java).apply { 21 | putExtra(GenericDialogActivity.EXTRA_CONTAINER, id) 22 | putExtra(GenericDialogActivity.EXTRA_DISMISS_ON_TOUCH_OUTSIDE, dismissible) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/async/UI.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual val Dispatchers.UI: CoroutineDispatcher get() = Dispatchers.Main 7 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/implementationhelpers/AnyDesiredMargins.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | 5 | data class DesiredMargins(val left: Float, val top: Float, val right: Float, val bottom: Float) { 6 | constructor(all: Float) : this(all, all, all, all) 7 | } 8 | 9 | val AnyDesiredMargins = WeakHashMap() 10 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/implementationhelpers/AnyLifecycles.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | 5 | 6 | val AnyLifecycles = WeakHashMap() 7 | 8 | //var Any.lifecycle 9 | // get() = AnyLifecycles.getOrPut(this){ TreeObservableProperty() } 10 | // set(value){ 11 | // AnyLifecycles[this] = value 12 | // } 13 | //fun Any.lifecycleChildOf(parent:Any){ 14 | // this.lifecycle = parent.lifecycle.child() 15 | //} 16 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/notification/MyFirebaseMessagingService.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.google.firebase.iid.FirebaseInstanceId 4 | import com.google.firebase.messaging.FirebaseMessagingService 5 | import com.google.firebase.messaging.RemoteMessage 6 | import com.lightningkite.koolui.ApplicationAccess 7 | 8 | class MyFirebaseMessagingService(): FirebaseMessagingService() { 9 | companion object { 10 | init { 11 | FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener { 12 | if(it.isSuccessful){ 13 | PushNotifications.backingToken.value = it.result?.token?.let{ PushNotificationToken(it) } 14 | } 15 | } 16 | } 17 | } 18 | 19 | override fun onNewToken(p0: String?) { 20 | PushNotifications.backingToken.value = p0?.let{ PushNotificationToken(it) } 21 | } 22 | 23 | override fun onMessageReceived(message: RemoteMessage) { 24 | super.onMessageReceived(message) 25 | 26 | val data = message.data 27 | val notification = Notification( 28 | id = data["n_id"]?.toIntOrNull() ?: 0, 29 | priority = data["n_priority"]?.toFloatOrNull() ?: .5f, 30 | title = data["n_title"] ?: "", 31 | content = data["n_content"] ?: "", 32 | image = data["n_image"], 33 | action = data["n_action"] ?: "", 34 | actions = data["n_actions"]?.let { 35 | it.split('|').associate { 36 | it.substringBefore('=') to it.substringAfter('=') 37 | } 38 | } ?: mapOf() 39 | ) 40 | ApplicationAccess.showNotification(notification) 41 | } 42 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.google.firebase.messaging.FirebaseMessaging 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | import com.lightningkite.reacktive.property.StandardObservableProperty 6 | 7 | actual object PushNotifications { 8 | internal var backingToken = StandardObservableProperty(null) 9 | actual val token: ObservableProperty get() = backingToken 10 | 11 | actual fun requestNotificationsPermission() { 12 | FirebaseMessaging.getInstance().isAutoInitEnabled = true 13 | MyFirebaseMessagingService 14 | } 15 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | import android.content.SharedPreferences 4 | import android.preference.PreferenceManager 5 | import com.lightningkite.koolui.ApplicationAccess 6 | import java.util.concurrent.ConcurrentHashMap 7 | 8 | actual object Preferences : Iterable> { 9 | 10 | val underlying by lazy { PreferenceManager.getDefaultSharedPreferences(ApplicationAccess.access!!.context) } 11 | val cache = ConcurrentHashMap() 12 | 13 | actual operator fun get(key: String): String? { 14 | cache[key]?.let{ return it } 15 | underlying.getString(key, null)?.let { 16 | cache[key] = it 17 | return it 18 | } 19 | return null 20 | } 21 | 22 | actual operator fun set(key: String, value: String?) { 23 | if(value == null){ 24 | cache.remove(key) 25 | underlying.edit().remove(key).apply() 26 | } else { 27 | cache[key] = value 28 | underlying.edit().putString(key, value).apply() 29 | } 30 | } 31 | actual override fun iterator(): Iterator> = underlying.all.entries.asSequence().mapNotNull { 32 | val value = it.value as? String ?: return@mapNotNull null 33 | it.key!! to value 34 | }.iterator() 35 | 36 | actual fun clear() { 37 | underlying.edit().clear().apply() 38 | } 39 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.image.Image 5 | import kotlinx.coroutines.GlobalScope 6 | import kotlinx.coroutines.async 7 | import kotlinx.coroutines.coroutineScope 8 | import kotlinx.coroutines.runBlocking 9 | 10 | actual object Resources { 11 | actual suspend fun getString( 12 | filename: String 13 | ): String = GlobalScope.async { 14 | ApplicationAccess.access!!.context.assets.open(filename).use { it.reader().readText() } 15 | }.await() 16 | 17 | actual suspend fun getByteArray( 18 | filename: String 19 | ): ByteArray = GlobalScope.async { 20 | ApplicationAccess.access!!.context.assets.open(filename).use { it.readBytes() } 21 | }.await() 22 | 23 | actual suspend fun getImage(filename: String): Image = GlobalScope.async { 24 | Image.fromByteArray(ApplicationAccess.access!!.context.assets.open(filename).use { it.readBytes() }) 25 | }.await() 26 | } 27 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/view/HasActivityAccess.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.view 2 | 3 | import android.content.Context 4 | import com.lightningkite.koolui.android.access.ActivityAccess 5 | 6 | interface HasActivityAccess { 7 | val activityAccess: ActivityAccess 8 | val context: Context get() = activityAccess.context 9 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/view/LayoutAndroidWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.view 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import com.lightningkite.koolui.android.* 7 | import com.lightningkite.koolui.concepts.Animation 8 | import com.lightningkite.koolui.layout.Layout 9 | import com.lightningkite.koolui.layout.LeafDimensionLayouts 10 | import com.lightningkite.koolui.layout.views.LayoutViewWrapper 11 | 12 | interface LayoutAndroidWrapper: LayoutViewWrapper, HasActivityAccess { 13 | 14 | override fun intrinsicDimensionLayouts(view: View): LeafDimensionLayouts = IntrinsicDimensionLayouts(view) 15 | 16 | override fun applyEntranceTransition(view: View, animation: Animation) { 17 | val parent = view.parent as? ViewGroup ?: return 18 | animation.android().animateIn.invoke(view, parent) 19 | } 20 | 21 | override fun applyExitTransition(view: View, animation: Animation, onComplete: () -> Unit) { 22 | val parent = view.parent as? ViewGroup ?: run { 23 | onComplete() 24 | return 25 | } 26 | animation.android().animateOut.invoke(view, parent).withEndAction(onComplete) 27 | } 28 | 29 | override fun defaultViewContainer(): View = ManualLayout(context) 30 | 31 | override fun SPECIFIC.adapter() = AndroidLayoutAdapter(this, true) 32 | 33 | 34 | override fun nativeViewAdapter(wraps: Layout<*, View>): View = LayoutToAndroidView(context).apply { 35 | this.layout = wraps 36 | } 37 | } -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/view/MaterialAndroidViewFactory.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.view 2 | 3 | import android.view.View 4 | import com.lightningkite.koolui.android.access.ActivityAccess 5 | import com.lightningkite.koolui.color.ColorSet 6 | import com.lightningkite.koolui.color.Theme 7 | import com.lightningkite.koolui.layout.Layout 8 | import com.lightningkite.koolui.layout.views.LayoutVFRootAndDialogs 9 | import com.lightningkite.koolui.view.basic.LayoutAndroidBasic 10 | import com.lightningkite.koolui.view.graphics.LayoutAndroidGraphics 11 | import com.lightningkite.koolui.view.graphics.LayoutAndroidInteractive 12 | import com.lightningkite.koolui.view.layout.LayoutAndroidLayout 13 | import com.lightningkite.koolui.view.navigation.LayoutAndroidNavigation 14 | import com.lightningkite.koolui.views.Themed 15 | import com.lightningkite.koolui.views.ViewFactory 16 | 17 | /** 18 | * This is an example how how to set up your factory. 19 | * It is not recommended you use this directly - instead copy it and use the modules you want. 20 | */ 21 | class MaterialAndroidViewFactory( 22 | override val activityAccess: ActivityAccess, 23 | theme: Theme, 24 | colorSet: ColorSet = theme.main 25 | ) : ViewFactory>, 26 | Themed by Themed.impl(theme, colorSet), 27 | LayoutAndroidBasic /*ViewFactoryBasic*/, 28 | LayoutAndroidInteractive /*ViewFactoryInteractive*/, 29 | LayoutAndroidGraphics /*ViewFactoryGraphics*/, 30 | LayoutAndroidLayout /*ViewFactoryLayout*/, 31 | LayoutAndroidNavigation /*ViewFactoryNavigation*/, 32 | LayoutVFRootAndDialogs /*ViewFactoryDialogs*/, 33 | LayoutAndroidWrapper /*LayoutViewWrapper*/ { 34 | override var root: Layout<*, View>? = null 35 | } 36 | 37 | -------------------------------------------------------------------------------- /koolui/src/androidMain/kotlin/com/lightningkite/koolui/view/graphics/LayoutAndroidGraphics.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.view.graphics 2 | 3 | import android.view.View 4 | import com.lightningkite.koolui.android.CanvasView 5 | import com.lightningkite.koolui.canvas.AndroidCanvas 6 | import com.lightningkite.koolui.canvas.Canvas 7 | import com.lightningkite.koolui.layout.Layout 8 | import com.lightningkite.koolui.layout.views.LayoutViewWrapper 9 | import com.lightningkite.koolui.layout.views.intrinsicLayout 10 | import com.lightningkite.koolui.view.HasActivityAccess 11 | import com.lightningkite.koolui.views.graphics.ViewFactoryGraphics 12 | import com.lightningkite.reacktive.property.ObservableProperty 13 | import com.lightningkite.reacktive.property.lifecycle.bind 14 | 15 | interface LayoutAndroidGraphics : ViewFactoryGraphics>, LayoutViewWrapper, HasActivityAccess { 16 | override fun canvas(draw: ObservableProperty Unit>): Layout<*, View> = intrinsicLayout(CanvasView(activityAccess.context)) { layout -> 17 | val c = AndroidCanvas() 18 | layout.isAttached.bind(draw) { 19 | this.render = { c.canvas = this; c.it() } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/ApplicationAccess.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | import com.lightningkite.koolui.notification.Notification 5 | import com.lightningkite.reacktive.property.ObservableProperty 6 | import com.lightningkite.recktangle.Point 7 | 8 | expect object ApplicationAccess { 9 | 10 | fun post(action: () -> Unit) 11 | 12 | val displaySize: ObservableProperty //can change on rotation, etc. 13 | val isInForeground: ObservableProperty 14 | /** 15 | * Called in reverse order until one of the lambdas returns true, indicating the press has been handled. 16 | */ 17 | val onBackPressed: MutableList<() -> Boolean> 18 | val onAnimationFrame: MutableCollection<() -> Unit> 19 | 20 | fun showNotification(notification: Notification) 21 | /** 22 | * Event for when a notification action is fired. 23 | * Called in reverse order until one of the lambdas returns true, indicating the action has been handled. 24 | * Null action means default/view. 25 | */ 26 | val onNotificationAction: MutableList<(action: String) -> Boolean> 27 | 28 | /** 29 | * Event for when a deep link is caught by the app. 30 | * You'll have to register to catch it in the manifests of each platform separately. 31 | */ 32 | val onDeepLink: MutableList<(url: String) -> Boolean> 33 | 34 | /** 35 | * Called before the application dies due to an uncaught error. 36 | * Use this to send an error report. 37 | */ 38 | val onException: MutableList<(throwable: Throwable) -> Unit> 39 | 40 | //Potential additions: clipboard access 41 | } 42 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/ExternalAccess.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | import com.lightningkite.lokalize.location.Geohash 5 | 6 | fun ExternalAccess.openGeohash(geohash: Geohash){ 7 | when (UIPlatform.current) { 8 | UIPlatform.IOS -> ExternalAccess.openUri(Uri("http://maps.apple.com/?q=Place&ll=${geohash.latitude},${geohash.longitude}")) 9 | UIPlatform.Android, 10 | UIPlatform.Virtual -> ExternalAccess.openUri(Uri("geo:${geohash.latitude},${geohash.longitude}")) 11 | else -> ExternalAccess.openUri(Uri("https://www.google.com/maps/search/?api=1&query=${geohash.latitude},${geohash.longitude}")) 12 | } 13 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/ExternalAccess.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.string.* 4 | 5 | expect object ExternalAccess { 6 | 7 | fun openUri(uri: Uri) 8 | 9 | fun saveToChosenFile( 10 | name: String, 11 | contentType: MediaTypeWithDescription, 12 | data: ByteArray, 13 | alwaysOpenDialog: Boolean = false, 14 | callback: (succeeded: Boolean) -> Unit = {} 15 | ) 16 | 17 | fun loadFromChosenFile( 18 | contentTypes: List = listOf(MediaTypeAcceptWithDescription.any), 19 | callback: (data: ByteArray?) -> Unit = {} 20 | ) 21 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/Location.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.lokalize.location.Geohash 4 | 5 | suspend fun String.getGeohash() = Location.getGeohash(this) 6 | suspend fun Geohash.getAddress() = Location.getAddress(this) -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/Location.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.Closeable 4 | import com.lightningkite.lokalize.location.Geohash 5 | import com.lightningkite.reacktive.Event 6 | import com.lightningkite.reacktive.property.ObservableProperty 7 | 8 | expect object Location { 9 | val available: Boolean 10 | suspend fun requestOnce( 11 | reason: String, 12 | accuracyBetterThanMeters: Double = 100.0 13 | ): LocationResult 14 | 15 | suspend fun requestOngoing( 16 | reason: String, 17 | accuracyBetterThanMeters: Double = 100.0 18 | ): ObservableProperty 19 | 20 | var getAddressImplementation: suspend (Geohash) -> String? 21 | suspend fun getAddress(from: Geohash): String? 22 | var getGeohashImplementation: suspend (String) -> Geohash? 23 | suspend fun getGeohash(from: String): Geohash? 24 | } 25 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/LocationResult.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.lokalize.location.Geohash 4 | import com.lightningkite.recktangle.Angle 5 | 6 | data class LocationResult( 7 | val location: Geohash, 8 | val accuracyMeters: Double = 100.0, 9 | val altitudeMeters: Double = 0.0, 10 | val altitudeAccuracyMeters: Double = 100.0, 11 | val headingFromNorth: Angle = Angle(0f), 12 | val speedMetersPerSecond: Double = 0.0 13 | ) -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | enum class UIPlatform { 4 | Android, 5 | Javascript, 6 | IOS, 7 | JavaFX, 8 | Lanterna, 9 | Virtual; 10 | 11 | companion object 12 | } 13 | 14 | expect val UIPlatform.Companion.current: UIPlatform -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/async/Lifecycle.scope.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import com.lightningkite.reacktive.Lifecycle 4 | import kotlinx.coroutines.* 5 | 6 | val Lifecycle.scope: CoroutineScope 7 | get() { 8 | val scope = CoroutineScope(Dispatchers.UI) 9 | GlobalScope.launch(Dispatchers.UI) { 10 | var willHoldLambda: (Boolean) -> Unit = {} 11 | willHoldLambda = { 12 | if (!it) { 13 | scope.cancel() 14 | GlobalScope.launch(Dispatchers.UI) { 15 | this@scope.remove(willHoldLambda) 16 | } 17 | } 18 | } 19 | this@scope.add(willHoldLambda) 20 | } 21 | return scope 22 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/async/suspend.observable.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import com.lightningkite.reacktive.property.ObservableProperty 4 | import com.lightningkite.reacktive.property.StandardObservableProperty 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.GlobalScope 7 | import kotlinx.coroutines.launch 8 | 9 | fun (suspend ()->DETAIL).observable(temp: GENERAL): ObservableProperty { 10 | val obs = StandardObservableProperty(temp) 11 | GlobalScope.launch(Dispatchers.UI){ 12 | obs.value = this@observable() 13 | } 14 | return obs 15 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/async/suspendingTransform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import com.lightningkite.reacktive.EnablingMutableCollection 4 | import com.lightningkite.reacktive.invokeAll 5 | import com.lightningkite.reacktive.property.MutableObservableProperty 6 | import com.lightningkite.reacktive.property.ObservableProperty 7 | import com.lightningkite.reacktive.property.StandardObservableProperty 8 | import kotlinx.coroutines.CoroutineScope 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.GlobalScope 11 | import kotlinx.coroutines.launch 12 | 13 | inline fun ObservableProperty.suspendingTransform( 14 | default: B, 15 | crossinline transformStarted: MutableObservableProperty.(A)->Unit = {}, 16 | scope: CoroutineScope = GlobalScope, 17 | crossinline transform: suspend (A)->B 18 | ): MutableObservableProperty { 19 | return object : EnablingMutableCollection<(B)->Unit>(), MutableObservableProperty { 20 | val listener = { it: A -> 21 | transformStarted(it) 22 | scope.launch(Dispatchers.UI){ 23 | value = transform(it) 24 | } 25 | Unit 26 | } 27 | override fun enable() { 28 | listener(this@suspendingTransform.value) 29 | this@suspendingTransform.add(listener) 30 | } 31 | 32 | override fun disable() { 33 | this@suspendingTransform.remove(listener) 34 | } 35 | 36 | override var value: B = default 37 | set(value){ 38 | field = value 39 | this.invokeAll(value) 40 | } 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/async/ui.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | expect val Dispatchers.UI: CoroutineDispatcher 7 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/canvas/Canvas.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.canvas 2 | 3 | import com.lightningkite.koolui.color.Color 4 | import com.lightningkite.koolui.geometry.Align 5 | import com.lightningkite.recktangle.Matrix2D 6 | import com.lightningkite.recktangle.Point 7 | 8 | 9 | interface Canvas { 10 | val size: Point 11 | 12 | fun beginPath() 13 | fun move(x: Float, y: Float) 14 | fun line(x: Float, y: Float) 15 | fun curve(controlX: Float, controlY: Float, x: Float, y:Float) 16 | fun curve(control1X: Float, control1Y: Float, control2X: Float, control2Y: Float, x: Float, y:Float) 17 | fun close() 18 | 19 | fun stroke(color: Color, width: Float) 20 | fun fill(color: Color) 21 | 22 | fun clearRect(left: Float, top: Float, right: Float, bottom: Float) 23 | 24 | fun text(text: String, x: Float, align: Align, baselineY: Float, textSize: Float, color: Color) 25 | fun measureTextWidth(text: String, textSize: Float): Float 26 | fun measureTextHeight(text: String, textSize: Float): Float 27 | } 28 | 29 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/color/ColorSet.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.color 2 | 3 | import com.lightningkite.koolui.concepts.Importance 4 | 5 | data class ColorSet( 6 | val background: Color = Color.white, 7 | val backgroundDisabled: Color = background.copy(alpha = background.alpha / 2), 8 | val backgroundHighlighted: Color = background.highlight(.2f), 9 | val foreground: Color = Color.black.copy(alpha = .8f), 10 | val foregroundDisabled: Color = foreground.copy(alpha = foreground.alpha / 2), 11 | val foregroundHighlighted: Color = foreground.copy(alpha = 1f) 12 | ) { 13 | 14 | fun importance(value: Importance): Color = when (value) { 15 | Importance.Low -> foregroundDisabled 16 | Importance.Normal -> foreground 17 | Importance.High -> foregroundHighlighted 18 | Importance.Danger -> destructive.foreground 19 | } 20 | 21 | companion object { 22 | fun basedOnBack(color: Color) = ColorSet( 23 | background = color, 24 | foreground = if (color.average > .7f) Color.black.copy(alpha = .8f) else Color.white.copy(alpha = .8f) 25 | ) 26 | fun basedOnBackBold(color: Color) = ColorSet( 27 | background = color, 28 | foreground = if (color.average > .7f) Color.black else Color.white, 29 | foregroundHighlighted = if (color.average > .7f) Color.black else Color.white 30 | ) 31 | 32 | val destructive = basedOnBack(Color.red) 33 | } 34 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/color/HSVColor.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.color 2 | 3 | import com.lightningkite.recktangle.Angle 4 | 5 | 6 | data class HSVColor( 7 | val alpha: Float = 0f, 8 | val hue: Angle = Angle(0f), 9 | val saturation: Float = 0f, 10 | val value: Float = 0f 11 | ) { 12 | fun toRGB(): Color { 13 | val h = (hue.circles * 6).toInt() 14 | val f = hue.circles * 6 - h 15 | val p = value * (1 - saturation) 16 | val q = value * (1 - f * saturation) 17 | val t = value * (1 - (1 - f) * saturation) 18 | 19 | return when (h) { 20 | 0 -> Color(alpha = alpha, red = value, green = t, blue = p) 21 | 1 -> Color(alpha = alpha, red = q, green = value, blue = p) 22 | 2 -> Color(alpha = alpha, red = p, green = value, blue = t) 23 | 3 -> Color(alpha = alpha, red = p, green = q, blue = value) 24 | 4 -> Color(alpha = alpha, red = t, green = p, blue = value) 25 | 5 -> Color(alpha = alpha, red = value, green = p, blue = q) 26 | else -> Color.transparent 27 | } 28 | } 29 | 30 | companion object { 31 | fun interpolate(left: HSVColor, right: HSVColor, ratio: Float): HSVColor { 32 | val invRatio = 1 - ratio 33 | // val leftHuePower = left.saturation 34 | // val rightHuePower = right.saturation 35 | // val hueRatio = leftHuePower / (rightHuePower + leftHuePower) 36 | return HSVColor( 37 | alpha = left.alpha.times(invRatio) + right.alpha.times(ratio), 38 | hue = left.hue + (left.hue angleTo right.hue) * ratio, 39 | saturation = left.saturation.times(invRatio) + right.saturation.times(ratio), 40 | value = left.value.times(invRatio) + right.value.times(ratio) 41 | ) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/color/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.color 2 | 3 | import com.lightningkite.koolui.concepts.Importance 4 | 5 | data class Theme( 6 | val main: ColorSet = ColorSet(), 7 | val bar: ColorSet = ColorSet.basedOnBackBold(Color.fromInt(0xFF5370CE.toInt())), 8 | val accent: ColorSet = ColorSet.basedOnBackBold(Color.fromInt(0xFFD82222.toInt())) 9 | ) { 10 | 11 | fun importance(value: Importance): ColorSet = when (value) { 12 | Importance.Low -> main 13 | Importance.Normal -> bar 14 | Importance.High -> accent 15 | Importance.Danger -> ColorSet.destructive 16 | } 17 | 18 | companion object { 19 | fun light( 20 | primaryColor: Color = Color.fromInt(0xFF5370CE.toInt()), 21 | secondaryColor: Color = Color.fromInt(0xFFD82222.toInt()) 22 | ) = Theme( 23 | main = ColorSet.basedOnBack(Color.white), 24 | bar = ColorSet.basedOnBackBold(primaryColor), 25 | accent = ColorSet.basedOnBackBold(secondaryColor) 26 | ) 27 | 28 | fun dark( 29 | primaryColor: Color = Color.fromInt(0xFF5370CE.toInt()), 30 | secondaryColor: Color = Color.fromInt(0xFFD82222.toInt()) 31 | ) = Theme( 32 | main = ColorSet.basedOnBack(Color.gray(.1f)), 33 | bar = ColorSet.basedOnBackBold(primaryColor), 34 | accent = ColorSet.basedOnBackBold(secondaryColor) 35 | ) 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/Animation.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | enum class Animation { 4 | 5 | None, 6 | Push, 7 | Pop, 8 | MoveUp, 9 | MoveDown, 10 | Fade, 11 | Flip 12 | } 13 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/Importance.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | enum class Importance { 4 | Low, 5 | Normal, 6 | High, 7 | Danger; 8 | 9 | companion object { 10 | val values = values().toList() 11 | } 12 | 13 | val more: Importance 14 | get() { 15 | return values.getOrNull(values.indexOf(this) + 1) ?: Importance.Danger 16 | } 17 | val less: Importance 18 | get() { 19 | return values.getOrNull(values.indexOf(this) - 1) ?: Importance.Low 20 | } 21 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/NumberInputType.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | enum class NumberInputType { 4 | Integer, 5 | Float, 6 | PositiveInteger, 7 | PositiveFloat 8 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/StackObservableProperty.Animation.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | import com.lightningkite.reacktive.property.ObservableProperty 4 | import com.lightningkite.reacktive.property.transform 5 | import com.lightningkite.reacktive.list.ObservableList 6 | import com.lightningkite.reacktive.list.lastOrNullObservable 7 | 8 | fun ObservableList.lastOrNullObservableWithAnimations( 9 | push: Animation = Animation.Push, 10 | pop: Animation = Animation.Pop, 11 | other: Animation = Animation.Fade 12 | ): ObservableProperty> { 13 | var current = this.size 14 | return this.lastOrNullObservable().transform { 15 | val newSize = this.size 16 | val result = it to when { 17 | newSize < current -> pop 18 | newSize == current -> other 19 | newSize > current -> push 20 | else -> other 21 | } 22 | current = newSize 23 | result 24 | } 25 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/TabItem.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | import com.lightningkite.koolui.image.ImageWithOptions 4 | import com.lightningkite.reacktive.property.ConstantObservableProperty 5 | import com.lightningkite.reacktive.property.ObservableProperty 6 | 7 | data class TabItem( 8 | var imageWithOptions: ImageWithOptions, 9 | var text: String, 10 | var description: String = "", 11 | var enabled: ObservableProperty = ConstantObservableProperty(true) 12 | ) 13 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/TextInputType.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | enum class TextInputType { 4 | Paragraph, 5 | Name, 6 | Password, 7 | Sentence, 8 | 9 | CapitalizedIdentifier, 10 | 11 | URL, 12 | Email, 13 | Phone, 14 | Address 15 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/concepts/TextSize.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.concepts 2 | 3 | enum class TextSize { 4 | Tiny, 5 | Body, 6 | Subheader, 7 | Header; 8 | 9 | companion object { 10 | val values = values().toList() 11 | } 12 | 13 | val bigger: TextSize 14 | get() { 15 | return values.getOrNull(values.indexOf(this) + 1) ?: TextSize.Header 16 | } 17 | val smaller: TextSize 18 | get() { 19 | return values.getOrNull(values.indexOf(this) - 1) ?: TextSize.Tiny 20 | } 21 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/geometry/Align.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.geometry 2 | 3 | enum class Align { Start, Center, End, Fill } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/geometry/AlignPair.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.geometry 2 | 3 | import com.lightningkite.koolui.geometry.Align.* 4 | 5 | enum class AlignPair(val horizontal: Align, val vertical: Align) { 6 | TopLeft(Start, Start), 7 | TopCenter(Center, Start), 8 | TopFill(Fill, Start), 9 | TopRight(End, Start), 10 | CenterLeft(Start, Center), 11 | CenterCenter(Center, Center), 12 | CenterFill(Fill, Center), 13 | CenterRight(End, Center), 14 | FillLeft(Start, Fill), 15 | FillCenter(Center, Fill), 16 | FillFill(Fill, Fill), 17 | FillRight(End, Fill), 18 | BottomLeft(Start, End), 19 | BottomCenter(Center, End), 20 | BottomFill(Fill, End), 21 | BottomRight(End, End), 22 | } 23 | 24 | fun alignPair(horizontal: Align, vertical: Align): AlignPair { 25 | return when (horizontal) { 26 | Align.Start -> when (vertical) { 27 | Align.Start -> AlignPair.TopLeft 28 | Align.Fill -> AlignPair.FillLeft 29 | Align.Center -> AlignPair.CenterLeft 30 | Align.End -> AlignPair.BottomLeft 31 | } 32 | Align.Center -> when (vertical) { 33 | Align.Start -> AlignPair.TopCenter 34 | Align.Fill -> AlignPair.FillCenter 35 | Align.Center -> AlignPair.CenterCenter 36 | Align.End -> AlignPair.BottomCenter 37 | } 38 | Align.Fill -> when (vertical) { 39 | Align.Start -> AlignPair.TopFill 40 | Align.Fill -> AlignPair.FillFill 41 | Align.Center -> AlignPair.CenterFill 42 | Align.End -> AlignPair.BottomFill 43 | } 44 | Align.End -> when (vertical) { 45 | Align.Start -> AlignPair.TopRight 46 | Align.Fill -> AlignPair.FillRight 47 | Align.Center -> AlignPair.CenterRight 48 | Align.End -> AlignPair.BottomRight 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/geometry/Direction.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.geometry 2 | 3 | import kotlin.math.PI 4 | 5 | enum class Direction(val uiPositive: Boolean, val vertical: Boolean, val radians: Double) { 6 | Right( 7 | uiPositive = true, 8 | vertical = false, 9 | radians = 0.0 10 | ), 11 | Up( 12 | uiPositive = false, 13 | vertical = true, 14 | radians = PI * .5 15 | ), 16 | Left( 17 | uiPositive = false, 18 | vertical = false, 19 | radians = PI 20 | ), 21 | Down( 22 | uiPositive = true, 23 | vertical = true, 24 | radians = PI * 1.5 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/geometry/LinearPlacement.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.geometry 2 | 3 | class LinearPlacement( 4 | val weight: Float = 0f, 5 | val align: Align = Align.Fill 6 | ) { 7 | companion object { 8 | val fillStart = LinearPlacement(1f, Align.Start) 9 | val fillCenter = LinearPlacement(1f, Align.Center) 10 | val fillEnd = LinearPlacement(1f, Align.End) 11 | val fillFill = LinearPlacement(1f, Align.Fill) 12 | val wrapStart = LinearPlacement(0f, Align.Start) 13 | val wrapCenter = LinearPlacement(0f, Align.Center) 14 | val wrapEnd = LinearPlacement(0f, Align.End) 15 | val wrapFill = LinearPlacement(0f, Align.Fill) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/geometry/Measurement.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.geometry 2 | 3 | data class Measurement(var startMargin: Float = 8f, var size: Float = 0f, var endMargin: Float = 8f) { 4 | val totalSpace: Float get() = startMargin + size + endMargin 5 | fun set(other: Measurement) { 6 | this.startMargin = other.startMargin 7 | this.size = other.size 8 | this.endMargin = other.endMargin 9 | } 10 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/image/Image.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.exception.stackTraceString 4 | import com.lightningkite.kommon.string.Uri 5 | import com.lightningkite.koolui.color.Color 6 | import com.lightningkite.recktangle.Point 7 | 8 | fun Image.withOptions( 9 | defaultSize: Point? = null, 10 | tint: Color = Color.white, 11 | scaleType: ImageScaleType = ImageScaleType.Fill 12 | ): ImageWithOptions = ImageWithOptions(this, defaultSize = defaultSize, tint = tint, scaleType = scaleType) 13 | 14 | suspend fun Image.Companion.fromUrl(url: Uri): Image? = try { 15 | Image.Companion.fromUrlUnsafe(url) 16 | } catch (e: Exception) { 17 | println(e.stackTraceString()); null 18 | } 19 | 20 | suspend fun Image.Companion.fromUrl(url: String): Image? = try { 21 | Image.Companion.fromUrlUnsafe(url) 22 | } catch (e: Exception) { 23 | println(e.stackTraceString()); null 24 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/image/Image.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | 5 | expect class Image { 6 | companion object { 7 | fun fromSvgString(svg: String): Image 8 | fun fromByteArray(byteArray: ByteArray): Image 9 | suspend fun fromUrlUnsafe(url: Uri): Image 10 | suspend fun fromUrlUnsafe(url: String): Image 11 | val blank: Image 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/image/ImageScaleType.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | /** 4 | * Describes how an image should scale. 5 | */ 6 | enum class ImageScaleType { 7 | /** 8 | * Make the image fill the whole space, cropping the edges to fit. 9 | */ 10 | Crop, 11 | /** 12 | * Make the image fill the space, but makes sure the ratio stays correct. 13 | */ 14 | Fill, 15 | /** 16 | * The image will take as much space as it takes, and will simply be centered. 17 | */ 18 | Center 19 | } 20 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/image/ImageWithOptions.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.koolui.color.Color 4 | import com.lightningkite.recktangle.Point 5 | 6 | class ImageWithOptions( 7 | val image: Image, 8 | val defaultSize: Point? = null, 9 | val tint: Color = Color.white, 10 | val scaleType: ImageScaleType = ImageScaleType.Fill 11 | ) { 12 | companion object { 13 | val none = ImageWithOptions(Image.blank) 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/implementationhelpers/TreeObservableProperty.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | import com.lightningkite.reacktive.invokeAll 5 | import com.lightningkite.reacktive.property.ObservableProperty 6 | 7 | class TreeObservableProperty() : ObservableProperty, MutableCollection<(Boolean) -> Unit> by ArrayList() { 8 | var parent: TreeObservableProperty? = null 9 | set(value) { 10 | if (value == this) throw IllegalArgumentException() 11 | field?.children?.remove(this) 12 | field = value 13 | value?.children?.add(this) 14 | broadcast() 15 | } 16 | var alwaysOn: Boolean = false 17 | set(value) { 18 | field = value 19 | broadcast() 20 | } 21 | override val value: Boolean get() = (parent?.value ?: alwaysOn) 22 | var previousValue: Boolean = value 23 | 24 | val children = ArrayList() 25 | fun add(element: TreeObservableProperty): Boolean = children.add(element) 26 | fun remove(element: TreeObservableProperty): Boolean = children.remove(element) 27 | 28 | fun broadcast() { 29 | val newValue = value 30 | if (newValue != previousValue) { 31 | previousValue = newValue 32 | this.invokeAll(newValue) 33 | for (child in children) { 34 | child.broadcast() 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/DimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | interface DimensionLayout { 6 | fun childNeedsMeasurement(fromChild: DimensionLayout) 7 | fun childNeedsLayout(fromChild: DimensionLayout? = null) 8 | 9 | var parent: DimensionLayout? 10 | val measurement: Measurement 11 | /** 12 | * @return Whether or not any work had to be done. 13 | */ 14 | fun layout(start: Float, end: Float): Boolean 15 | /** 16 | * @return Whether or not any work had to be done. 17 | */ 18 | fun refresh(): Boolean 19 | 20 | val start: Float 21 | val end: Float 22 | 23 | val needsLayout: Boolean 24 | val childNeedsLayout: Boolean 25 | 26 | fun requestMeasurement() 27 | fun requestLayout() 28 | 29 | var onPlacement: (start: Float, end: Float) -> Unit 30 | var onLayoutRequest: ()->Unit 31 | } 32 | 33 | 34 | val DimensionLayout.size: Float get() = end - start 35 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/DimensionLayout.withNeedsLayoutCallback.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | //Creation order: children(); view(); Layout(DimensionLayout(view.x, children.x), DimensionLayout(view.y, children.y)) 4 | 5 | /* 6 | 7 | - Invalidate - goes up the tree marking invalidated, layout called by user on 'frame' 8 | - layout - called with a 'start' and 'end', placing itself and its children 9 | 10 | PROCESS 11 | 12 | - 'invalidations' are made, bubbling up / everything starts as invalidated 13 | - next frame 'Layout.layout' is called 14 | - 'Layout.layout' cycles calling 'X.layout' and 'Y.layout' until there are no invalidations left OR it's been 3+ iterations 15 | - 'layout' goes down through the tree, skipping wherever it hasn't been invalidated AND size is the same 16 | 17 | PARTS 18 | 19 | - 'DimensionMeasure' - Measures a view 20 | - 'DimensionLayout' - Lays out a view 21 | - 'Layout' - Combo of both X and Y measures/layouts, lifecycles 22 | - 'ViewAdapter' - basically as is 23 | 24 | MINIMAL WORK 25 | 26 | - View claims it needs relayout VS View claims it has new size 27 | - New size will lead to parent needing to relayout *IF* the size of said view actually can change 28 | - Invalidation must climb tree because root controls it; however, on the way back down, we only want to hit what's necessary 29 | - Child needs layout VS *I* need layout 30 | 31 | */ 32 | 33 | fun DimensionLayout.withNeedsLayoutCallback(callback: ()->Unit) = object : DimensionLayout by this { 34 | override fun childNeedsLayout(fromChild: DimensionLayout?) { 35 | this@withNeedsLayoutCallback.childNeedsLayout(fromChild) 36 | if(this.childNeedsLayout){ 37 | callback() 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/ForceMarginsDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | class ForceMarginsDimensionLayout( 6 | val wraps: DimensionLayout, 7 | val startMargin: Float, 8 | val endMargin: Float 9 | ) : DimensionLayout by wraps { 10 | val myMeasurement = Measurement() 11 | override val measurement: Measurement 12 | get() { 13 | myMeasurement.startMargin = startMargin 14 | myMeasurement.endMargin = endMargin 15 | myMeasurement.size = wraps.measurement.size 16 | return myMeasurement 17 | } 18 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/ForceSizeDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | class ForceSizeDimensionLayout( 6 | val wraps: DimensionLayout, 7 | val size: Float 8 | ) : DimensionLayout by wraps { 9 | val myMeasurement = Measurement() 10 | override val measurement: Measurement 11 | get() { 12 | myMeasurement.startMargin = wraps.measurement.startMargin 13 | myMeasurement.endMargin = wraps.measurement.endMargin 14 | myMeasurement.size = size 15 | return myMeasurement 16 | } 17 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/FrameDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | class FrameDimensionLayout( 6 | val child: DimensionLayout, 7 | val startMargin: Float = 8f, 8 | val endMargin: Float = 8f 9 | ) : BaseDimensionLayout() { 10 | 11 | init { 12 | child.parent = this 13 | } 14 | 15 | override val childSequence: Sequence 16 | get() = sequenceOf(child) 17 | 18 | override fun measure(output: Measurement) { 19 | output.startMargin = startMargin 20 | output.endMargin = endMargin 21 | output.size = child.measurement.totalSpace 22 | } 23 | 24 | override fun layoutChildren(size: Float) { 25 | child.layout(child.measurement.startMargin, size - child.measurement.endMargin) 26 | } 27 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/LeafDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | import kotlin.math.max 5 | 6 | class LeafDimensionLayout( 7 | val measureLambda: (output: Measurement) -> Unit 8 | ) : BaseDimensionLayout() { 9 | 10 | constructor(startMargin: Float, size: Float, endMargin: Float):this({ 11 | it.startMargin = startMargin 12 | it.size = size 13 | it.endMargin = endMargin 14 | }) 15 | 16 | override fun measure(output: Measurement) { 17 | measureLambda(output) 18 | } 19 | override fun layoutChildren(size: Float) {} 20 | override val childSequence: Sequence get() = emptySequence() 21 | 22 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/LeafDimensionLayouts.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | abstract class LeafDimensionLayouts { 6 | abstract fun measureX(output: Measurement) 7 | abstract fun measureY(xSize: Float, output: Measurement) 8 | 9 | val x: DimensionLayout = object : BaseDimensionLayout() { 10 | override val childSequence: Sequence 11 | get() = emptySequence() 12 | 13 | override fun layoutChildren(size: Float) { 14 | y.requestMeasurement() 15 | y.requestLayout() 16 | } 17 | 18 | override fun layout(start: Float, end: Float): Boolean { 19 | val result = super.layout(start, end) 20 | return result 21 | } 22 | 23 | override fun measure(output: Measurement) { 24 | measureX(output) 25 | } 26 | } 27 | val y: DimensionLayout = object : BaseDimensionLayout() { 28 | override val childSequence: Sequence 29 | get() = emptySequence() 30 | 31 | override fun layoutChildren(size: Float) {} 32 | 33 | override fun layout(start: Float, end: Float): Boolean { 34 | val result = super.layout(start, end) 35 | return result 36 | } 37 | 38 | override fun measure(output: Measurement) { 39 | measureY(x.size, output) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/MaxSizeDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | import kotlin.math.min 5 | 6 | class MaxSizeDimensionLayout( 7 | val wraps: DimensionLayout, 8 | val maxSize: Float 9 | ) : DimensionLayout by wraps { 10 | val myMeasurement = Measurement() 11 | override val measurement: Measurement 12 | get() { 13 | myMeasurement.startMargin = wraps.measurement.startMargin 14 | myMeasurement.endMargin = wraps.measurement.endMargin 15 | myMeasurement.size = min(maxSize, wraps.measurement.size) 16 | return myMeasurement 17 | } 18 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/MinSizeDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | import kotlin.math.max 5 | 6 | class MinSizeDimensionLayout( 7 | val wraps: DimensionLayout, 8 | val minSize: Float 9 | ) : DimensionLayout by wraps { 10 | val myMeasurement = Measurement() 11 | override val measurement: Measurement 12 | get() { 13 | myMeasurement.startMargin = wraps.measurement.startMargin 14 | myMeasurement.endMargin = wraps.measurement.endMargin 15 | myMeasurement.size = max(minSize, wraps.measurement.size) 16 | return myMeasurement 17 | } 18 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/PassOnDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | class PassOnDimensionLayout( 6 | val child: DimensionLayout 7 | ) : BaseDimensionLayout() { 8 | 9 | init { 10 | child.parent = this 11 | } 12 | 13 | override val childSequence: Sequence 14 | get() = sequenceOf(child) 15 | 16 | override fun measure(output: Measurement) { 17 | output.set(child.measurement) 18 | } 19 | 20 | override fun layoutChildren(size: Float) { 21 | child.layout(0f, size) 22 | } 23 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/ScrollDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | import kotlin.math.max 5 | import kotlin.math.min 6 | 7 | class ScrollDimensionLayout( 8 | val wraps: DimensionLayout 9 | ) : BaseDimensionLayout() { 10 | init { 11 | wraps.parent = this 12 | } 13 | 14 | override fun measure(output: Measurement) { 15 | output.set(wraps.measurement) 16 | } 17 | 18 | override fun layoutChildren(size: Float) { 19 | wraps.layout(0f, min(size, wraps.measurement.size)) 20 | } 21 | 22 | override val childSequence: Sequence 23 | get() = sequenceOf(wraps) 24 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/SwapDimensionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | 5 | class SwapDimensionLayout( 6 | child: DimensionLayout, 7 | val startMargin: Float = 0f, 8 | val endMargin: Float = 0f 9 | ) : BaseDimensionLayout() { 10 | 11 | var child: DimensionLayout 12 | get() = layoutChild 13 | set(value){ 14 | layoutChild = value 15 | measurementChild = value 16 | } 17 | 18 | var layoutChild: DimensionLayout = child 19 | set(value){ 20 | if(field != measurementChild) { 21 | field.parent = null 22 | } 23 | field = value 24 | value.parent = this 25 | requestLayout() 26 | } 27 | 28 | var measurementChild: DimensionLayout = child 29 | set(value){ 30 | if(field != layoutChild) { 31 | field.parent = null 32 | } 33 | field = value 34 | value.parent = this 35 | requestMeasurement() 36 | } 37 | 38 | init { 39 | child.parent = this 40 | } 41 | 42 | override val childSequence: Sequence 43 | get() = sequenceOf(layoutChild, measurementChild) 44 | 45 | override fun measure(output: Measurement) { 46 | output.startMargin = startMargin 47 | output.endMargin = endMargin 48 | output.size = layoutChild.measurement.totalSpace 49 | } 50 | 51 | override fun layoutChildren(size: Float) { 52 | layoutChild.layout(layoutChild.measurement.startMargin, size - layoutChild.measurement.endMargin) 53 | } 54 | 55 | override fun childInvalidatesLayout(fromChild: DimensionLayout): Boolean = fromChild == layoutChild 56 | override fun childInvalidatesMeasurement(fromChild: DimensionLayout): Boolean = fromChild == measurementChild 57 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/ViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout 2 | 3 | interface ViewAdapter { 4 | val view: S 5 | val viewAsBase: V get() = view 6 | fun updatePlacementX(start: Float, end: Float) 7 | fun updatePlacementY(start: Float, end: Float) 8 | fun onAddChild(layout: Layout<*, V>){} 9 | fun onRemoveChild(layout: Layout<*, V>){} 10 | } 11 | 12 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/layout/views/LayoutViewWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.layout.views 2 | 3 | import com.lightningkite.koolui.concepts.Animation 4 | import com.lightningkite.koolui.implementationhelpers.TreeObservableProperty 5 | import com.lightningkite.koolui.layout.Layout 6 | import com.lightningkite.koolui.layout.LeafDimensionLayouts 7 | import com.lightningkite.koolui.layout.ViewAdapter 8 | import com.lightningkite.reacktive.Lifecycle 9 | import com.lightningkite.reacktive.property.ObservableProperty 10 | 11 | interface LayoutViewWrapper { 12 | fun nativeViewAdapter(wraps: Layout<*, VIEW>): VIEW 13 | fun defaultViewContainer(): VIEW 14 | fun SPECIFIC.adapter(): ViewAdapter 15 | fun intrinsicDimensionLayouts(view: VIEW): LeafDimensionLayouts 16 | fun applyEntranceTransition(view: VIEW, animation: Animation) 17 | fun applyExitTransition(view: VIEW, animation: Animation, onComplete: () -> Unit) 18 | } 19 | 20 | fun LayoutViewWrapper.wrap(view: SPECIFIC): Layout { 21 | val dim = intrinsicDimensionLayouts(view) 22 | return Layout( 23 | viewAdapter = view.adapter(), 24 | x = dim.x, 25 | y = dim.y 26 | ) 27 | } 28 | inline fun LayoutViewWrapper.wrap(view: SPECIFIC, configure: SPECIFIC.(lifecycle: TreeObservableProperty)->Unit): Layout = wrap(view).apply { 29 | view.configure(this.isAttached) 30 | } 31 | inline fun LayoutViewWrapper.intrinsicLayout(view: SPECIFIC, configure: SPECIFIC.(Layout)->Unit): Layout = wrap(view).apply { 32 | view.configure(this) 33 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/notification/Notification.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | data class Notification( 4 | val id: Int = 0, 5 | val priority: Float = .5f, 6 | val title: String = "", 7 | val content: String = "", 8 | val image: String? = null, 9 | val action: String = "", 10 | val actions: Map = mapOf() 11 | ) { 12 | //Actions: 13 | // Text input 14 | // Bring to foreground 15 | // Don't bring to foreground 16 | } 17 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/notification/PushNotificationToken.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.koolui.UIPlatform 4 | import com.lightningkite.koolui.current 5 | 6 | data class PushNotificationToken(val token: String, val platform: UIPlatform = UIPlatform.current) { 7 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.reacktive.property.ObservableProperty 4 | 5 | expect object PushNotifications { 6 | val token: ObservableProperty 7 | fun requestNotificationsPermission() 8 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | expect object Preferences:Iterable> { 4 | operator fun get(key: String):String? 5 | operator fun set(key: String, value: String?) 6 | override fun iterator(): Iterator> 7 | fun clear() 8 | } 9 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.image.Image 4 | 5 | 6 | expect object Resources { 7 | suspend fun getString( 8 | filename: String 9 | ): String 10 | 11 | suspend fun getByteArray( 12 | filename: String 13 | ): ByteArray 14 | 15 | suspend fun getImage( 16 | filename: String 17 | ): Image 18 | } 19 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/Themed.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.kommon.collection.pop 4 | import com.lightningkite.kommon.collection.push 5 | import com.lightningkite.koolui.color.ColorSet 6 | import com.lightningkite.koolui.color.Theme 7 | 8 | interface Themed { 9 | val theme: Theme 10 | val colorSet: ColorSet 11 | fun usingColorSet(colorSet: ColorSet, action: ()->T): T 12 | 13 | private class Impl(override val theme: Theme, override var colorSet: ColorSet) : Themed { 14 | val colorSetStack = ArrayList() 15 | override fun usingColorSet(colorSet: ColorSet, action: () -> T): T { 16 | colorSetStack.push(colorSet) 17 | this.colorSet = colorSet 18 | val result = action() 19 | colorSetStack.pop() 20 | this.colorSet = colorSetStack.lastOrNull() ?: theme.main 21 | return result 22 | } 23 | } 24 | companion object { 25 | fun impl(theme: Theme, colorSet: ColorSet = theme.main): Themed = Impl(theme, colorSet) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/Touch.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.reacktive.Event0 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | import com.lightningkite.reacktive.property.StandardObservableProperty 6 | import com.lightningkite.recktangle.Point 7 | 8 | interface Touch { 9 | val position: ObservableProperty 10 | val onRelease: Event0 11 | 12 | class Impl : Touch { 13 | override val position = StandardObservableProperty(Point()) 14 | override val onRelease = ArrayList<()->Unit>() 15 | } 16 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/ViewGenerator.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.koolui.views.layout.space 4 | 5 | interface ViewGenerator { 6 | val title: String get() = "" 7 | fun generate(dependency: DEPENDENCY): VIEW 8 | fun generateActions(dependency: DEPENDENCY): VIEW? = null 9 | 10 | companion object { 11 | //TODO: Re-enable inlining when Kotlin Native works with it 12 | /*inline*/ fun make( 13 | title: String, 14 | /*crossinline*/ generate: DEPENDENCY.() -> VIEW 15 | ) = object : ViewGenerator { 16 | override val title: String = title 17 | override fun generate(dependency: DEPENDENCY): VIEW = generate.invoke(dependency) 18 | } 19 | 20 | object Empty : ViewGenerator, Any?> { 21 | override fun generate(dependency: ViewFactory): Any? = dependency.space() 22 | } 23 | 24 | @Suppress("UNCHECKED_CAST") 25 | fun , VIEW> empty() = Empty as ViewGenerator 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/dialogs/ViewFactoryDialogs.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.dialogs 2 | 3 | interface ViewFactoryDialogs { 4 | /** 5 | * Launches a dialog with the given view in it. 6 | */ 7 | fun launchDialog( 8 | dismissable: Boolean = true, 9 | onDismiss: () -> Unit = {}, 10 | makeView: (dismissDialog: () -> Unit) -> VIEW 11 | ): Unit 12 | 13 | /** 14 | * Launches a selector with options to choose from. 15 | */ 16 | fun launchSelector( 17 | title: String? = null, 18 | options: List Unit>> 19 | ): Unit 20 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/dialogs/ViewFactoryDialogsDefault.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.dialogs 2 | 3 | import com.lightningkite.koolui.concepts.Animation 4 | import com.lightningkite.koolui.concepts.Importance 5 | import com.lightningkite.koolui.concepts.TextSize 6 | import com.lightningkite.koolui.geometry.Align 7 | import com.lightningkite.koolui.geometry.AlignPair 8 | import com.lightningkite.koolui.layout.DynamicAlignDimensionLayout 9 | import com.lightningkite.koolui.views.basic.ViewFactoryBasic 10 | import com.lightningkite.koolui.views.basic.text 11 | import com.lightningkite.koolui.views.interactive.ViewFactoryInteractive 12 | import com.lightningkite.koolui.views.interactive.button 13 | import com.lightningkite.koolui.views.layout.ViewFactoryLayout 14 | import com.lightningkite.koolui.views.layout.vertical 15 | import com.lightningkite.koolui.views.root.ViewFactoryRoot 16 | 17 | interface ViewFactoryDialogsDefault : ViewFactoryDialogs, ViewFactoryRoot, ViewFactoryLayout, ViewFactoryInteractive, ViewFactoryBasic { 18 | 19 | /** 20 | * Launches a selector with options to choose from. 21 | */ 22 | override fun launchSelector( 23 | title: String?, 24 | options: List Unit>> 25 | ): Unit { 26 | launchDialog(dismissable = true) { dismiss -> 27 | scrollVertical(vertical { 28 | title?.let { 29 | -text(text = it, align = AlignPair.CenterCenter, size = TextSize.Subheader) 30 | } 31 | for ((text, action) in options) { 32 | -button(text, importance = Importance.Low, onClick = { action(); dismiss() }) 33 | } 34 | }) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/graphics/ViewFactoryGraphics.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.graphics 2 | 3 | import com.lightningkite.koolui.canvas.Canvas 4 | import com.lightningkite.koolui.image.ImageWithOptions 5 | import com.lightningkite.reacktive.property.ObservableProperty 6 | 7 | interface ViewFactoryGraphics { 8 | /** 9 | * A canvas you can draw on. 10 | */ 11 | fun canvas( 12 | draw: ObservableProperty Unit> 13 | ): VIEW 14 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/interactive/KeyboardType.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.interactive 2 | 3 | enum class KeyboardType { 4 | Letters, Numbers, All 5 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/navigation/ViewFactoryNavigation.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.navigation 2 | 3 | import com.lightningkite.koolui.geometry.Direction 4 | import com.lightningkite.koolui.views.ViewGenerator 5 | import com.lightningkite.reacktive.list.ObservableList 6 | import com.lightningkite.reacktive.property.MutableObservableProperty 7 | import com.lightningkite.reacktive.property.ObservableProperty 8 | import com.lightningkite.reacktive.property.StandardObservableProperty 9 | 10 | 11 | /** 12 | * Shows a list of items and notifies you when it's scrolled to the end. 13 | */ 14 | fun ViewFactoryNavigation.list( 15 | data: ObservableList, 16 | firstIndex: MutableObservableProperty = StandardObservableProperty(0), 17 | lastIndex: MutableObservableProperty = StandardObservableProperty(0), 18 | direction: Direction = Direction.Down, 19 | makeView: (obs: ObservableProperty) -> VIEW 20 | ): VIEW = list(data, firstIndex, lastIndex, direction) { obs, index -> makeView(obs) } 21 | 22 | 23 | fun ViewFactoryNavigation.pagesEmbedded( 24 | dependency: DEPENDENCY, 25 | page: MutableObservableProperty, 26 | vararg pageGenerators: (DEPENDENCY) -> VIEW 27 | ) = pages(dependency, page, *pageGenerators.map { 28 | object : ViewGenerator { 29 | @Suppress("UNCHECKED_CAST") 30 | override fun generate(dependency: DEPENDENCY): VIEW = it(dependency) 31 | } 32 | }.toTypedArray()) -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/root/ViewFactoryRoot.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.root 2 | 3 | import com.lightningkite.koolui.views.ViewGenerator 4 | 5 | fun , VIEW> DEPENDENCY.contentRoot(viewGenerator: ViewGenerator): VIEW { 6 | return contentRoot(viewGenerator.generate(this)) 7 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/root/ViewFactoryRoot.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.root 2 | 3 | interface ViewFactoryRoot { 4 | 5 | /** 6 | * Wraps the given view in another view, if necessary for this view factory to function. 7 | * Some view factories need a certain root view to be accessible to add dialogs and such. 8 | */ 9 | fun contentRoot(view: VIEW): VIEW = view 10 | } -------------------------------------------------------------------------------- /koolui/src/commonMain/kotlin/com/lightningkite/koolui/views/web/ViewFactoryWeb.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.web 2 | 3 | import com.lightningkite.reacktive.property.ObservableProperty 4 | 5 | interface ViewFactoryWeb { 6 | 7 | /** 8 | * Shows a webpage within this view. 9 | * If the string starts with 'http://', it will be interpreted as a URL. 10 | * Otherwise, it will be interpreted as non-interactive HTML content. 11 | */ 12 | fun web( 13 | content: ObservableProperty 14 | ): VIEW 15 | } -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/NDate.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.lokalize.time.TimeConstants 4 | import com.lightningkite.lokalize.time.TimeStamp 5 | import platform.Foundation.NSDate 6 | import platform.Foundation.timeIntervalSince1970 7 | 8 | fun NSDate.toTimeStamp(): TimeStamp = TimeStamp(this.timeIntervalSince1970.times(1000).toLong()) 9 | fun TimeStamp.toNSDate(): NSDate = NSDate(timeIntervalSinceReferenceDate = (millisecondsSinceEpoch - TimeConstants.MS_PER_DAY * (365 * 31 + 8)).toDouble() / 1000.0) 10 | 11 | /* 12 | 13 | Leap years in 1970 - 2001 14 | 1972 15 | 1976 16 | 1980 17 | 1984 18 | 1988 19 | 1992 20 | 1996 21 | 2000 22 | 23 | */ 24 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/NSData.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import kotlinx.cinterop.* 4 | import platform.Foundation.NSData 5 | import platform.Foundation.NSMutableData 6 | import platform.Foundation.appendBytes 7 | 8 | fun NSData.toByteArray(): ByteArray { 9 | val data: CPointer = bytes!!.reinterpret() 10 | return ByteArray(length.toInt()) { index -> data[index] } 11 | } 12 | 13 | fun ByteArray.toNSData(): NSData = NSMutableData().apply { 14 | if (isEmpty()) return@apply 15 | this@toNSData.usePinned { 16 | appendBytes(it.addressOf(0), size.toULong()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | actual val UIPlatform.Companion.current: UIPlatform get() = UIPlatform.IOS -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/async/UI.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.Runnable 6 | import platform.darwin.dispatch_async 7 | import platform.darwin.dispatch_get_main_queue 8 | import kotlin.coroutines.CoroutineContext 9 | 10 | actual val Dispatchers.UI: CoroutineDispatcher get() = UIDispatcher 11 | 12 | private object UIDispatcher : CoroutineDispatcher() { 13 | override fun dispatch(context: CoroutineContext, block: Runnable) { 14 | dispatch_async(dispatch_get_main_queue()) { block.run() } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/image/Image.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | import com.lightningkite.koolui.toNSData 5 | import platform.UIKit.UIImage 6 | 7 | actual class Image(val image: UIImage?) { 8 | 9 | actual companion object { 10 | actual fun fromSvgString(svg: String): Image { 11 | return Image(null) 12 | } 13 | 14 | actual val blank: Image = Image(null) 15 | 16 | actual fun fromByteArray(byteArray: ByteArray): Image = Image(UIImage.imageWithData(byteArray.toNSData())) 17 | 18 | actual suspend fun fromUrlUnsafe(url: Uri): Image = fromUrlUnsafe(url.string) 19 | 20 | actual suspend fun fromUrlUnsafe(url: String): Image { 21 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.reacktive.property.ConstantObservableProperty 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | 6 | actual object PushNotifications { 7 | actual val token: ObservableProperty = ConstantObservableProperty(null) 8 | actual fun requestNotificationsPermission() { 9 | TODO() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | actual object Preferences : Iterable> { 4 | 5 | val cache: HashMap = HashMap() 6 | 7 | actual operator fun get(key: String): String? { 8 | return cache[key] 9 | } 10 | 11 | actual operator fun set(key: String, value: String?) { 12 | if (value == null) { 13 | cache.remove(key) 14 | } else { 15 | cache[key] = value 16 | } 17 | } 18 | 19 | actual override fun iterator(): Iterator> = cache.entries.asSequence().map { it.key to it.value }.iterator() 20 | 21 | actual fun clear() { 22 | cache.clear() 23 | } 24 | 25 | fun String.escapeKey(): String = replace(" ", "\\ ").escapeValue() 26 | fun String.escapeValue(): String = replace("\\", "\\\\").replace("\n", "\\n") 27 | fun String.unescapeValue(): String = replace("\\\\", "\\").replace("\\n", "\n") 28 | fun String.unescapeKey(): String = unescapeValue().replace("\\ ", " ") 29 | } 30 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.image.Image 5 | import com.lightningkite.koolui.toByteArray 6 | import kotlinx.coroutines.GlobalScope 7 | import kotlinx.coroutines.async 8 | import platform.Foundation.* 9 | 10 | actual object Resources { 11 | 12 | actual suspend fun getString( 13 | filename: String 14 | ): String { 15 | val path = NSBundle.mainBundle.pathForResource(filename.substringBeforeLast('.'), filename.substringAfterLast('.'))!! 16 | return NSString.stringWithContentsOfFile(path, NSUTF8StringEncoding, null)!! 17 | } 18 | 19 | actual suspend fun getByteArray( 20 | filename: String 21 | ): ByteArray { 22 | val path = NSBundle.mainBundle.pathForResource(filename.substringBeforeLast('.'), filename.substringAfterLast('.'))!! 23 | return NSData.dataWithContentsOfFile(path)!!.toByteArray() 24 | } 25 | 26 | actual suspend fun getImage(filename: String): Image = Image.fromByteArray(getByteArray(filename)) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/Anchors.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import platform.UIKit.* 4 | 5 | interface Anchors { 6 | val bottomAnchor: NSLayoutYAxisAnchor 7 | val centerXAnchor: NSLayoutXAxisAnchor 8 | val centerYAnchor: NSLayoutYAxisAnchor 9 | val heightAnchor: NSLayoutDimension 10 | val leadingAnchor: NSLayoutXAxisAnchor 11 | val leftAnchor: NSLayoutXAxisAnchor 12 | val rightAnchor: NSLayoutXAxisAnchor 13 | val topAnchor: NSLayoutYAxisAnchor 14 | val trailingAnchor: NSLayoutXAxisAnchor 15 | val widthAnchor: NSLayoutDimension 16 | } 17 | 18 | fun UIView.anchors(): Anchors = object : Anchors { 19 | override val bottomAnchor get() = this@anchors.bottomAnchor 20 | override val centerXAnchor get() = this@anchors.centerXAnchor 21 | override val centerYAnchor get() = this@anchors.centerYAnchor 22 | override val heightAnchor get() = this@anchors.heightAnchor 23 | override val leadingAnchor get() = this@anchors.leadingAnchor 24 | override val leftAnchor get() = this@anchors.leftAnchor 25 | override val rightAnchor get() = this@anchors.rightAnchor 26 | override val topAnchor get() = this@anchors.topAnchor 27 | override val trailingAnchor get() = this@anchors.trailingAnchor 28 | override val widthAnchor get() = this@anchors.widthAnchor 29 | } 30 | 31 | fun UILayoutGuide.anchors(): Anchors = object : Anchors { 32 | override val bottomAnchor get() = this@anchors.bottomAnchor 33 | override val centerXAnchor get() = this@anchors.centerXAnchor 34 | override val centerYAnchor get() = this@anchors.centerYAnchor 35 | override val heightAnchor get() = this@anchors.heightAnchor 36 | override val leadingAnchor get() = this@anchors.leadingAnchor 37 | override val leftAnchor get() = this@anchors.leftAnchor 38 | override val rightAnchor get() = this@anchors.rightAnchor 39 | override val topAnchor get() = this@anchors.topAnchor 40 | override val trailingAnchor get() = this@anchors.trailingAnchor 41 | override val widthAnchor get() = this@anchors.widthAnchor 42 | } 43 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/CGRect.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import kotlinx.cinterop.readValue 4 | import platform.CoreGraphics.CGRect 5 | import platform.CoreGraphics.CGRectZero 6 | 7 | val CGRect.Companion.zeroVal get() = CGRectZero.readValue() 8 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/ClosureSleeve.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.layout.ViewAdapter 4 | import kotlinx.cinterop.* 5 | import platform.Foundation.NSSelectorFromString 6 | import platform.UIKit.* 7 | import platform.darwin.NSObject 8 | import platform.darwin.NSUInteger 9 | import platform.objc.* 10 | import platform.posix.uintVar 11 | import kotlin.native.concurrent.ThreadLocal 12 | 13 | class ClosureSleeve(val closure: () -> Unit) : NSObject() { 14 | @ObjCAction 15 | fun runContainedClosure() = closure() 16 | } 17 | 18 | fun ViewAdapter.addAction(events: UIControlEvents, sleeve: NSObject) = addAction(this.view, events, sleeve) 19 | 20 | fun ViewAdapter<*, UIView>.addAction(view: UIControl, events: UIControlEvents, sleeve: NSObject) { 21 | // val pinned = sleeve.pin() 22 | view.addTarget(sleeve, NSSelectorFromString("runContainedClosure"), events) 23 | (this as? UIViewAdapter)?.holding?.set("event_$events", sleeve) 24 | } 25 | 26 | fun ViewAdapter.setDelegate(type: String, delegate: D): D { 27 | (this as? UIViewAdapter)?.holding?.set("delegate_$type", delegate) 28 | return delegate 29 | } 30 | 31 | fun ViewAdapter<*, UIView>.addGestureRecognizer(recognizer: UIGestureRecognizer, sleeve: NSObject) { 32 | recognizer.addTarget(sleeve, NSSelectorFromString("runContainedClosure")) 33 | viewAsBase.addGestureRecognizer(recognizer) 34 | (this as? UIViewAdapter)?.holding?.set("gestureRecognizer_${recognizer.name}", sleeve) 35 | } 36 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/Color.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.color.Color 4 | import platform.UIKit.UIColor 5 | 6 | val Color.ios: UIColor 7 | get() = UIColor.colorWithRed( 8 | red = red.toDouble(), 9 | green = green.toDouble(), 10 | blue = blue.toDouble(), 11 | alpha = alpha.toDouble() 12 | ) 13 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/IntrinsicLayoutDimensions.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.geometry.Measurement 4 | import com.lightningkite.koolui.layout.LeafDimensionLayouts 5 | import kotlinx.cinterop.useContents 6 | import platform.UIKit.UIView 7 | import platform.UIKit.intrinsicContentSize 8 | 9 | class IntrinsicLayoutDimensions(val view: UIView, val startMargin: Float = 8f, val endMargin: Float = 8f) : LeafDimensionLayouts() { 10 | override fun measureX(output: Measurement) { 11 | output.startMargin = startMargin 12 | output.endMargin = endMargin 13 | output.size = view.intrinsicContentSize.useContents { width }.toFloat() 14 | } 15 | 16 | override fun measureY(xSize: Float, output: Measurement) { 17 | output.startMargin = startMargin 18 | output.endMargin = endMargin 19 | output.size = view.intrinsicContentSize.useContents { height }.toFloat() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/LayoutRootView.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.layout.Layout 4 | import com.lightningkite.recktangle.Rectangle 5 | import kotlinx.cinterop.CValue 6 | import kotlinx.cinterop.ObjCAction 7 | import kotlinx.cinterop.useContents 8 | import platform.CoreGraphics.CGRect 9 | import platform.Foundation.NSCoder 10 | import platform.UIKit.UIView 11 | import platform.UIKit.addSubview 12 | import platform.UIKit.setNeedsLayout 13 | import platform.darwin.sel_registerName 14 | 15 | class LayoutRootView : UIView { 16 | @OverrideInit 17 | constructor(frame: CValue) : super(frame) 18 | 19 | @OverrideInit 20 | constructor(coder: NSCoder) : super(coder) 21 | 22 | lateinit var layout: Layout<*, UIView> 23 | 24 | fun setup(layout: Layout<*, UIView>) { 25 | this.layout = layout 26 | layout.isAttached.alwaysOn = true 27 | layout.x.onLayoutRequest = { 28 | setNeedsLayout() 29 | } 30 | layout.y.onLayoutRequest = { 31 | setNeedsLayout() 32 | } 33 | 34 | addSubview(layout.viewAsBase) 35 | } 36 | 37 | val rect = Rectangle() 38 | 39 | @ObjCAction 40 | fun layoutSubviews() { 41 | frame.useContents { 42 | rect.left = (origin.x).toFloat() 43 | rect.top = (origin.y).toFloat() 44 | rect.right = (origin.x + size.width).toFloat() 45 | rect.bottom = (origin.y + size.height).toFloat() 46 | } 47 | layout.layout(rect) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/TextSize.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.color.Color 4 | import com.lightningkite.koolui.concepts.TextSize 5 | import platform.UIKit.UIColor 6 | import platform.UIKit.UIFont 7 | import platform.UIKit.labelFontSize 8 | 9 | val TextSize.ios 10 | get() = when(this){ 11 | TextSize.Tiny -> UIFont.labelFontSize * .75 12 | TextSize.Body -> UIFont.labelFontSize 13 | TextSize.Subheader -> UIFont.labelFontSize * 1.25 14 | TextSize.Header -> UIFont.labelFontSize * 1.5 15 | } 16 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/UIControl.doneButton.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.toTimeStamp 4 | import platform.CoreGraphics.CGRect 5 | import platform.Foundation.NSSelectorFromString 6 | import platform.UIKit.* 7 | import platform.darwin.NSObject 8 | 9 | fun UIViewAdapter<*>.toolbarWithDoneButton(closureSleeveProvider: (() -> Unit) -> NSObject): UIToolbar { 10 | val toolbar = UIToolbar(CGRect.zeroVal) 11 | toolbar.barStyle = UIBarStyleDefault 12 | toolbar.translucent = true 13 | toolbar.sizeToFit() 14 | val sleeve = closureSleeveProvider { 15 | view.moveToNextOrResign() 16 | } 17 | holding["pickerDone"] = sleeve 18 | val doneButton = UIBarButtonItem(title = "Done", style = UIBarButtonItemStylePlain, target = sleeve, action = NSSelectorFromString("runContainedClosure")) 19 | toolbar.setItems(listOf( 20 | UIBarButtonItem(barButtonSystemItem = UIBarButtonSystemItemFlexibleSpace, target = null, action = null), 21 | doneButton 22 | )) 23 | return toolbar 24 | } 25 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/UILabelWithVerticalAlignment.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.geometry.Align 4 | import kotlinx.cinterop.CValue 5 | import kotlinx.cinterop.readValue 6 | import kotlinx.cinterop.useContents 7 | import platform.CoreGraphics.CGRect 8 | import platform.Foundation.NSCoder 9 | import platform.UIKit.NSStringDrawingUsesFontLeading 10 | import platform.UIKit.UILabel 11 | import platform.UIKit.boundingRectWithSize 12 | import kotlin.math.min 13 | 14 | class UILabelWithVerticalAlignment : UILabel { 15 | 16 | var verticalAlignment: Align = Align.Center 17 | 18 | @OverrideInit 19 | constructor(coder: NSCoder) : super(coder) 20 | 21 | @OverrideInit 22 | constructor(frame: CValue) : super(frame) 23 | 24 | override fun drawTextInRect(rect: CValue) { 25 | val attributedText = this.attributedText!! 26 | rect.useContents { 27 | val originalHeight = size.height 28 | size.height = min( 29 | attributedText.boundingRectWithSize(rect.useContents { size }.readValue(), NSStringDrawingUsesFontLeading, null).useContents { size.height }, 30 | numberOfLines * font.lineHeight 31 | ) 32 | origin.y = when (verticalAlignment) { 33 | Align.Start -> 0.0 34 | Align.Fill, Align.Center -> (originalHeight - size.height) / 2 35 | Align.End -> originalHeight - size.height 36 | } 37 | } 38 | super.drawTextInRect(rect) 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/UITextFieldDoneDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import com.lightningkite.koolui.layout.ViewAdapter 4 | import platform.UIKit.* 5 | import platform.darwin.NSObject 6 | 7 | class UITextFieldDoneDelegate : NSObject(), UITextFieldDelegateProtocol { 8 | override fun textFieldShouldReturn(textField: UITextField): Boolean { 9 | textField.moveToNextOrResign() 10 | return true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /koolui/src/iosMain/kotlin/com/lightningkite/koolui/views/ios/UIView.focus.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.ios 2 | 3 | import platform.UIKit.* 4 | 5 | private fun UIView.findNextChildFocus(afterIndex: Int = 0): UIView? { 6 | var index = afterIndex + 1 7 | while (index < subviews.size) { 8 | val sub = subviews[index] as UIView 9 | if (sub is UITextField) { 10 | return sub 11 | } else { 12 | sub.findNextChildFocus()?.let { subFocus -> 13 | return subFocus 14 | } 15 | } 16 | index += 1 17 | } 18 | return null 19 | } 20 | 21 | private fun UIView.findNextParentFocus(afterIndex: Int = 0): UIView? { 22 | findNextChildFocus(afterIndex = afterIndex)?.let { child -> 23 | return child 24 | } 25 | 26 | superview?.let { superview -> 27 | val myIndex = superview.subviews.indexOf(this) 28 | return superview.findNextParentFocus(afterIndex = myIndex) 29 | } 30 | 31 | return null 32 | } 33 | 34 | fun UIView.findNextFocus(afterIndex: Int = 0): UIView? { 35 | 36 | superview?.let { superview -> 37 | val myIndex = superview.subviews.indexOf(this) 38 | return superview.findNextParentFocus(afterIndex = myIndex) 39 | } 40 | 41 | return null 42 | } 43 | 44 | 45 | fun UIResponder.moveToNextOrResign() { 46 | (this as? UIView)?.findNextFocus()?.let { next -> 47 | next.becomeFirstResponder() 48 | } ?: run { 49 | this.resignFirstResponder() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/Location.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.Closeable 4 | import com.lightningkite.kommon.atomic.AtomicReference 5 | import com.lightningkite.lokalize.location.Geohash 6 | import com.lightningkite.reacktive.property.ObservableProperty 7 | import kotlinx.coroutines.GlobalScope 8 | import kotlinx.coroutines.async 9 | 10 | actual object Location { 11 | actual val available: Boolean 12 | get() = false 13 | 14 | actual suspend fun requestOnce(reason: String, accuracyBetterThanMeters: Double): LocationResult = throw UnsupportedOperationException() 15 | 16 | actual suspend fun requestOngoing(reason: String, accuracyBetterThanMeters: Double): ObservableProperty = throw UnsupportedOperationException() 17 | 18 | 19 | private var getAddressImplementationAtomic: AtomicReference String?> = AtomicReference { input -> 20 | null 21 | } 22 | actual var getAddressImplementation: suspend (Geohash) -> String? by getAddressImplementationAtomic 23 | actual suspend fun getAddress(from: Geohash): String? = getAddressImplementation(from) 24 | 25 | private var getGeohashImplementationAtomic: AtomicReference Geohash?> = AtomicReference { input -> 26 | null 27 | } 28 | actual var getGeohashImplementation: suspend (String) -> Geohash? by getGeohashImplementationAtomic 29 | actual suspend fun getGeohash(from: String): Geohash? = getGeohashImplementation(from) 30 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/MousePosition.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | import com.lightningkite.recktangle.Point 5 | import javafx.scene.input.MouseEvent 6 | import javafx.stage.Stage 7 | import java.beans.EventHandler 8 | 9 | object MousePosition { 10 | var stages = WeakHashMap() 11 | val point = Point() 12 | fun init(stage: Stage) { 13 | if (stages.containsKey(stage)) return 14 | stages[stage] = stage 15 | stage.addEventFilter(MouseEvent.MOUSE_MOVED) { 16 | stage.scene?.window?.let { window -> 17 | point.x = it.screenX.toFloat() 18 | point.y = it.screenY.toFloat() 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | actual val UIPlatform.Companion.current: UIPlatform get() = UIPlatform.JavaFX -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/async/UI.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual val Dispatchers.UI: CoroutineDispatcher get() = Dispatchers.Main 7 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/image/BufferedImageTranscoder.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | /** 4 | * This is a compilation of code snippets required to render SVG files in JavaFX using Batik. 5 | * See my full post on StackOverflow: http://stackoverflow.com/a/23894292/603003 6 | */ 7 | 8 | import java.awt.image.BufferedImage 9 | import org.apache.batik.transcoder.TranscoderException 10 | import org.apache.batik.transcoder.TranscoderOutput 11 | import org.apache.batik.transcoder.image.ImageTranscoder 12 | 13 | /** 14 | * Many thanks to bb-generation for sharing this code! 15 | * @author bb-generation 16 | * @link https://web.archive.org/web/20131215231214/http://bbgen.net/blog/2011/06/java-svg-to-bufferedimage/ 17 | * @license Unfortunately unknown, but using this code is probably categorized as "fair use" (because the code is in my opinion too simple to be licensed) 18 | */ 19 | class BufferedImageTranscoder : ImageTranscoder() { 20 | 21 | var bufferedImage: BufferedImage? = null 22 | private set 23 | 24 | override fun createImage(width: Int, height: Int): BufferedImage { 25 | return BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) 26 | } 27 | 28 | @Throws(TranscoderException::class) 29 | override fun writeImage(img: BufferedImage, to: TranscoderOutput?) { 30 | this.bufferedImage = img 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/image/Image.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | import com.lightningkite.recktangle.Point 5 | import javafx.scene.image.Image 6 | import javafx.scene.image.WritableImage 7 | import java.io.ByteArrayInputStream 8 | 9 | actual class Image(val get: (scale: Float, setSize: Point?) -> Image) { 10 | 11 | actual companion object { 12 | actual fun fromSvgString(svg: String): com.lightningkite.koolui.image.Image { 13 | return Image { scale, size -> 14 | if (size != null) SVGRenderer.render(svg, size) else SVGRenderer.render(svg, scale) 15 | } 16 | } 17 | 18 | actual fun fromByteArray(byteArray: ByteArray): com.lightningkite.koolui.image.Image { 19 | val backing = Image(ByteArrayInputStream(byteArray)) 20 | return Image { _, _ -> backing } 21 | } 22 | 23 | actual val blank: com.lightningkite.koolui.image.Image = Image { _, _ -> WritableImage(1, 1) } 24 | 25 | actual suspend fun fromUrlUnsafe(url: Uri): com.lightningkite.koolui.image.Image = com.lightningkite.koolui.image.Image { _, _ -> 26 | Image(url.string) 27 | } 28 | actual suspend fun fromUrlUnsafe(url: String): com.lightningkite.koolui.image.Image = fromUrlUnsafe(Uri(url)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/implementationhelpers/AnyDesiredMargins.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | 5 | data class DesiredMargins(val left: Float, val top: Float, val right: Float, val bottom: Float) { 6 | constructor(all: Float) : this(all, all, all, all) 7 | } 8 | 9 | val AnyDesiredMargins = WeakHashMap() 10 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/implementationhelpers/AnyLifecycles.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | 5 | 6 | val AnyLifecycles = WeakHashMap() 7 | 8 | //var Any.lifecycle 9 | // get() = AnyLifecycles.getOrPut(this){ TreeObservableProperty() } 10 | // set(value){ 11 | // AnyLifecycles[this] = value 12 | // } 13 | //fun Any.lifecycleChildOf(parent:Any){ 14 | // this.lifecycle = parent.lifecycle.child() 15 | //} 16 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.reacktive.property.ConstantObservableProperty 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | 6 | actual object PushNotifications { 7 | actual val token: ObservableProperty = ConstantObservableProperty(null) 8 | actual fun requestNotificationsPermission(){} 9 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | import java.io.File 4 | 5 | actual object Preferences : Iterable> { 6 | 7 | lateinit var file: File 8 | val cache: HashMap = HashMap() 9 | 10 | fun init(file: File){ 11 | this.file = file 12 | if(!file.exists()) { 13 | file.createNewFile() 14 | } 15 | //load from file 16 | file.readText().split('\n').forEach { 17 | val key = it.substringBefore(' ').unescapeKey() 18 | val value = it.substringAfter(' ').unescapeValue() 19 | cache[key] = value 20 | } 21 | } 22 | 23 | actual operator fun get(key: String): String? { 24 | return cache[key] 25 | } 26 | 27 | actual operator fun set(key: String, value: String?) { 28 | if(value == null){ 29 | cache.remove(key) 30 | } else { 31 | cache[key] = value 32 | } 33 | file.writeText(cache.entries.joinToString("\n"){ 34 | it.key.escapeKey() + " " + it.value.escapeValue() 35 | }) 36 | } 37 | 38 | actual override fun iterator(): Iterator> = cache.entries.asSequence().map { it.key to it.value }.iterator() 39 | 40 | actual fun clear(){ 41 | cache.clear() 42 | file.writeText("") 43 | } 44 | 45 | fun String.escapeKey():String = replace(" ", "\\ ").escapeValue() 46 | fun String.escapeValue():String = replace("\\", "\\\\").replace("\n", "\\n") 47 | fun String.unescapeValue(): String = replace("\\\\", "\\").replace("\\n", "\n") 48 | fun String.unescapeKey():String = unescapeValue().replace("\\ ", " ") 49 | } 50 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.image.Image 5 | import kotlinx.coroutines.GlobalScope 6 | import kotlinx.coroutines.async 7 | 8 | actual object Resources { 9 | 10 | lateinit var classLoader: ClassLoader 11 | 12 | actual suspend fun getString( 13 | filename: String 14 | ): String = GlobalScope.async { 15 | classLoader.getResourceAsStream(filename).use { it.reader().readText() } 16 | }.await() 17 | 18 | actual suspend fun getByteArray( 19 | filename: String 20 | ): ByteArray = GlobalScope.async { 21 | classLoader.getResourceAsStream(filename).use { it.readBytes() } 22 | }.await() 23 | 24 | actual suspend fun getImage(filename: String): Image = GlobalScope.async { 25 | Image.fromByteArray(classLoader.getResourceAsStream(filename).use { it.readBytes() }) 26 | }.await() 27 | 28 | } 29 | -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/views/EmptySelectionModel.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import javafx.collections.FXCollections 4 | import javafx.collections.ObservableList 5 | import javafx.scene.control.MultipleSelectionModel 6 | 7 | class NoSelectionModel : MultipleSelectionModel() { 8 | 9 | override fun getSelectedIndices(): ObservableList { 10 | return FXCollections.emptyObservableList() 11 | } 12 | 13 | override fun getSelectedItems(): ObservableList { 14 | return FXCollections.emptyObservableList() 15 | } 16 | 17 | override fun selectIndices(index: Int, vararg indices: Int) {} 18 | 19 | override fun selectAll() {} 20 | 21 | override fun selectFirst() {} 22 | 23 | override fun selectLast() {} 24 | 25 | override fun clearAndSelect(index: Int) {} 26 | 27 | override fun select(index: Int) {} 28 | 29 | override fun select(obj: T) {} 30 | 31 | override fun clearSelection(index: Int) {} 32 | 33 | override fun clearSelection() {} 34 | 35 | override fun isSelected(index: Int): Boolean { 36 | return false 37 | } 38 | 39 | override fun isEmpty(): Boolean { 40 | return true 41 | } 42 | 43 | override fun selectPrevious() {} 44 | 45 | override fun selectNext() {} 46 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/views/LayoutJavaFxViewFactory.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.koolui.color.ColorSet 4 | import com.lightningkite.koolui.color.Theme 5 | import com.lightningkite.koolui.concepts.Animation 6 | import com.lightningkite.koolui.layout.Layout 7 | import com.lightningkite.koolui.layout.LeafDimensionLayouts 8 | import com.lightningkite.koolui.layout.ViewAdapter 9 | import com.lightningkite.koolui.layout.views.LayoutVFRootAndDialogs 10 | import com.lightningkite.koolui.views.basic.LayoutJavaFxBasic 11 | import com.lightningkite.koolui.views.graphics.LayoutJavaFxGraphics 12 | import com.lightningkite.koolui.views.interactive.LayoutJavaFxInteractive 13 | import com.lightningkite.koolui.views.layout.LayoutJavaFxLayout 14 | import com.lightningkite.koolui.views.navigation.ViewFactoryNavigationDefault 15 | import com.lightningkite.reacktive.property.ObservableProperty 16 | import com.lightningkite.recktangle.Rectangle 17 | import javafx.scene.Node 18 | import javafx.scene.layout.Pane 19 | import javafx.scene.layout.StackPane 20 | 21 | /** 22 | * This is an example how how to set up your factory. 23 | * It is not recommended you use this directly - instead copy it and use the modules you want. 24 | */ 25 | class LayoutJavaFxViewFactory( 26 | theme: Theme, 27 | colorSet: ColorSet = theme.main, 28 | override val scale: Double = 1.0 29 | ) : ViewFactory>, 30 | HasScale, 31 | Themed by Themed.impl(theme, colorSet), 32 | LayoutJavaFxBasic /*ViewFactoryBasic*/, 33 | LayoutJavaFxInteractive /*ViewFactoryInteractive*/, 34 | LayoutJavaFxGraphics /*ViewFactoryGraphics*/, 35 | LayoutJavaFxLayout /*ViewFactoryLayout*/, 36 | ViewFactoryNavigationDefault> /*ViewFactoryNavigation*/, 37 | LayoutVFRootAndDialogs /*ViewFactoryDialogs*/, 38 | JavaFxLayoutWrapper /*ViewLayoutWrapper*/ { 39 | 40 | override var root: Layout<*, Node>? = null 41 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/views/Node.touch.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.reacktive.invokeAll 4 | import com.lightningkite.reacktive.property.update 5 | import javafx.scene.Node 6 | 7 | fun Node.setOnNewTouch(onNewTouch: (Touch)->Unit) { 8 | val touches = HashMap() 9 | setOnTouchPressed { event -> 10 | val entry = Touch.Impl() 11 | touches[event.touchPoint.id] = entry 12 | entry.position.value.x = event.touchPoint.x.toFloat() 13 | entry.position.value.y = event.touchPoint.y.toFloat() 14 | entry.position.update() 15 | } 16 | setOnTouchMoved { event -> 17 | val entry = touches[event.touchPoint.id] ?: return@setOnTouchMoved 18 | entry.position.value.x = event.touchPoint.x.toFloat() 19 | entry.position.value.y = event.touchPoint.y.toFloat() 20 | } 21 | this.setOnTouchReleased { event -> 22 | val entry = touches.remove(event.touchPoint.id) ?: return@setOnTouchReleased 23 | entry.position.value.x = event.touchPoint.x.toFloat() 24 | entry.position.value.y = event.touchPoint.y.toFloat() 25 | entry.position.update() 26 | entry.onRelease.invokeAll() 27 | } 28 | 29 | this.setOnMousePressed { event -> 30 | val entry = Touch.Impl() 31 | touches[0] = entry 32 | entry.position.value.x = event.x.toFloat() 33 | entry.position.value.y = event.y.toFloat() 34 | entry.position.update() 35 | } 36 | this.setOnMouseMoved { event -> 37 | val entry = touches[0] ?: return@setOnMouseMoved 38 | entry.position.value.x = event.x.toFloat() 39 | entry.position.value.y = event.y.toFloat() 40 | } 41 | this.setOnMouseReleased { event -> 42 | val entry = touches.remove(0) ?: return@setOnMouseReleased 43 | entry.position.value.x = event.x.toFloat() 44 | entry.position.value.y = event.y.toFloat() 45 | entry.position.update() 46 | entry.onRelease.invokeAll() 47 | } 48 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/views/ObservableList.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.reacktive.list.ObservableList 4 | import com.lightningkite.reacktive.list.ObservableListListenerSet 5 | import com.lightningkite.reacktive.list.lifecycle.bind 6 | import com.lightningkite.reacktive.property.ObservableProperty 7 | import javafx.collections.FXCollections 8 | 9 | 10 | fun ObservableList.asJavaFX( 11 | lifecycle: ObservableProperty 12 | ): javafx.collections.ObservableList { 13 | val backing = this 14 | val javafx = FXCollections.observableArrayList() 15 | 16 | backing.bindToJavaFX(lifecycle, javafx) 17 | 18 | return javafx 19 | } 20 | 21 | fun ObservableList.bindToJavaFX( 22 | lifecycle: ObservableProperty, 23 | javafx: javafx.collections.ObservableList 24 | ) { 25 | val backing = this 26 | 27 | lifecycle.bind(backing, ObservableListListenerSet( 28 | onAddListener = { item, position -> 29 | javafx.add(position, item) 30 | }, 31 | onRemoveListener = { item, position -> 32 | javafx.removeAt(position) 33 | }, 34 | onChangeListener = { old, item, position -> 35 | javafx[position] = item 36 | }, 37 | onMoveListener = { _, oldPosition, position -> 38 | val item = javafx[oldPosition] 39 | val swapWith = javafx[position] 40 | javafx[oldPosition] = swapWith 41 | javafx[position] = item 42 | }, 43 | onReplaceListener = { list -> 44 | javafx.setAll(list) 45 | } 46 | )) 47 | 48 | javafx.setAll(backing) 49 | lifecycle.add { 50 | if (it) { 51 | javafx.setAll(backing) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/views/TextSize.javafx.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.koolui.concepts.TextSize 4 | 5 | interface HasScale { 6 | val scale: Double 7 | 8 | val TextSize.javafx 9 | get() = when (this) { 10 | TextSize.Tiny -> 10.0 * scale 11 | TextSize.Body -> 14.0 * scale 12 | TextSize.Subheader -> 18.0 * scale 13 | TextSize.Header -> 24.0 * scale 14 | } 15 | } -------------------------------------------------------------------------------- /koolui/src/javafxMain/kotlin/com/lightningkite/koolui/views/graphics/LayoutJavaFxGraphics.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.graphics 2 | 3 | import com.lightningkite.koolui.canvas.Canvas 4 | import com.lightningkite.koolui.layout.Layout 5 | import com.lightningkite.koolui.layout.views.LayoutViewWrapper 6 | import com.lightningkite.koolui.layout.views.wrap 7 | import com.lightningkite.koolui.views.HasScale 8 | import com.lightningkite.reacktive.property.ObservableProperty 9 | import com.lightningkite.reacktive.property.lifecycle.bind 10 | import javafx.scene.Node 11 | 12 | interface LayoutJavaFxGraphics : ViewFactoryGraphics>, HasScale, LayoutViewWrapper { 13 | 14 | class SizedCanvas: javafx.scene.canvas.Canvas() { 15 | var onResize: ()->Unit = {} 16 | override fun isResizable(): Boolean = true 17 | override fun resize(width: Double, height: Double) { 18 | super.setWidth(width) 19 | super.setHeight(height) 20 | onResize() 21 | } 22 | } 23 | 24 | override fun canvas(draw: ObservableProperty Unit>): Layout<*, Node> { 25 | return wrap(SizedCanvas()) { lifecycle -> 26 | val c = JavaFXCanvas(this, scale) 27 | fun redraw(drawer: Canvas.()->Unit){ 28 | c.ctx.clearRect(0.0, 0.0, c.javaFxCanvas.width, c.javaFxCanvas.height) 29 | drawer(c) 30 | } 31 | onResize = { redraw(draw.value) } 32 | lifecycle.bind(draw){ 33 | redraw(it) 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/Align.ext.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.koolui.geometry.Align 4 | 5 | fun Align.toWeb() = when (this) { 6 | Align.Start -> "flex-start" 7 | Align.Center -> "center" 8 | Align.End -> "flex-end" 9 | Align.Fill -> "stretch" 10 | } -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/Lifecycle.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.koolui.implementationhelpers.AnyLifecycles 4 | import com.lightningkite.koolui.implementationhelpers.TreeObservableProperty 5 | import org.w3c.dom.HTMLElement 6 | 7 | 8 | var HTMLElement.lifecycle 9 | get() = AnyLifecycles.getOrPut(this) { TreeObservableProperty() } 10 | set(value) { 11 | AnyLifecycles[this] = value 12 | } -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/UIBuildingTools.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.exception.stackTraceString 4 | import org.w3c.dom.HTMLElement 5 | import kotlin.browser.document 6 | 7 | fun HTMLElement.appendLifecycled(other: HTMLElement) { 8 | other.lifecycle.parent = this.lifecycle 9 | appendChild(other) 10 | } 11 | 12 | fun HTMLElement.removeLifecycled(other: HTMLElement) { 13 | try { 14 | removeChild(other) 15 | other.lifecycle.parent = null 16 | } catch(e: Exception){ 17 | println(e.stackTraceString()) 18 | } 19 | } 20 | 21 | @Suppress("UNCHECKED_CAST") 22 | fun HTMLElement.appendLifecycled(name: String, setup: T.() -> Unit): T { 23 | val newNode = document.createElement(name).let { it as T }.apply(setup) 24 | newNode.lifecycle.parent = this.lifecycle 25 | appendChild(newNode) 26 | return newNode 27 | } 28 | 29 | @Suppress("UNCHECKED_CAST") 30 | fun makeElement(name: String, setup: T.() -> Unit): T = 31 | document.createElement(name).let { it as T }.apply(setup) 32 | 33 | @Suppress("UNCHECKED_CAST") 34 | fun makeElement(name: String): T = 35 | document.createElement(name).let { it as T } -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | actual val UIPlatform.Companion.current: UIPlatform get() = UIPlatform.Javascript -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/async/ui.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual val Dispatchers.UI: CoroutineDispatcher get() = Dispatchers.Default 7 | -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/image/Image.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.asInt8Array 4 | import com.lightningkite.kommon.string.Uri 5 | import org.w3c.dom.url.URL 6 | import org.w3c.files.Blob 7 | 8 | external fun encodeURI(string: String): String 9 | 10 | actual class Image(val url: String? = null, val data: ByteArray? = null) { 11 | 12 | actual companion object { 13 | actual fun fromSvgString(svg: String): Image = Image( 14 | url = "data:image/svg+xml;utf8,${svg.trim().replace("#", "%23").replace('\"', '\'').replace( 15 | "<", 16 | "%3C" 17 | ).replace(">", "%3E")}" 18 | ) 19 | 20 | actual fun fromByteArray(byteArray: ByteArray): Image { 21 | return Image(data = byteArray) 22 | } 23 | 24 | actual val blank: Image = Image("") 25 | 26 | actual suspend fun fromUrlUnsafe(url: Uri): Image = Image(url.string) 27 | actual suspend fun fromUrlUnsafe(url: String): Image = Image(url) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/implementationhelpers/AnyDesiredMargins.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | 5 | data class DesiredMargins(val left: Float, val top: Float, val right: Float, val bottom: Float) { 6 | constructor(all: Float) : this(all, all, all, all) 7 | } 8 | 9 | val AnyDesiredMargins = WeakHashMap() 10 | -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/implementationhelpers/AnyLifecycles.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.implementationhelpers 2 | 3 | import com.lightningkite.kommon.collection.WeakHashMap 4 | 5 | 6 | val AnyLifecycles = WeakHashMap() 7 | 8 | //var Any.lifecycle 9 | // get() = AnyLifecycles.getOrPut(this){ TreeObservableProperty() } 10 | // set(value){ 11 | // AnyLifecycles[this] = value 12 | // } 13 | //fun Any.lifecycleChildOf(parent:Any){ 14 | // this.lifecycle = parent.lifecycle.child() 15 | //} 16 | -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.reacktive.property.ConstantObservableProperty 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | 6 | actual object PushNotifications { 7 | actual val token: ObservableProperty = ConstantObservableProperty(null) 8 | actual fun requestNotificationsPermission(){} 9 | } -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | import org.w3c.dom.get 4 | import kotlin.browser.localStorage 5 | import kotlin.browser.window 6 | 7 | actual object Preferences : Iterable> { 8 | actual operator fun get(key: String): String? { 9 | return window.localStorage.getItem(key) 10 | } 11 | 12 | actual operator fun set(key: String, value: String?) { 13 | if(value == null) 14 | window.localStorage.removeItem(key) 15 | else 16 | window.localStorage.setItem(key, value) 17 | } 18 | 19 | actual fun clear(){ 20 | window.localStorage.clear() 21 | } 22 | 23 | actual override fun iterator(): Iterator> = window.localStorage.let{ storage -> 24 | return (0 until storage.length).asSequence().map { 25 | val key = storage.key(it)!! 26 | key to storage.getItem(key)!! 27 | }.iterator() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.image.Image 5 | import kotlin.browser.window 6 | 7 | actual object Resources { 8 | actual suspend fun getString( 9 | filename: String 10 | ): String = TODO() 11 | // HttpClient.callString(url = ApplicationAccess.appPath + "/resources/" + filename, method = HttpMethod.GET) 12 | 13 | actual suspend fun getByteArray( 14 | filename: String 15 | ): ByteArray = TODO() 16 | // HttpClient.callByteArray(url = ApplicationAccess.appPath + "/resources/" + filename, method = HttpMethod.GET) 17 | 18 | actual suspend fun getImage(filename: String): Image = Image("${window.location.protocol}//${window.location.host}/resources/$filename") 19 | } 20 | -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/views/Importance.toCssClass.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views 2 | 3 | import com.lightningkite.koolui.concepts.Importance 4 | 5 | 6 | fun Importance.toCssClass() = when (this) { 7 | Importance.Low -> "ImportanceLow" 8 | Importance.Normal -> "ImportanceNormal" 9 | Importance.High -> "ImportanceHigh" 10 | Importance.Danger -> "ImportanceDanger" 11 | } -------------------------------------------------------------------------------- /koolui/src/jsMain/kotlin/com/lightningkite/koolui/views/graphics/LayoutHtmlGraphics.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.graphics 2 | 3 | import com.lightningkite.koolui.canvas.Canvas 4 | import com.lightningkite.koolui.canvas.HtmlCanvas 5 | import com.lightningkite.koolui.layout.Layout 6 | import com.lightningkite.koolui.layout.views.LayoutViewWrapper 7 | import com.lightningkite.koolui.layout.views.intrinsicLayout 8 | import com.lightningkite.koolui.makeElement 9 | import com.lightningkite.reacktive.property.ObservableProperty 10 | import com.lightningkite.reacktive.property.lifecycle.bind 11 | import org.w3c.dom.HTMLCanvasElement 12 | import org.w3c.dom.HTMLElement 13 | 14 | interface LayoutHtmlGraphics : ViewFactoryGraphics>, LayoutViewWrapper { 15 | override fun canvas(draw: ObservableProperty Unit>): Layout<*, HTMLElement> { 16 | return intrinsicLayout(makeElement("canvas")) { layout -> 17 | val c = HtmlCanvas(this) 18 | layout.isAttached.bind(draw) { 19 | c.ctx.clearRect(0.0, 0.0, c.element.width.toDouble(), c.element.height.toDouble()) 20 | it(c) 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/ExternalAccess.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.string.MediaTypeAcceptWithDescription 4 | import com.lightningkite.kommon.string.MediaTypeWithDescription 5 | import com.lightningkite.kommon.string.Uri 6 | import com.lightningkite.reacktive.invokeAll 7 | 8 | actual object ExternalAccess { 9 | val uriOpened = ArrayList<(Uri)->Unit>() 10 | actual fun openUri(uri: Uri) { 11 | println("Opened $uri") 12 | uriOpened.invokeAll(uri) 13 | } 14 | 15 | val savedToFile = ArrayList<(ByteArray)->Unit>() 16 | actual fun saveToChosenFile(name: String, contentType: MediaTypeWithDescription, data: ByteArray, alwaysOpenDialog: Boolean, callback: (succeeded: Boolean) -> Unit) { 17 | println("Saved to file") 18 | savedToFile.invokeAll(data) 19 | } 20 | 21 | var dataToLoad: ByteArray? = null 22 | actual fun loadFromChosenFile(contentTypes: List, callback: (data: ByteArray?) -> Unit) { 23 | println("Load from file") 24 | dataToLoad?.let(callback) ?: callback(null) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/Location.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.Closeable 4 | import com.lightningkite.kommon.atomic.AtomicReference 5 | import com.lightningkite.kommon.exception.ForbiddenException 6 | import com.lightningkite.lokalize.location.Geohash 7 | import com.lightningkite.reacktive.property.ObservableProperty 8 | import com.lightningkite.reacktive.property.StandardObservableProperty 9 | 10 | actual object Location { 11 | actual val available: Boolean 12 | get() = true 13 | 14 | var mockLocation = StandardObservableProperty(LocationResult(Geohash(0))) 15 | var shouldReject = false 16 | 17 | actual suspend fun requestOnce(reason: String, accuracyBetterThanMeters: Double): LocationResult { 18 | if(shouldReject) 19 | throw ForbiddenException() 20 | else 21 | return mockLocation.value 22 | } 23 | 24 | actual suspend fun requestOngoing(reason: String, accuracyBetterThanMeters: Double): ObservableProperty { 25 | if(shouldReject) 26 | throw ForbiddenException() 27 | else 28 | return mockLocation 29 | } 30 | 31 | 32 | private var getAddressImplementationAtomic: AtomicReference String?> = AtomicReference { input -> 33 | null 34 | } 35 | actual var getAddressImplementation: suspend (Geohash) -> String? by getAddressImplementationAtomic 36 | actual suspend fun getAddress(from: Geohash): String? = getAddressImplementation(from) 37 | 38 | private var getGeohashImplementationAtomic: AtomicReference Geohash?> = AtomicReference { input -> 39 | null 40 | } 41 | actual var getGeohashImplementation: suspend (String) -> Geohash? by getGeohashImplementationAtomic 42 | actual suspend fun getGeohash(from: String): Geohash? = getGeohashImplementation(from) 43 | } -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | actual val UIPlatform.Companion.current: UIPlatform get() = UIPlatform.Virtual -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/async/UI.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual val Dispatchers.UI: CoroutineDispatcher get() = Dispatchers.Default 7 | -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/image/Image.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | 5 | actual class Image(val data: ByteArray) { 6 | 7 | actual companion object { 8 | actual fun fromSvgString(svg: String): Image { 9 | return Image(svg.toByteArray()) 10 | } 11 | 12 | actual fun fromByteArray(byteArray: ByteArray): Image { 13 | return Image(byteArray) 14 | } 15 | 16 | actual val blank: Image = Image(byteArrayOf()) 17 | 18 | actual suspend fun fromUrlUnsafe(url: Uri): Image = Image(url.string.toByteArray()) 19 | actual suspend fun fromUrlUnsafe(url: String): Image = fromUrlUnsafe(Uri(url)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.reacktive.property.ConstantObservableProperty 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | 6 | actual object PushNotifications { 7 | actual val token: ObservableProperty = ConstantObservableProperty(null) 8 | actual fun requestNotificationsPermission(){} 9 | } -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | import java.io.File 4 | 5 | actual object Preferences : Iterable> { 6 | 7 | lateinit var file: File 8 | val cache: HashMap = HashMap() 9 | 10 | fun init(file: File){ 11 | this.file = file 12 | if(!file.exists()) { 13 | file.createNewFile() 14 | } 15 | //load from file 16 | file.readText().split('\n').forEach { 17 | val key = it.substringBefore(' ').unescapeKey() 18 | val value = it.substringAfter(' ').unescapeValue() 19 | cache[key] = value 20 | } 21 | } 22 | 23 | actual operator fun get(key: String): String? { 24 | return cache[key] 25 | } 26 | 27 | actual operator fun set(key: String, value: String?) { 28 | if(value == null){ 29 | cache.remove(key) 30 | } else { 31 | cache[key] = value 32 | } 33 | file.writeText(cache.entries.joinToString("\n"){ 34 | it.key.escapeKey() + " " + it.value.escapeValue() 35 | }) 36 | } 37 | 38 | actual override fun iterator(): Iterator> = cache.entries.asSequence().map { it.key to it.value }.iterator() 39 | 40 | actual fun clear(){ 41 | cache.clear() 42 | file.writeText("") 43 | } 44 | 45 | fun String.escapeKey():String = replace(" ", "\\ ").escapeValue() 46 | fun String.escapeValue():String = replace("\\", "\\\\").replace("\n", "\\n") 47 | fun String.unescapeValue(): String = replace("\\\\", "\\").replace("\\n", "\n") 48 | fun String.unescapeKey():String = unescapeValue().replace("\\ ", " ") 49 | } 50 | -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.image.Image 5 | import kotlinx.coroutines.GlobalScope 6 | import kotlinx.coroutines.async 7 | 8 | actual object Resources { 9 | 10 | lateinit var classLoader: ClassLoader 11 | 12 | actual suspend fun getString( 13 | filename: String 14 | ): String = GlobalScope.async { 15 | classLoader.getResourceAsStream(filename).use { it.reader().readText() } 16 | }.await() 17 | 18 | actual suspend fun getByteArray( 19 | filename: String 20 | ): ByteArray = GlobalScope.async { 21 | classLoader.getResourceAsStream(filename).use { it.readBytes() } 22 | }.await() 23 | 24 | actual suspend fun getImage( 25 | filename: String 26 | ): Image = GlobalScope.async { 27 | Image.fromByteArray(classLoader.getResourceAsStream(filename).use { it.readBytes() }) 28 | }.await() 29 | 30 | } 31 | -------------------------------------------------------------------------------- /koolui/src/jvmVirtualMain/kotlin/com/lightningkite/koolui/views/virtual/AbstractViews.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.views.virtual 2 | 3 | import com.lightningkite.kommon.collection.treeWalkDepthSequence 4 | import com.lightningkite.koolui.implementationhelpers.TreeObservableProperty 5 | 6 | abstract class View { 7 | var attached = TreeObservableProperty() 8 | } 9 | 10 | abstract class ContainerView : View() { 11 | abstract fun listViews(): List 12 | 13 | fun recursiveViews(): Sequence = listViews().asSequence().treeWalkDepthSequence { 14 | (it as? ContainerView)?.listViews()?.asSequence() ?: sequenceOf() 15 | } 16 | } -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/ExternalAccess.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.string.MediaTypeAcceptWithDescription 4 | import com.lightningkite.kommon.string.MediaTypeWithDescription 5 | import com.lightningkite.kommon.string.Uri 6 | import com.lightningkite.reacktive.invokeAll 7 | 8 | actual object ExternalAccess { 9 | val uriOpened = ArrayList<(Uri)->Unit>() 10 | actual fun openUri(uri: Uri) { 11 | println("Opened $uri") 12 | uriOpened.invokeAll(uri) 13 | } 14 | 15 | val savedToFile = ArrayList<(ByteArray)->Unit>() 16 | actual fun saveToChosenFile(name: String, contentType: MediaTypeWithDescription, data: ByteArray, alwaysOpenDialog: Boolean, callback: (succeeded: Boolean) -> Unit) { 17 | println("Saved to file") 18 | savedToFile.invokeAll(data) 19 | } 20 | 21 | var dataToLoad: ByteArray? = null 22 | actual fun loadFromChosenFile(contentTypes: List, callback: (data: ByteArray?) -> Unit) { 23 | println("Load from file") 24 | dataToLoad?.let(callback) ?: callback(null) 25 | } 26 | 27 | } 28 | 29 | /*THE PLAN 30 | 31 | Tab/Reverse Tab navigates between elements 32 | ALT + Arrow keys also works for navigation 33 | You can use ALT + Letter/Number to jump to a certain element and activate it 34 | Type to enter information into the current thing 35 | 36 | RENDER SYSTEM 37 | 38 | The hierarchy has to be tracked for the selection model, so view classes it is 39 | 40 | interface View { 41 | val children: Sequence 42 | 43 | //available 44 | fun requestRender() 45 | 46 | //rendering 47 | fun render(renderThing: RenderThing, bounds: Rectangle) 48 | 49 | val interactive: Boolean 50 | fun interaction(keyStroke: KeyStroke): Boolean 51 | } 52 | 53 | */ -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/Location.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | import com.lightningkite.kommon.Closeable 4 | import com.lightningkite.kommon.atomic.AtomicReference 5 | import com.lightningkite.kommon.exception.ForbiddenException 6 | import com.lightningkite.lokalize.location.Geohash 7 | import com.lightningkite.reacktive.property.ObservableProperty 8 | import com.lightningkite.reacktive.property.StandardObservableProperty 9 | 10 | actual object Location { 11 | actual val available: Boolean 12 | get() = true 13 | 14 | var mockLocation = StandardObservableProperty(LocationResult(Geohash(0))) 15 | var shouldReject = false 16 | 17 | actual suspend fun requestOnce(reason: String, accuracyBetterThanMeters: Double): LocationResult { 18 | if(shouldReject) 19 | throw ForbiddenException() 20 | else 21 | return mockLocation.value 22 | } 23 | 24 | actual suspend fun requestOngoing(reason: String, accuracyBetterThanMeters: Double): ObservableProperty { 25 | if(shouldReject) 26 | throw ForbiddenException() 27 | else 28 | return mockLocation 29 | } 30 | 31 | 32 | private var getAddressImplementationAtomic: AtomicReference String?> = AtomicReference { input -> 33 | null 34 | } 35 | actual var getAddressImplementation: suspend (Geohash) -> String? by getAddressImplementationAtomic 36 | actual suspend fun getAddress(from: Geohash): String? = getAddressImplementation(from) 37 | 38 | private var getGeohashImplementationAtomic: AtomicReference Geohash?> = AtomicReference { input -> 39 | null 40 | } 41 | actual var getGeohashImplementation: suspend (String) -> Geohash? by getGeohashImplementationAtomic 42 | actual suspend fun getGeohash(from: String): Geohash? = getGeohashImplementation(from) 43 | } -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/UIPlatform.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui 2 | 3 | actual val UIPlatform.Companion.current: UIPlatform get() = UIPlatform.Lanterna -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/async/UI.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.async 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual val Dispatchers.UI: CoroutineDispatcher get() = Dispatchers.Default 7 | -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/image/Image.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.image 2 | 3 | import com.lightningkite.kommon.string.Uri 4 | 5 | actual class Image(val data: ByteArray) { 6 | 7 | actual companion object { 8 | actual fun fromSvgString(svg: String): Image { 9 | return Image(svg.toByteArray()) 10 | } 11 | 12 | actual fun fromByteArray(byteArray: ByteArray): Image { 13 | return Image(byteArray) 14 | } 15 | 16 | actual val blank: Image = Image(byteArrayOf()) 17 | 18 | actual suspend fun fromUrlUnsafe(url: Uri): Image = Image(url.string.toByteArray()) 19 | actual suspend fun fromUrlUnsafe(url: String): Image = fromUrlUnsafe(Uri(url)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/notification/PushNotifications.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.notification 2 | 3 | import com.lightningkite.reacktive.property.ConstantObservableProperty 4 | import com.lightningkite.reacktive.property.ObservableProperty 5 | 6 | actual object PushNotifications { 7 | actual val token: ObservableProperty = ConstantObservableProperty(null) 8 | actual fun requestNotificationsPermission(){} 9 | } -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/preferences/Preferences.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.preferences 2 | 3 | import java.io.File 4 | 5 | actual object Preferences : Iterable> { 6 | 7 | lateinit var file: File 8 | val cache: HashMap = HashMap() 9 | 10 | fun init(file: File){ 11 | this.file = file 12 | if(!file.exists()) { 13 | file.createNewFile() 14 | } 15 | //load from file 16 | file.readText().split('\n').forEach { 17 | val key = it.substringBefore(' ').unescapeKey() 18 | val value = it.substringAfter(' ').unescapeValue() 19 | cache[key] = value 20 | } 21 | } 22 | 23 | actual operator fun get(key: String): String? { 24 | return cache[key] 25 | } 26 | 27 | actual operator fun set(key: String, value: String?) { 28 | if(value == null){ 29 | cache.remove(key) 30 | } else { 31 | cache[key] = value 32 | } 33 | file.writeText(cache.entries.joinToString("\n"){ 34 | it.key.escapeKey() + " " + it.value.escapeValue() 35 | }) 36 | } 37 | 38 | actual override fun iterator(): Iterator> = cache.entries.asSequence().map { it.key to it.value }.iterator() 39 | 40 | actual fun clear(){ 41 | cache.clear() 42 | file.writeText("") 43 | } 44 | 45 | fun String.escapeKey():String = replace(" ", "\\ ").escapeValue() 46 | fun String.escapeValue():String = replace("\\", "\\\\").replace("\n", "\\n") 47 | fun String.unescapeValue(): String = replace("\\\\", "\\").replace("\\n", "\n") 48 | fun String.unescapeKey():String = unescapeValue().replace("\\ ", " ") 49 | } 50 | -------------------------------------------------------------------------------- /koolui/src/lanternaMain/kotlin/com/lightningkite/koolui/resources/Resources.kt: -------------------------------------------------------------------------------- 1 | package com.lightningkite.koolui.resources 2 | 3 | import com.lightningkite.koolui.ApplicationAccess 4 | import com.lightningkite.koolui.image.Image 5 | import kotlinx.coroutines.GlobalScope 6 | import kotlinx.coroutines.async 7 | 8 | actual object Resources { 9 | 10 | lateinit var classLoader: ClassLoader 11 | 12 | actual suspend fun getString( 13 | filename: String 14 | ): String = GlobalScope.async { 15 | classLoader.getResourceAsStream(filename).use { it.reader().readText() } 16 | }.await() 17 | 18 | actual suspend fun getByteArray( 19 | filename: String 20 | ): ByteArray = GlobalScope.async { 21 | classLoader.getResourceAsStream(filename).use { it.readBytes() } 22 | }.await() 23 | 24 | actual suspend fun getImage( 25 | filename: String 26 | ): Image = GlobalScope.async { 27 | Image.fromByteArray(classLoader.getResourceAsStream(filename).use { it.readBytes() }) 28 | }.await() 29 | 30 | } 31 | -------------------------------------------------------------------------------- /koolui/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 11 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /koolui/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /koolui/src/main/res/layout/horizontal_recycler_view_scrollbar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /koolui/src/main/res/layout/vertical_recycler_view_scrollbar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /koolui/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /koolui/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /koolui/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | view-android 3 | 4 | -------------------------------------------------------------------------------- /koolui/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 15 | 16 | 27 | 28 | 36 | 37 | -------------------------------------------------------------------------------- /koolui/versions.properties: -------------------------------------------------------------------------------- 1 | kommon=0.1.8 2 | koolui=0.1.6 3 | lokalize=0.1.6 4 | mirror=0.1.9 5 | mirror-form=0.1.9 6 | recktangle=0.1.6 7 | reacktive=0.1.6 8 | kabinet=0.0.1 9 | konvenience=0.0.8 10 | kotlin=1.3.50-eap-5 11 | kotlinx_serialization=0.11.1 12 | kotlinx_coroutines=1.2.2 13 | kotlinx_io=0.1.13-1.3.50-eap-5 14 | ktor=1.2.2 15 | # mirror-archive-api = 0.0.3 16 | # mirror-archive-nitrite = 0.0.3 17 | # mirror-archive-postgres = 0.0.3 18 | # mirror-archive-influxdb = 0.0.3 19 | # mirror-archive-redis = 0.0.3 20 | # mirror-form = 0.0.3 21 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | google() 6 | jcenter() 7 | mavenLocal() 8 | maven("https://dl.bintray.com/lightningkite/com.lightningkite.krosslin") 9 | maven("https://dl.bintray.com/kotlin/kotlin-eap") 10 | } 11 | resolutionStrategy { 12 | eachPlugin { 13 | if (requested.id.namespace == "com.android") { 14 | useModule("com.android.tools.build:gradle:${requested.version}") 15 | } 16 | } 17 | } 18 | } 19 | 20 | include("koolui") 21 | include("koolui-test") 22 | 23 | enableFeaturePreview("GRADLE_METADATA") 24 | -------------------------------------------------------------------------------- /versions.properties: -------------------------------------------------------------------------------- 1 | kommon=0.1.8 2 | koolui=0.1.6 3 | lokalize=0.1.6 4 | mirror=0.1.9 5 | mirror-form=0.1.9 6 | recktangle=0.1.6 7 | reacktive=0.1.6 8 | kabinet=0.0.1 9 | konvenience=0.0.8 10 | kotlin=1.3.50-eap-5 11 | kotlinx_serialization=0.11.1 12 | kotlinx_coroutines=1.2.2 13 | kotlinx_io=0.1.13-1.3.50-eap-5 14 | ktor=1.2.2 15 | # mirror-archive-api = 0.0.3 16 | # mirror-archive-nitrite = 0.0.3 17 | # mirror-archive-postgres = 0.0.3 18 | # mirror-archive-influxdb = 0.0.3 19 | # mirror-archive-redis = 0.0.3 20 | # mirror-form = 0.0.3 21 | --------------------------------------------------------------------------------