├── .gitignore ├── core ├── animation-frame │ └── animation-native.desktop.ts ├── application-settings │ └── application-settings.desktop.ts ├── application │ ├── application.desktop.d.ts │ └── application.desktop.ts ├── bundle-entry-points.desktop.ts ├── color │ └── color.desktop.ts ├── connectivity │ └── connectivity.desktop.ts ├── debugger │ └── devtools-elements.desktop.ts ├── file-system │ └── file-system-access.desktop.ts ├── fps-meter │ └── fps-native.desktop.ts ├── http │ └── http-request │ │ └── http-request.desktop.ts ├── image-source │ └── image-source.desktop.ts ├── package.json ├── platform │ └── platform.desktop.ts ├── text │ └── text.desktop.ts ├── timer │ └── timer.desktop.ts ├── tsconfig.json ├── ui │ ├── action-bar │ │ └── action-bar.desktop.ts │ ├── animation │ │ └── animation.desktop.ts │ ├── button │ │ └── button.desktop.ts │ ├── core │ │ └── view │ │ │ ├── view-helper │ │ │ └── view-helper.desktop.ts │ │ │ └── view.desktop.ts │ ├── dialogs │ │ └── dialogs.desktop.ts │ ├── editable-text-base │ │ └── editable-text-base.desktop.ts │ ├── frame │ │ └── frame.desktop.ts │ ├── gestures │ │ └── gestures.desktop.ts │ ├── label │ │ └── label.desktop.ts │ ├── layouts │ │ ├── grid-layout │ │ │ └── grid-layout.desktop.ts │ │ ├── layout-base.desktop.ts │ │ └── stack-layout │ │ │ └── stack-layout.desktop.ts │ ├── page │ │ └── page.desktop.ts │ ├── progress │ │ └── progress.desktop.ts │ ├── scroll-view │ │ └── scroll-view.desktop.ts │ ├── styling │ │ ├── background.desktop.ts │ │ └── font.desktop.ts │ ├── text-base │ │ └── text-base.desktop.ts │ ├── text-field │ │ └── text-field.desktop.ts │ └── text-view │ │ └── text-view.desktop.ts └── utils │ ├── layout-helper │ └── layout-helper.desktop.ts │ ├── mainthread-helper.desktop.ts │ ├── utils.desktop.d.ts │ └── utils.desktop.ts ├── index.ts ├── package-lock.json ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # NativeScript 2 | hooks/ 3 | node_modules/ 4 | platforms/ 5 | 6 | # NativeScript Template 7 | *.js.map 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # General 17 | .DS_Store 18 | .AppleDouble 19 | .LSOverride 20 | .idea 21 | .cloud 22 | .project 23 | tmp/ 24 | typings/ 25 | 26 | # Visual Studio Code 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | 33 | # Idea based editors 34 | .idea/* 35 | -------------------------------------------------------------------------------- /core/animation-frame/animation-native.desktop.ts: -------------------------------------------------------------------------------- 1 | export function getTimeInFrameBase(): number { 2 | return 1; 3 | } 4 | -------------------------------------------------------------------------------- /core/application-settings/application-settings.desktop.ts: -------------------------------------------------------------------------------- 1 | import * as Common from "@nativescript/core/application-settings/application-settings-common"; 2 | 3 | import * as utils from "@nativescript/core/utils/utils"; 4 | 5 | // const userDefaults = NSUserDefaults.standardUserDefaults; 6 | 7 | export function hasKey(key: string): boolean { 8 | Common.checkKey(key); 9 | 10 | return false; //userDefaults.objectForKey(key) !== null; 11 | } 12 | 13 | // utils.ios.getters 14 | export function getBoolean(key: string, defaultValue?: boolean): boolean { 15 | Common.checkKey(key); 16 | if (hasKey(key)) { 17 | return false; //userDefaults.boolForKey(key); 18 | } 19 | 20 | return defaultValue; 21 | } 22 | 23 | export function getString(key: string, defaultValue?: string): string { 24 | Common.checkKey(key); 25 | if (hasKey(key)) { 26 | return ""; //userDefaults.stringForKey(key); 27 | } 28 | 29 | return defaultValue; 30 | } 31 | 32 | export function getNumber(key: string, defaultValue?: number): number { 33 | Common.checkKey(key); 34 | if (hasKey(key)) { 35 | return 0; //userDefaults.doubleForKey(key); 36 | } 37 | 38 | return defaultValue; 39 | } 40 | 41 | // setters 42 | export function setBoolean(key: string, value: boolean): void { 43 | Common.checkKey(key); 44 | Common.ensureValidValue(value, "boolean"); 45 | // userDefaults.setBoolForKey(value, key); 46 | } 47 | 48 | export function setString(key: string, value: string): void { 49 | Common.checkKey(key); 50 | Common.ensureValidValue(value, "string"); 51 | // userDefaults.setObjectForKey(value, key); 52 | } 53 | 54 | export function setNumber(key: string, value: number): void { 55 | Common.checkKey(key); 56 | Common.ensureValidValue(value, "number"); 57 | // userDefaults.setDoubleForKey(value, key); 58 | } 59 | 60 | export function remove(key: string): void { 61 | Common.checkKey(key); 62 | // userDefaults.removeObjectForKey(key); 63 | } 64 | 65 | export function clear(): void { 66 | // userDefaults.removePersistentDomainForName(NSBundle.mainBundle.bundleIdentifier); 67 | } 68 | 69 | export function flush(): boolean { 70 | return true; // userDefaults.synchronize(); 71 | } 72 | 73 | export function getAllKeys(): Array { 74 | return []; // utils.ios.collections.nsArrayToJSArray(userDefaults.dictionaryRepresentation().allKeys); 75 | } 76 | -------------------------------------------------------------------------------- /core/application/application.desktop.d.ts: -------------------------------------------------------------------------------- 1 | export let desktop: DesktopApplication; 2 | 3 | export interface DesktopApplication { 4 | /** 5 | * Gets or sets the orientation of the application. 6 | * Available values: "portrait", "landscape", "unknown". 7 | */ 8 | orientation: "portrait" | "landscape" | "unknown"; 9 | 10 | /** 11 | * Gets the system appearance. 12 | * Available values: "dark", "light", null. 13 | * Null for iOS <= 11. 14 | */ 15 | systemAppearance: "dark" | "light" | null; 16 | 17 | /** 18 | * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html). 19 | */ 20 | nativeApp: any /* UIApplication */; 21 | } 22 | -------------------------------------------------------------------------------- /core/application/application.desktop.ts: -------------------------------------------------------------------------------- 1 | // Definitions. 2 | import { 3 | ApplicationEventData, 4 | CssChangedEventData, 5 | OrientationChangedEventData 6 | } from "@nativescript/core/application"; 7 | 8 | import { FrameBase } from "@nativescript/core/ui/frame/frame-common"; 9 | 10 | import { 11 | DesktopApplication as DesktopApplicationDefinition, 12 | } from "./application.desktop.d"; 13 | 14 | import { 15 | displayedEvent, 16 | getCssFileName, 17 | hasLaunched, 18 | hasListeners, 19 | launchEvent, 20 | livesync, 21 | lowMemoryEvent, 22 | notify, 23 | Observable, 24 | on, 25 | orientationChanged, 26 | orientationChangedEvent, 27 | setApplication, 28 | suspendEvent 29 | } from "@nativescript/core/application/application-common"; 30 | 31 | import { 32 | QApplication, QMainWindow 33 | } from "@nodegui/nodegui"; 34 | 35 | import { 36 | CSSSource, loadAppCSS 37 | } from "@nativescript/core/ui/styling/style-scope"; 38 | 39 | import { profile } from "@nativescript/core/profiling"; 40 | 41 | // First reexport so that app module is initialized. 42 | export * from "@nativescript/core/application/application-common"; 43 | 44 | // Types. 45 | import { Frame, NavigationEntry, View } from "@nativescript/core/ui/frame"; 46 | import { Builder } from "@nativescript/core/ui/builder"; 47 | 48 | export class DesktopApplication extends Observable implements DesktopApplicationDefinition { 49 | private _orientation: "portrait" | "landscape" | "unknown"; 50 | private _rootView: View = null; 51 | public systemAppearance: "dark" | "light" | null; 52 | public paused: boolean; 53 | public nativeApp: QApplication; 54 | public mainWindow: QMainWindow; 55 | public packageName: string; 56 | 57 | public init(nativeApp: any) { 58 | if (this.nativeApp === nativeApp) { 59 | return; 60 | } 61 | 62 | if (this.nativeApp) { 63 | throw new Error("application.android already initialized."); 64 | } 65 | 66 | if (!this._rootView) { 67 | // try to navigate to the mainEntry (if specified) 68 | if (mainEntry) { 69 | if (createRootFrame.value) { 70 | const frame = this._rootView = new Frame(); 71 | frame.navigate(mainEntry); 72 | } else { 73 | this._rootView = Builder.createViewFromEntry(mainEntry); 74 | } 75 | } else { 76 | // TODO: Throw an exception? 77 | throw new Error("A Frame must be used to navigate to a Page."); 78 | } 79 | } 80 | 81 | this.nativeApp = nativeApp; 82 | 83 | if (!this._rootView.isLoaded) { 84 | (this._rootView).callLoaded(); 85 | } 86 | 87 | this.mainWindow = new QMainWindow(); 88 | this.mainWindow.setMinimumSize(640, 480); 89 | this.mainWindow.setCentralWidget((this._rootView).desktop); 90 | this.mainWindow.setObjectName("mainWindow"); 91 | 92 | this.mainWindow.show(); 93 | 94 | notify({ 95 | eventName: launchEvent, 96 | object: this 97 | }); 98 | 99 | return this._rootView; 100 | } 101 | 102 | get rootView(): View { 103 | return this._rootView; 104 | } 105 | 106 | get orientation(): "portrait" | "landscape" | "unknown" { 107 | if (!this._orientation) { 108 | // const resources = this.context.getResources(); 109 | // const configuration = resources.getConfiguration(); 110 | // const orientation = configuration.orientation; 111 | 112 | // this._orientation = getOrientationValue(orientation); 113 | } 114 | 115 | return this._orientation; 116 | } 117 | 118 | set orientation(value: "portrait" | "landscape" | "unknown") { 119 | this._orientation = value; 120 | } 121 | } 122 | 123 | const desktopApp = new DesktopApplication(); 124 | export { desktopApp as desktop }; 125 | 126 | setApplication(desktopApp); 127 | 128 | let mainEntry: NavigationEntry; 129 | let started = false; 130 | const createRootFrame = { value: true }; 131 | let css; 132 | 133 | export function _start(entry?: NavigationEntry | string) { 134 | if (started) { 135 | throw new Error("Application is already started."); 136 | } 137 | 138 | started = true; 139 | mainEntry = typeof entry === "string" ? { moduleName: entry } : entry; 140 | if (!desktopApp.nativeApp) { 141 | desktopApp.init(getNativeApplication()); 142 | } 143 | } 144 | 145 | export function _shouldCreateRootFrame(): boolean { 146 | return createRootFrame.value; 147 | } 148 | 149 | export function run(entry?: NavigationEntry | string) { 150 | createRootFrame.value = false; 151 | _start(entry); 152 | } 153 | 154 | export function addCss(cssText: string): void { 155 | notify({eventName: "cssChanged", object: desktopApp, cssText: cssText}); 156 | const rootView = getRootView(); 157 | if (rootView) { 158 | rootView._onCssStateChange(); 159 | } 160 | } 161 | 162 | const CALLBACKS = "_callbacks"; 163 | 164 | export function _resetRootView(entry?: NavigationEntry | string) { 165 | // const activity = desktopApp.foregroundActivity; 166 | // if (!activity) { 167 | // throw new Error("Cannot find android activity."); 168 | // } 169 | 170 | createRootFrame.value = false; 171 | mainEntry = typeof entry === "string" ? { moduleName: entry } : entry; 172 | // const callbacks: AndroidActivityCallbacks = activity[CALLBACKS]; 173 | // if (!callbacks) { 174 | // throw new Error("Cannot find android activity callbacks."); 175 | // } 176 | // callbacks.resetActivityContent(activity); 177 | } 178 | 179 | export function getMainEntry() { 180 | return mainEntry; 181 | } 182 | 183 | export function getRootView(): View { 184 | // // Use start activity as a backup when foregroundActivity is still not set 185 | // // in cases when we are getting the root view before activity.onResumed event is fired 186 | // const activity = desktopApp.foregroundActivity || desktopApp.startActivity; 187 | // if (!activity) { 188 | // return undefined; 189 | // } 190 | // const callbacks: AndroidActivityCallbacks = activity[CALLBACKS]; 191 | // 192 | if (desktopApp.rootView) { 193 | return desktopApp.rootView; 194 | } 195 | } 196 | 197 | export function getNativeApplication(): QApplication { 198 | // Try getting it from module - check whether application.android.init has been explicitly called 199 | let nativeApp = desktopApp.nativeApp; 200 | if (!nativeApp) { 201 | nativeApp = QApplication.instance(); 202 | 203 | // we cannot work without having the app instance 204 | if (!nativeApp) { 205 | throw new Error("Failed to retrieve native QMainWindow object."); 206 | } 207 | } 208 | 209 | return nativeApp; 210 | } 211 | 212 | export function orientation(): "portrait" | "landscape" | "unknown" { 213 | return desktopApp.orientation; 214 | } 215 | 216 | on(orientationChangedEvent, (args: OrientationChangedEventData) => { 217 | const rootView = getRootView(); 218 | if (rootView) { 219 | orientationChanged(rootView, args.newValue); 220 | } 221 | }); 222 | 223 | global.__onLiveSync = function __onLiveSync(context?: ModuleContext) { 224 | if (desktopApp && desktopApp.paused) { 225 | return; 226 | } 227 | 228 | const rootView = getRootView(); 229 | livesync(rootView, context); 230 | }; 231 | 232 | function getOrientationValue(orientation: number): any { 233 | // switch (orientation) { 234 | // case android.content.res.Configuration.ORIENTATION_LANDSCAPE: 235 | // return "landscape"; 236 | // case android.content.res.Configuration.ORIENTATION_PORTRAIT: 237 | // return "portrait"; 238 | // default: 239 | // return "unknown"; 240 | // } 241 | } 242 | -------------------------------------------------------------------------------- /core/bundle-entry-points.desktop.ts: -------------------------------------------------------------------------------- 1 | if (global.TNS_WEBPACK) { 2 | require("@nativescript/core/globals"); 3 | require("@nativescript/core/utils/utils"); 4 | 5 | // Register "dynamically" loaded module that need to be resolved by the 6 | // XML/component builders. 7 | 8 | global.registerModule("text/formatted-string", () => require("@nativescript/core/text/formatted-string")); 9 | global.registerModule("text/span", () => require("@nativescript/core/text/span")); 10 | global.registerModule("ui/text-base/formatted-string", () => require("@nativescript/core/ui/text-base/formatted-string")); 11 | global.registerModule("ui/text-base/span", () => require("@nativescript/core/ui/text-base/span")); 12 | global.registerModule("ui/action-bar", () => require("@nativescript/core/ui/action-bar")); 13 | global.registerModule("ui/button", () => require("@nativescript/core/ui/button")); 14 | global.registerModule("ui/content-view", () => require("@nativescript/core/ui/content-view")); 15 | global.registerModule("ui/frame", () => require("@nativescript/core/ui/frame")); 16 | global.registerModule("ui/label", () => require("@nativescript/core/ui/label")); 17 | global.registerModule("ui/layouts/grid-layout", () => require("@nativescript/core/ui/layouts/grid-layout")); 18 | global.registerModule("ui/layouts/stack-layout", () => require("@nativescript/core/ui/layouts/stack-layout")); 19 | global.registerModule("ui/page", () => require("@nativescript/core/ui/page")); 20 | global.registerModule("ui/progress", () => require("@nativescript/core/ui/progress")); 21 | global.registerModule("ui/repeater", () => require("@nativescript/core/ui/repeater")); 22 | global.registerModule("ui/scroll-view", () => require("@nativescript/core/ui/scroll-view")); 23 | global.registerModule("ui/text-field", () => require("@nativescript/core/ui/text-field")); 24 | global.registerModule("ui/text-view", () => require("@nativescript/core/ui/text-view")); 25 | } 26 | -------------------------------------------------------------------------------- /core/color/color.desktop.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nativescript/core/color/color-common"; 2 | 3 | export class Color extends common.Color { 4 | get desktop(): string { 5 | return super.hex; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /core/connectivity/connectivity.desktop.ts: -------------------------------------------------------------------------------- 1 | export enum connectionType { 2 | none = 0, 3 | wifi = 1, 4 | mobile = 2, 5 | } 6 | 7 | // // Get Connection Type 8 | // declare const sockaddr; 9 | // function _createReachability(host?: string): any { 10 | // if (host) { 11 | // return SCNetworkReachabilityCreateWithName(null, host); 12 | // } else { 13 | // const zeroAddress = new interop.Reference(sockaddr, { 14 | // sa_len: 16, 15 | // sa_family: 2 16 | // }); 17 | // 18 | // return SCNetworkReachabilityCreateWithAddress(null, zeroAddress); 19 | // } 20 | // } 21 | // 22 | // function _getReachabilityFlags(host?: string): number { 23 | // const reachability = _createReachability(host); 24 | // const flagsRef = new interop.Reference(); 25 | // const gotFlags = SCNetworkReachabilityGetFlags(reachability, flagsRef); 26 | // if (!gotFlags) { 27 | // return null; 28 | // } 29 | // 30 | // return flagsRef.value; 31 | // } 32 | // 33 | // function _getConnectionType(host?: string): number { 34 | // const flags = _getReachabilityFlags(host); 35 | // 36 | // return _getConnectionTypeFromFlags(flags); 37 | // } 38 | // 39 | // function _getConnectionTypeFromFlags(flags: number): number { 40 | // if (!flags) { 41 | // return connectionType.none; 42 | // } 43 | // 44 | // const isReachable = flags & SCNetworkReachabilityFlags.kSCNetworkReachabilityFlagsReachable; 45 | // const connectionRequired = flags & SCNetworkReachabilityFlags.kSCNetworkReachabilityFlagsConnectionRequired; 46 | // if (!isReachable || connectionRequired) { 47 | // return connectionType.none; 48 | // } 49 | // 50 | // const isWWAN = flags & SCNetworkReachabilityFlags.kSCNetworkReachabilityFlagsIsWWAN; 51 | // if (isWWAN) { 52 | // return connectionType.mobile; 53 | // } 54 | // 55 | // return connectionType.wifi; 56 | // } 57 | // 58 | export function getConnectionType(): number { 59 | // return _getConnectionType(); 60 | return 0; 61 | } 62 | 63 | // // Start/Stop Monitoring 64 | // function _reachabilityCallback(target: any, flags: number, info: any) { 65 | // if (_connectionTypeChangedCallback) { 66 | // const newConnectionType = _getConnectionTypeFromFlags(flags); 67 | // _connectionTypeChangedCallback(newConnectionType); 68 | // } 69 | // } 70 | // 71 | // const _reachabilityCallbackFunctionRef = new interop.FunctionReference(_reachabilityCallback); 72 | // 73 | // let _monitorReachabilityRef: any; 74 | // let _connectionTypeChangedCallback: (newConnectionType: number) => void; 75 | // 76 | export function startMonitoring(connectionTypeChangedCallback: (newConnectionType: number) => void): void { 77 | // if (!_monitorReachabilityRef) { 78 | // _monitorReachabilityRef = _createReachability(); 79 | // _connectionTypeChangedCallback = zonedCallback(connectionTypeChangedCallback); 80 | // SCNetworkReachabilitySetCallback(_monitorReachabilityRef, _reachabilityCallbackFunctionRef, null); 81 | // SCNetworkReachabilityScheduleWithRunLoop(_monitorReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 82 | // _connectionTypeChangedCallback(_getConnectionType()); 83 | // } 84 | } 85 | 86 | export function stopMonitoring(): void { 87 | // if (_monitorReachabilityRef) { 88 | // SCNetworkReachabilityUnscheduleFromRunLoop(_monitorReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 89 | // _monitorReachabilityRef = undefined; 90 | // _connectionTypeChangedCallback = undefined; 91 | // } 92 | } 93 | -------------------------------------------------------------------------------- /core/debugger/devtools-elements.desktop.ts: -------------------------------------------------------------------------------- 1 | import { InspectorEvents, InspectorCommands } from "@nativescript/core/debugger/devtools-elements"; 2 | import { getDocument, getComputedStylesForNode, removeNode, setAttributeAsText } from "@nativescript/core/debugger/devtools-elements.common"; 3 | //import { registerInspectorEvents, DOMNode } from "@nativescript/core/debugger/dom-node"; 4 | 5 | export function attachDOMInspectorEventCallbacks(DOMDomainFrontend: InspectorEvents) { 6 | // registerInspectorEvents(DOMDomainFrontend); 7 | // 8 | // const originalChildNodeInserted: (parentId: number, lastId: number, node: string | DOMNode) => void = DOMDomainFrontend.childNodeInserted; 9 | // 10 | // DOMDomainFrontend.childNodeInserted = (parentId: number, lastId: number, node: DOMNode) => { 11 | // originalChildNodeInserted(parentId, lastId, node.toObject()); 12 | // }; 13 | } 14 | 15 | export function attachDOMInspectorCommandCallbacks(DOMDomainBackend: InspectorCommands) { 16 | // DOMDomainBackend.getDocument = getDocument; 17 | // DOMDomainBackend.removeNode = removeNode; 18 | // DOMDomainBackend.setAttributeAsText = setAttributeAsText; 19 | } 20 | 21 | export function attachCSSInspectorCommandCallbacks(CSSDomainBackend: InspectorCommands) { 22 | // CSSDomainBackend.getComputedStylesForNode = getComputedStylesForNode; 23 | } 24 | -------------------------------------------------------------------------------- /core/file-system/file-system-access.desktop.ts: -------------------------------------------------------------------------------- 1 | import { encoding as textEncoding } from "../text/text.desktop"; 2 | import { desktop } from "../utils/utils.desktop"; 3 | 4 | // TODO: Implement all the APIs receiving callback using async blocks 5 | // TODO: Check whether we need try/catch blocks for the iOS implementation 6 | export class FileSystemAccess { 7 | 8 | public getLastModified(path: string): Date | any { 9 | // const fileManager = NSFileManager.defaultManager; 10 | // const attributes = fileManager.attributesOfItemAtPathError(path); 11 | // 12 | // if (attributes) { 13 | // return attributes.objectForKey("NSFileModificationDate"); 14 | // } else { 15 | // return new Date(); 16 | // } 17 | } 18 | 19 | public getFileSize(path: string): number | any { 20 | // const fileManager = NSFileManager.defaultManager; 21 | // const attributes = fileManager.attributesOfItemAtPathError(path); 22 | // if (attributes) { 23 | // return attributes.objectForKey("NSFileSize"); 24 | // } else { 25 | // return 0; 26 | // } 27 | } 28 | 29 | public getParent(path: string, onError?: (error: any) => any): { path: string; name: string } | any { 30 | // try { 31 | // const fileManager = NSFileManager.defaultManager; 32 | // const nsString = NSString.stringWithString(path); 33 | // 34 | // const parentPath = nsString.stringByDeletingLastPathComponent; 35 | // const name = fileManager.displayNameAtPath(parentPath); 36 | // 37 | // return { 38 | // path: parentPath.toString(), 39 | // name: name 40 | // }; 41 | // } catch (exception) { 42 | // if (onError) { 43 | // onError(exception); 44 | // } 45 | // 46 | // return undefined; 47 | // } 48 | } 49 | 50 | public getFile(path: string, onError?: (error: any) => any): { path: string; name: string; extension: string } | any { 51 | // try { 52 | // const fileManager = NSFileManager.defaultManager; 53 | // const exists = fileManager.fileExistsAtPath(path); 54 | // 55 | // if (!exists) { 56 | // const parentPath = this.getParent(path, onError).path; 57 | // if (!fileManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(parentPath, true, null) 58 | // || !fileManager.createFileAtPathContentsAttributes(path, null, null)) { 59 | // if (onError) { 60 | // onError(new Error("Failed to create file at path '" + path + "'")); 61 | // } 62 | // 63 | // return undefined; 64 | // } 65 | // } 66 | // 67 | // const fileName = fileManager.displayNameAtPath(path); 68 | // 69 | // return { 70 | // path: path, 71 | // name: fileName, 72 | // extension: this.getFileExtension(path) 73 | // }; 74 | // } catch (exception) { 75 | // if (onError) { 76 | // onError(exception); 77 | // } 78 | // 79 | // return undefined; 80 | // } 81 | } 82 | 83 | public getFolder(path: string, onError?: (error: any) => any): { path: string; name: string } | any { 84 | // try { 85 | // const fileManager = NSFileManager.defaultManager; 86 | // const exists = this.folderExists(path); 87 | // 88 | // if (!exists) { 89 | // try { 90 | // fileManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(path, true, null); 91 | // } 92 | // catch (ex) { 93 | // if (onError) { 94 | // onError(new Error("Failed to create folder at path '" + path + "': " + ex)); 95 | // } 96 | // 97 | // return undefined; 98 | // } 99 | // } 100 | // 101 | // const dirName = fileManager.displayNameAtPath(path); 102 | // 103 | // return { 104 | // path: path, 105 | // name: dirName 106 | // }; 107 | // } catch (ex) { 108 | // if (onError) { 109 | // onError(new Error("Failed to create folder at path '" + path + "'")); 110 | // } 111 | // 112 | // return undefined; 113 | // } 114 | } 115 | 116 | public getExistingFolder(path: string, onError?: (error: any) => any): { path: string; name: string } | any { 117 | // try { 118 | // const fileManager = NSFileManager.defaultManager; 119 | // const exists = this.folderExists(path); 120 | // 121 | // if (exists) { 122 | // const dirName = fileManager.displayNameAtPath(path); 123 | // 124 | // return { 125 | // path: path, 126 | // name: dirName 127 | // }; 128 | // } 129 | // 130 | // return undefined; 131 | // } catch (ex) { 132 | // if (onError) { 133 | // onError(new Error("Failed to get folder at path '" + path + "'")); 134 | // } 135 | // 136 | // return undefined; 137 | // } 138 | } 139 | 140 | public eachEntity(path: string, onEntity: (file: { path: string; name: string; extension: string }) => any, onError?: (error: any) => any) { 141 | if (!onEntity) { 142 | return; 143 | } 144 | 145 | this.enumEntities(path, onEntity, onError); 146 | } 147 | 148 | public getEntities(path: string, onError?: (error: any) => any): Array<{ path: string; name: string; extension: string }> { 149 | const fileInfos = new Array<{ path: string; name: string; extension: string }>(); 150 | 151 | const onEntity = function (entity: { path: string; name: string; extension: string }): boolean { 152 | fileInfos.push(entity); 153 | 154 | return true; 155 | }; 156 | 157 | let errorOccurred; 158 | const localError = function (error: any) { 159 | if (onError) { 160 | onError(error); 161 | } 162 | 163 | errorOccurred = true; 164 | }; 165 | 166 | this.enumEntities(path, onEntity, localError); 167 | 168 | if (!errorOccurred) { 169 | return fileInfos; 170 | } 171 | 172 | return null; 173 | } 174 | 175 | public fileExists(path: string): boolean { 176 | const result = this.exists(path); 177 | 178 | return result.exists; 179 | } 180 | 181 | public folderExists(path: string): boolean { 182 | const result = this.exists(path); 183 | 184 | return result.exists && result.isDirectory; 185 | } 186 | 187 | private exists(path: string): { exists: boolean, isDirectory: boolean } | any { 188 | // const fileManager = NSFileManager.defaultManager; 189 | // const isDirectory = new interop.Reference(interop.types.bool, false); 190 | // const exists = fileManager.fileExistsAtPathIsDirectory(path, isDirectory); 191 | 192 | // return { exists: exists, isDirectory: isDirectory.value }; 193 | } 194 | 195 | public concatPath(left: string, right: string): string | any { 196 | // return NSString.pathWithComponents([left, right]).toString(); 197 | } 198 | 199 | public deleteFile(path: string, onError?: (error: any) => any) { 200 | this.deleteEntity(path, onError); 201 | } 202 | 203 | public deleteFolder(path: string, onError?: (error: any) => any) { 204 | this.deleteEntity(path, onError); 205 | } 206 | 207 | public emptyFolder(path: string, onError?: (error: any) => any) { 208 | // const fileManager = NSFileManager.defaultManager; 209 | // const entities = this.getEntities(path, onError); 210 | // 211 | // if (!entities) { 212 | // return; 213 | // } 214 | // 215 | // for (let i = 0; i < entities.length; i++) { 216 | // try { 217 | // fileManager.removeItemAtPathError(entities[i].path); 218 | // } 219 | // catch (ex) { 220 | // if (onError) { 221 | // onError(new Error("Failed to empty folder '" + path + "': " + ex)); 222 | // } 223 | // 224 | // return; 225 | // } 226 | // } 227 | } 228 | 229 | public rename(path: string, newPath: string, onError?: (error: any) => any) { 230 | // const fileManager = NSFileManager.defaultManager; 231 | // 232 | // try { 233 | // fileManager.moveItemAtPathToPathError(path, newPath); 234 | // } catch (ex) { 235 | // if (onError) { 236 | // onError(new Error("Failed to rename '" + path + "' to '" + newPath + "': " + ex)); 237 | // } 238 | // } 239 | } 240 | 241 | public getLogicalRootPath(): string | any { 242 | // const mainBundlePath = NSBundle.mainBundle.bundlePath; 243 | // const resolvedPath = NSString.stringWithString(mainBundlePath).stringByResolvingSymlinksInPath; 244 | // 245 | // return resolvedPath; 246 | } 247 | 248 | public getDocumentsFolderPath(): string | any { 249 | // return this.getKnownPath(NSSearchPathDirectory.DocumentDirectory); 250 | } 251 | 252 | public getTempFolderPath(): string | any { 253 | // return this.getKnownPath(NSSearchPathDirectory.CachesDirectory); 254 | } 255 | 256 | public getCurrentAppPath(): string { 257 | return ""; // desktop.getCurrentAppPath(); 258 | } 259 | 260 | public readText = this.readTextSync.bind(this); 261 | 262 | public readTextAsync(path: string, encoding?: any) { 263 | // const actualEncoding = encoding || textEncoding.UTF_8; 264 | // 265 | // return new Promise((resolve, reject) => { 266 | // try { 267 | // (NSString as any).stringWithContentsOfFileEncodingCompletion( 268 | // path, 269 | // actualEncoding, 270 | // (result, error) => { 271 | // if (error) { 272 | // reject(error); 273 | // } else { 274 | // resolve(result.toString()); 275 | // } 276 | // }, 277 | // ); 278 | // } catch (ex) { 279 | // reject(new Error("Failed to read file at path '" + path + "': " + ex)); 280 | // } 281 | // }); 282 | } 283 | 284 | public readTextSync(path: string, onError?: (error: any) => any, encoding?: any) { 285 | // const actualEncoding = encoding || textEncoding.UTF_8; 286 | // 287 | // try { 288 | // const nsString = NSString.stringWithContentsOfFileEncodingError(path, actualEncoding); 289 | // 290 | // return nsString.toString(); 291 | // } catch (ex) { 292 | // if (onError) { 293 | // onError(new Error("Failed to read file at path '" + path + "': " + ex)); 294 | // } 295 | // } 296 | } 297 | 298 | public read = this.readSync.bind(this); 299 | 300 | public readAsync(path: string): any { 301 | // return new Promise((resolve, reject) => { 302 | // try { 303 | // (NSData as any).dataWithContentsOfFileCompletion(path, resolve); 304 | // } catch (ex) { 305 | // reject(new Error("Failed to read file at path '" + path + "': " + ex)); 306 | // } 307 | // }); 308 | } 309 | 310 | public readSync(path: string, onError?: (error: any) => any): any { 311 | // try { 312 | // return NSData.dataWithContentsOfFile(path); 313 | // } catch (ex) { 314 | // if (onError) { 315 | // onError(new Error("Failed to read file at path '" + path + "': " + ex)); 316 | // } 317 | // } 318 | } 319 | 320 | public writeText = this.writeTextSync.bind(this); 321 | 322 | public writeTextAsync(path: string, content: string, encoding?: any): Promise | any { 323 | // const nsString = NSString.stringWithString(content); 324 | // const actualEncoding = encoding || textEncoding.UTF_8; 325 | // 326 | // return new Promise((resolve, reject) => { 327 | // try { 328 | // (nsString as any).writeToFileAtomicallyEncodingCompletion( 329 | // path, 330 | // true, 331 | // actualEncoding, 332 | // (error) => { 333 | // if (error) { 334 | // reject(error); 335 | // } else { 336 | // resolve(); 337 | // } 338 | // }, 339 | // ); 340 | // } catch (ex) { 341 | // reject(new Error("Failed to write file at path '" + path + "': " + ex)); 342 | // } 343 | // }); 344 | } 345 | 346 | public writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) { 347 | // const nsString = NSString.stringWithString(content); 348 | // 349 | // const actualEncoding = encoding || textEncoding.UTF_8; 350 | // 351 | // // TODO: verify the useAuxiliaryFile parameter should be false 352 | // try { 353 | // nsString.writeToFileAtomicallyEncodingError(path, false, actualEncoding); 354 | // } catch (ex) { 355 | // if (onError) { 356 | // onError(new Error("Failed to write to file '" + path + "': " + ex)); 357 | // } 358 | // } 359 | } 360 | 361 | public write = this.writeSync.bind(this); 362 | 363 | public writeAsync(path: string, content): Promise | any { 364 | // return new Promise((resolve, reject) => { 365 | // try { 366 | // (content as any).writeToFileAtomicallyCompletion( 367 | // path, 368 | // true, 369 | // () => { resolve(); }, 370 | // ); 371 | // } catch (ex) { 372 | // reject(new Error("Failed to write file at path '" + path + "': " + ex)); 373 | // } 374 | // }); 375 | } 376 | 377 | public writeSync(path: string, content, onError?: (error: any) => any) { 378 | try { 379 | content.writeToFileAtomically(path, true); 380 | } catch (ex) { 381 | if (onError) { 382 | onError(new Error("Failed to write to file '" + path + "': " + ex)); 383 | } 384 | } 385 | } 386 | 387 | private getKnownPath(folderType: number): string | any { 388 | // const fileManager = NSFileManager.defaultManager; 389 | // const paths = fileManager.URLsForDirectoryInDomains(folderType, NSSearchPathDomainMask.UserDomainMask); 390 | // 391 | // const url = paths.objectAtIndex(0); 392 | // 393 | // return url.path; 394 | } 395 | 396 | // TODO: This method is the same as in the iOS implementation. 397 | // Make it in a separate file / module so it can be reused from both implementations. 398 | private getFileExtension(path: string): string { 399 | // TODO [For Panata]: The definitions currently specify "any" as a return value of this method 400 | //const nsString = Foundation.NSString.stringWithString(path); 401 | //const extension = nsString.pathExtension(); 402 | 403 | //if (extension && extension.length > 0) { 404 | // extension = extension.concat(".", extension); 405 | //} 406 | 407 | //return extension; 408 | const dotIndex = path.lastIndexOf("."); 409 | if (dotIndex && dotIndex >= 0 && dotIndex < path.length) { 410 | return path.substring(dotIndex); 411 | } 412 | 413 | return ""; 414 | } 415 | 416 | private deleteEntity(path: string, onError?: (error: any) => any) { 417 | // const fileManager = NSFileManager.defaultManager; 418 | // try { 419 | // fileManager.removeItemAtPathError(path); 420 | // } catch (ex) { 421 | // if (onError) { 422 | // onError(new Error("Failed to delete file at path '" + path + "': " + ex)); 423 | // } 424 | // } 425 | } 426 | 427 | private enumEntities(path: string, callback: (entity: { path: string; name: string; extension: string }) => boolean, onError?: (error) => any) { 428 | // try { 429 | // const fileManager = NSFileManager.defaultManager; 430 | // let files: NSArray; 431 | // try { 432 | // files = fileManager.contentsOfDirectoryAtPathError(path); 433 | // } catch (ex) { 434 | // if (onError) { 435 | // onError(new Error("Failed to enum files for folder '" + path + "': " + ex)); 436 | // } 437 | // 438 | // return; 439 | // } 440 | // 441 | // for (let i = 0; i < files.count; i++) { 442 | // const file = files.objectAtIndex(i); 443 | // 444 | // const info = { 445 | // path: this.concatPath(path, file), 446 | // name: file, 447 | // extension: "" 448 | // }; 449 | // 450 | // if (!this.folderExists(this.joinPath(path, file))) { 451 | // info.extension = this.getFileExtension(info.path); 452 | // } 453 | // 454 | // const retVal = callback(info); 455 | // if (retVal === false) { 456 | // // the callback returned false meaning we should stop the iteration 457 | // break; 458 | // } 459 | // } 460 | // } catch (ex) { 461 | // if (onError) { 462 | // onError(ex); 463 | // } 464 | // } 465 | } 466 | 467 | public getPathSeparator(): string { 468 | return "/"; 469 | } 470 | 471 | public normalizePath(path: string): string | any { 472 | // const nsString: NSString = NSString.stringWithString(path); 473 | // const normalized = nsString.stringByStandardizingPath; 474 | // 475 | // return normalized; 476 | } 477 | 478 | public joinPath(left: string, right: string): string | any { 479 | // const nsString: NSString = NSString.stringWithString(left); 480 | // 481 | // return nsString.stringByAppendingPathComponent(right); 482 | } 483 | 484 | public joinPaths(paths: string[]): string { 485 | return paths.join(this.getPathSeparator()); // desktop.joinPaths(...paths); 486 | } 487 | } 488 | -------------------------------------------------------------------------------- /core/fps-meter/fps-native.desktop.ts: -------------------------------------------------------------------------------- 1 | import * as definition from "@nativescript/core/fps-meter/fps-native"; 2 | 3 | class FrameHandlerImpl { 4 | 5 | private _owner: WeakRef; 6 | 7 | public static initWithOwner(owner: WeakRef): FrameHandlerImpl | any { 8 | // let handler = FrameHandlerImpl.new(); 9 | // handler._owner = owner; 10 | // 11 | // return handler; 12 | } 13 | 14 | public handleFrame(sender: any): void { 15 | let owner = this._owner.get(); 16 | if (owner) { 17 | owner._handleFrame(sender); 18 | } 19 | } 20 | } 21 | 22 | export class FPSCallback implements definition.FPSCallback { 23 | public running: boolean; 24 | private onFrame: Function; 25 | // private displayLink: CADisplayLink; 26 | private impl: FrameHandlerImpl; 27 | 28 | constructor(onFrame: (currentTimeMillis: number) => void) { 29 | this.onFrame = onFrame; 30 | 31 | this.impl = FrameHandlerImpl.initWithOwner(new WeakRef(this)); 32 | 33 | // this.displayLink = CADisplayLink.displayLinkWithTargetSelector(this.impl, "handleFrame"); 34 | // this.displayLink.paused = true; 35 | // this.displayLink.addToRunLoopForMode(NSRunLoop.currentRunLoop, NSDefaultRunLoopMode); 36 | // // UIScrollView (including in UIITableView) will run a loop in UITrackingRunLoopMode during scrolling. 37 | // // If we do not add the CADisplayLink in this mode, it would appear paused during scrolling. 38 | // this.displayLink.addToRunLoopForMode(NSRunLoop.currentRunLoop, UITrackingRunLoopMode); 39 | } 40 | 41 | public start() { 42 | if (this.running) { 43 | return; 44 | } 45 | 46 | this.running = true; 47 | // this.displayLink.paused = false; 48 | } 49 | 50 | public stop() { 51 | if (!this.running) { 52 | return; 53 | } 54 | 55 | // this.displayLink.paused = true; 56 | this.running = false; 57 | } 58 | 59 | public _handleFrame(sender: any) { 60 | if (!this.running) { 61 | return; 62 | } 63 | 64 | // timestamp is CFTimeInterval, which is in seconds, the onFrame callback expects millis, so multiply by 1000 65 | this.onFrame(sender.timestamp * 1000); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/http/http-request/http-request.desktop.ts: -------------------------------------------------------------------------------- 1 | // imported for definition purposes only 2 | import * as httpModule from "@nativescript/core/http"; 3 | import * as imageSourceModule from "@nativescript/core/image-source"; 4 | import * as fsModule from "@nativescript/core/file-system"; 5 | 6 | import * as types from "@nativescript/core/utils/types"; 7 | import * as domainDebugger from "@nativescript/core/debugger/debugger"; 8 | import { getFilenameFromUrl } from "@nativescript/core/http/http-request/http-request-common"; 9 | 10 | export enum HttpResponseEncoding { 11 | UTF8, 12 | GBK 13 | } 14 | 15 | const currentDevice = { systemVersion: "10.0" }; 16 | const device = "Desktop"; 17 | const osVersion = currentDevice.systemVersion; 18 | 19 | const GET = "GET"; 20 | const USER_AGENT_HEADER = "User-Agent"; 21 | const USER_AGENT = `Mozilla/5.0 (${device}; CPU OS ${osVersion.replace(".", "_")} like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/${osVersion} Mobile/10A5355d Safari/8536.25`; 22 | //const sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration; 23 | //const queue = NSOperationQueue.mainQueue; 24 | 25 | function parseJSON(source: string): any { 26 | const src = source.trim(); 27 | if (src.lastIndexOf(")") === src.length - 1) { 28 | return JSON.parse(src.substring(src.indexOf("(") + 1, src.lastIndexOf(")"))); 29 | } 30 | 31 | return JSON.parse(src); 32 | } 33 | 34 | // class NSURLSessionTaskDelegateImpl extends NSObject implements NSURLSessionTaskDelegate { 35 | // public static ObjCProtocols = [NSURLSessionTaskDelegate]; 36 | // public URLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandler(session: NSURLSession, task: NSURLSessionTask, response: NSHTTPURLResponse, request: NSURLRequest, completionHandler: (p1: NSURLRequest) => void): void { 37 | // completionHandler(null); 38 | // } 39 | // } 40 | // const sessionTaskDelegateInstance: NSURLSessionTaskDelegateImpl = NSURLSessionTaskDelegateImpl.new(); 41 | 42 | let defaultSession; 43 | function ensureDefaultSession() { 44 | // if (!defaultSession) { 45 | // defaultSession = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, null, queue); 46 | // } 47 | } 48 | 49 | let sessionNotFollowingRedirects; 50 | function ensureSessionNotFollowingRedirects() { 51 | // if (!sessionNotFollowingRedirects) { 52 | // sessionNotFollowingRedirects = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, sessionTaskDelegateInstance, queue); 53 | // } 54 | } 55 | 56 | let imageSource: typeof imageSourceModule; 57 | function ensureImageSource() { 58 | if (!imageSource) { 59 | imageSource = require("../../image-source"); 60 | } 61 | } 62 | 63 | let fs: typeof fsModule; 64 | function ensureFileSystem() { 65 | if (!fs) { 66 | fs = require("../../file-system"); 67 | } 68 | } 69 | 70 | export function request(options: httpModule.HttpRequestOptions): Promise { 71 | return new Promise((resolve, reject) => { 72 | 73 | if (!options.url) { 74 | reject(new Error("Request url was empty.")); 75 | 76 | return; 77 | } 78 | 79 | try { 80 | // const network = domainDebugger.getNetwork(); 81 | // const debugRequest = network && network.create(); 82 | // 83 | // const urlRequest = NSMutableURLRequest.requestWithURL( 84 | // NSURL.URLWithString(options.url)); 85 | // 86 | // urlRequest.HTTPMethod = types.isDefined(options.method) ? options.method : GET; 87 | // 88 | // urlRequest.setValueForHTTPHeaderField(USER_AGENT, USER_AGENT_HEADER); 89 | // 90 | // if (options.headers) { 91 | // for (let header in options.headers) { 92 | // urlRequest.setValueForHTTPHeaderField(options.headers[header] + "", header); 93 | // } 94 | // } 95 | // 96 | // if (types.isString(options.content) || options.content instanceof FormData) { 97 | // urlRequest.HTTPBody = NSString.stringWithString(options.content.toString()).dataUsingEncoding(4); 98 | // } 99 | // 100 | // if (types.isNumber(options.timeout)) { 101 | // urlRequest.timeoutInterval = options.timeout / 1000; 102 | // } 103 | // 104 | // let session; 105 | // if (types.isBoolean(options.dontFollowRedirects) && options.dontFollowRedirects) { 106 | // ensureSessionNotFollowingRedirects(); 107 | // session = sessionNotFollowingRedirects; 108 | // } else { 109 | // ensureDefaultSession(); 110 | // session = defaultSession; 111 | // } 112 | // 113 | // const dataTask = session.dataTaskWithRequestCompletionHandler(urlRequest, 114 | // function (data: NSData, response: NSHTTPURLResponse, error: NSError) { 115 | // if (error) { 116 | // reject(new Error(error.localizedDescription)); 117 | // } else { 118 | // const headers: httpModule.Headers = {}; 119 | // if (response && response.allHeaderFields) { 120 | // const headerFields = response.allHeaderFields; 121 | // 122 | // headerFields.enumerateKeysAndObjectsUsingBlock((key, value, stop) => { 123 | // addHeader(headers, key, value); 124 | // }); 125 | // } 126 | // 127 | // if (debugRequest) { 128 | // debugRequest.mimeType = response.MIMEType; 129 | // debugRequest.data = data; 130 | // const debugResponse = { 131 | // url: options.url, 132 | // status: response.statusCode, 133 | // statusText: NSHTTPURLResponse.localizedStringForStatusCode(response.statusCode), 134 | // headers: headers, 135 | // mimeType: response.MIMEType, 136 | // fromDiskCache: false 137 | // }; 138 | // debugRequest.responseReceived(debugResponse); 139 | // debugRequest.loadingFinished(); 140 | // } 141 | // 142 | // resolve({ 143 | // content: { 144 | // raw: data, 145 | // toString: (encoding?: any) => NSDataToString(data, encoding), 146 | // toJSON: (encoding?: any) => parseJSON(NSDataToString(data, encoding)), 147 | // toImage: () => { 148 | // ensureImageSource(); 149 | // 150 | // return new Promise((resolve, reject) => { 151 | // (UIImage).tns_decodeImageWithDataCompletion(data, image => { 152 | // if (image) { 153 | // resolve(new imageSource.ImageSource(image)); 154 | // } else { 155 | // reject(new Error("Response content may not be converted to an Image")); 156 | // } 157 | // }); 158 | // }); 159 | // }, 160 | // toFile: (destinationFilePath?: string) => { 161 | // ensureFileSystem(); 162 | // 163 | // if (!destinationFilePath) { 164 | // destinationFilePath = getFilenameFromUrl(options.url); 165 | // } 166 | // if (data instanceof NSData) { 167 | // // ensure destination path exists by creating any missing parent directories 168 | // const file = fs.File.fromPath(destinationFilePath); 169 | // 170 | // data.writeToFileAtomically(destinationFilePath, true); 171 | // 172 | // return file; 173 | // } else { 174 | // reject(new Error(`Cannot save file with path: ${destinationFilePath}.`)); 175 | // } 176 | // } 177 | // }, 178 | // statusCode: response.statusCode, 179 | // headers: headers 180 | // }); 181 | // } 182 | // }); 183 | // 184 | // if (options.url && debugRequest) { 185 | // const request = { 186 | // url: options.url, 187 | // method: "GET", 188 | // headers: options.headers 189 | // }; 190 | // debugRequest.requestWillBeSent(request); 191 | // } 192 | // 193 | // dataTask.resume(); 194 | 195 | // @ts-ignore 196 | resolve({ content: { raw: 'Not implemented!' } }); 197 | } catch (ex) { 198 | reject(ex); 199 | } 200 | }); 201 | } 202 | 203 | // function NSDataToString(data: any, encoding?: HttpResponseEncoding): string { 204 | // let code = NSUTF8StringEncoding; // long:4 205 | // 206 | // if (encoding === HttpResponseEncoding.GBK) { 207 | // code = CFStringEncodings.kCFStringEncodingGB_18030_2000; // long:1586 208 | // } 209 | // 210 | // let encodedString = NSString.alloc().initWithDataEncoding(data, code); 211 | // 212 | // // If UTF8 string encoding fails try with ISO-8859-1 213 | // if (!encodedString) { 214 | // code = NSISOLatin1StringEncoding; // long:5 215 | // encodedString = NSString.alloc().initWithDataEncoding(data, code); 216 | // } 217 | // 218 | // return encodedString.toString(); 219 | // } 220 | 221 | export function addHeader(headers: httpModule.Headers, key: string, value: string): void { 222 | if (!headers[key]) { 223 | headers[key] = value; 224 | } else if (Array.isArray(headers[key])) { 225 | (headers[key]).push(value); 226 | } else { 227 | const values: string[] = [headers[key]]; 228 | values.push(value); 229 | headers[key] = values; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /core/image-source/image-source.desktop.ts: -------------------------------------------------------------------------------- 1 | // Definitions. 2 | import { ImageSource as ImageSourceDefinition } from "@nativescript/core/image-source"; 3 | import { ImageAsset } from "@nativescript/core/image-asset"; 4 | import * as httpModule from "@nativescript/core/http"; 5 | import { Font } from "@nativescript/core/ui/styling/font"; 6 | import { Color } from "@nativescript/core/color"; 7 | 8 | // Types. 9 | import { path as fsPath, knownFolders } from "@nativescript/core/file-system"; 10 | import { isFileOrResourcePath, RESOURCE_PREFIX, layout } from "@nativescript/core/utils/utils"; 11 | 12 | export { isFileOrResourcePath }; 13 | 14 | let http: typeof httpModule; 15 | function ensureHttp() { 16 | if (!http) { 17 | http = require("../http"); 18 | } 19 | } 20 | 21 | export class ImageSource implements ImageSourceDefinition { 22 | //public android: android.graphics.Bitmap; 23 | public ios: null; 24 | public android: null; 25 | public desktop: any; 26 | 27 | get height(): number { 28 | if (this.desktop) { 29 | return this.desktop.size.height; 30 | } 31 | 32 | return NaN; 33 | } 34 | 35 | get width(): number { 36 | if (this.desktop) { 37 | return this.desktop.size.width; 38 | } 39 | 40 | return NaN; 41 | } 42 | 43 | get rotationAngle(): number { 44 | return NaN; 45 | } 46 | 47 | set rotationAngle(_value: number) { 48 | // compatibility with Android 49 | } 50 | 51 | constructor(nativeSource?: any) { 52 | if (nativeSource) { 53 | this.setNativeSource(nativeSource); 54 | } 55 | } 56 | 57 | static fromAsset(asset: ImageAsset): Promise { 58 | return new Promise((resolve, reject) => { 59 | asset.getImageAsync((image, err) => { 60 | if (image) { 61 | resolve(new ImageSource(image)); 62 | } else { 63 | reject(err); 64 | } 65 | }); 66 | }); 67 | } 68 | 69 | static fromUrl(url: string): Promise { 70 | ensureHttp(); 71 | 72 | return http.getImage(url); 73 | } 74 | 75 | static fromResourceSync(name: string): ImageSource { 76 | //const nativeSource = (UIImage).tns_safeImageNamed(name) || (UIImage).tns_safeImageNamed(`${name}.jpg`); 77 | 78 | return /*nativeSource ? new ImageSource(nativeSource) :*/ null; 79 | } 80 | static fromResource(name: string): Promise { 81 | return new Promise((resolve, reject) => { 82 | try { 83 | // (UIImage).tns_safeDecodeImageNamedCompletion(name, image => { 84 | // if (image) { 85 | // resolve(new ImageSource(image)); 86 | // } else { 87 | // (UIImage).tns_safeDecodeImageNamedCompletion(`${name}.jpg`, image => { 88 | // resolve(new ImageSource(image)); 89 | // }); 90 | // } 91 | // }); 92 | } catch (ex) { 93 | reject(ex); 94 | } 95 | }); 96 | } 97 | 98 | static fromFileSync(path: string): ImageSource { 99 | // const uiImage = UIImage.imageWithContentsOfFile(getFileName(path)); 100 | 101 | return/* uiImage ? new ImageSource(uiImage) :*/ null; 102 | } 103 | static fromFile(path: string): Promise { 104 | return new Promise((resolve, reject) => { 105 | try { 106 | // (UIImage).tns_decodeImageWidthContentsOfFileCompletion(getFileName(path), 107 | // uiImage => { 108 | // resolve(new ImageSource(uiImage)); 109 | // }); 110 | } catch (ex) { 111 | reject(ex); 112 | } 113 | }); 114 | } 115 | 116 | static fromFileOrResourceSync(path: string): ImageSource { 117 | if (!isFileOrResourcePath(path)) { 118 | throw new Error("Path \"" + "\" is not a valid file or resource."); 119 | } 120 | 121 | if (path.indexOf(RESOURCE_PREFIX) === 0) { 122 | return ImageSource.fromResourceSync(path.substr(RESOURCE_PREFIX.length)); 123 | } 124 | 125 | return ImageSource.fromFileSync(path); 126 | } 127 | 128 | static fromDataSync(data: any): ImageSource { 129 | // const uiImage = UIImage.imageWithData(data); 130 | 131 | return /*uiImage ? new ImageSource(uiImage) : */null; 132 | } 133 | static fromData(data: any): Promise { 134 | return new Promise((resolve, reject) => { 135 | try { 136 | // (UIImage).tns_decodeImageWithDataCompletion(data, 137 | // uiImage => { 138 | // resolve(new ImageSource(uiImage)); 139 | // }); 140 | } catch (ex) { 141 | reject(ex); 142 | } 143 | }); 144 | } 145 | 146 | static fromBase64Sync(source: string): ImageSource { 147 | // let uiImage: UIImage; 148 | // if (typeof source === "string") { 149 | // const data = NSData.alloc().initWithBase64EncodedStringOptions(source, NSDataBase64DecodingOptions.IgnoreUnknownCharacters); 150 | // uiImage = UIImage.imageWithData(data); 151 | // } 152 | 153 | return /*uiImage ? new ImageSource(uiImage) :*/ null; 154 | } 155 | static fromBase64(source: string): Promise { 156 | return new Promise((resolve, reject) => { 157 | try { 158 | // const data = NSData.alloc().initWithBase64EncodedStringOptions(source, NSDataBase64DecodingOptions.IgnoreUnknownCharacters); 159 | // UIImage.imageWithData["async"](UIImage, [data]).then( 160 | // uiImage => { 161 | // resolve(new ImageSource(uiImage)); 162 | // }); 163 | } catch (ex) { 164 | reject(ex); 165 | } 166 | }); 167 | } 168 | 169 | static fromFontIconCodeSync(source: string, font: Font, color: Color): ImageSource | any { 170 | // let fontSize = layout.toDevicePixels(font.fontSize); 171 | // if (!fontSize) { 172 | // // TODO: Consider making 36 font size as default for optimal look on TabView and ActionBar 173 | // fontSize = UIFont.labelFontSize; 174 | // } 175 | // 176 | // const density = layout.getDisplayDensity(); 177 | // const scaledFontSize = fontSize * density; 178 | // 179 | // const attributes = { 180 | // [NSFontAttributeName]: font.getUIFont(UIFont.systemFontOfSize(scaledFontSize)) 181 | // }; 182 | // 183 | // if (color) { 184 | // attributes[NSForegroundColorAttributeName] = color.ios; 185 | // } 186 | // 187 | // const attributedString = NSAttributedString.alloc().initWithStringAttributes(source, >attributes); 188 | // 189 | // UIGraphicsBeginImageContextWithOptions(attributedString.size(), false, 0.0); 190 | // attributedString.drawAtPoint(CGPointMake(0, 0)); 191 | // 192 | // const iconImage = UIGraphicsGetImageFromCurrentImageContext(); 193 | // UIGraphicsEndImageContext(); 194 | // 195 | // return iconImage ? new ImageSource(iconImage) : null; 196 | } 197 | 198 | public fromAsset(asset: ImageAsset) { 199 | console.log("fromAsset() is deprecated. Use ImageSource.fromAsset() instead."); 200 | 201 | return ImageSource.fromAsset(asset) 202 | .then(imgSource => { 203 | this.setNativeSource(imgSource.desktop); 204 | 205 | return this; 206 | }); 207 | } 208 | 209 | public loadFromResource(name: string): boolean { 210 | console.log("loadFromResource() is deprecated. Use ImageSource.fromResourceSync() instead."); 211 | 212 | const imgSource = ImageSource.fromResourceSync(name); 213 | this.desktop = imgSource ? imgSource.desktop : null; 214 | 215 | return !!this.desktop; 216 | } 217 | 218 | public fromResource(name: string): Promise { 219 | console.log("fromResource() is deprecated. Use ImageSource.fromResource() instead."); 220 | 221 | return ImageSource.fromResource(name) 222 | .then(imgSource => { 223 | this.desktop = imgSource.desktop; 224 | 225 | return !!this.desktop; 226 | }); 227 | } 228 | 229 | public loadFromFile(path: string): boolean { 230 | console.log("loadFromFile() is deprecated. Use ImageSource.fromFileSync() instead."); 231 | 232 | const imgSource = ImageSource.fromFileSync(path); 233 | this.desktop = imgSource ? imgSource.desktop : null; 234 | 235 | return !!this.desktop; 236 | } 237 | 238 | public fromFile(path: string): Promise { 239 | console.log("fromFile() is deprecated. Use ImageSource.fromFile() instead."); 240 | 241 | return ImageSource.fromFile(path) 242 | .then(imgSource => { 243 | this.desktop = imgSource.desktop; 244 | 245 | return !!this.desktop; 246 | }); 247 | } 248 | 249 | public loadFromData(data: any): boolean { 250 | console.log("loadFromData() is deprecated. Use ImageSource.fromDataSync() instead."); 251 | 252 | const imgSource = ImageSource.fromDataSync(data); 253 | this.desktop = imgSource ? imgSource.desktop : null; 254 | 255 | return !!this.desktop; 256 | } 257 | 258 | public fromData(data: any): Promise { 259 | console.log("fromData() is deprecated. Use ImageSource.fromData() instead."); 260 | 261 | return ImageSource.fromData(data) 262 | .then(imgSource => { 263 | this.desktop = imgSource.desktop; 264 | 265 | return !!this.desktop; 266 | }); 267 | } 268 | 269 | public loadFromBase64(source: string): boolean { 270 | console.log("loadFromBase64() is deprecated. Use ImageSource.fromBase64Sync() instead."); 271 | 272 | const imgSource = ImageSource.fromBase64Sync(source); 273 | this.desktop = imgSource ? imgSource.desktop : null; 274 | 275 | return !!this.desktop; 276 | } 277 | 278 | public fromBase64(source: string): Promise { 279 | console.log("fromBase64() is deprecated. Use ImageSource.fromBase64() instead."); 280 | 281 | return ImageSource.fromBase64(source) 282 | .then(imgSource => { 283 | this.desktop = imgSource.desktop; 284 | 285 | return !!this.desktop; 286 | }); 287 | } 288 | 289 | public loadFromFontIconCode(source: string, font: Font, color: Color): boolean { 290 | console.log("loadFromFontIconCode() is deprecated. Use ImageSource.fromFontIconCodeSync() instead."); 291 | 292 | const imgSource = ImageSource.fromFontIconCodeSync(source, font, color); 293 | this.desktop = imgSource ? imgSource.desktop : null; 294 | 295 | return !!this.desktop; 296 | } 297 | 298 | public setNativeSource(source: any): void { 299 | // if (source && !(source instanceof UIImage)) { 300 | // throw new Error("The method setNativeSource() expects UIImage instance."); 301 | // } 302 | this.desktop = source; 303 | } 304 | 305 | public saveToFile(path: string, format: "png" | "jpeg" | "jpg", quality?: number): boolean { 306 | if (!this.desktop) { 307 | return false; 308 | } 309 | 310 | if (quality) { 311 | quality = (quality - 0) / (100 - 0); // Normalize quality on a scale of 0 to 1 312 | } 313 | 314 | const data = getImageData(this.desktop, format, quality); 315 | if (data) { 316 | //return NSFileManager.defaultManager.createFileAtPathContentsAttributes(path, data, null); 317 | } 318 | 319 | return false; 320 | } 321 | 322 | public toBase64String(format: "png" | "jpeg" | "jpg", quality?: number): string { 323 | let res = null; 324 | if (!this.desktop) { 325 | return res; 326 | } 327 | 328 | if (quality) { 329 | quality = (quality - 0) / (100 - 0); // Normalize quality on a scale of 0 to 1 330 | } 331 | 332 | const data = getImageData(this.desktop, format, quality); 333 | if (data) { 334 | res = data.base64Encoding(); 335 | } 336 | 337 | return res; 338 | 339 | } 340 | } 341 | 342 | function getFileName(path: string): string { 343 | let fileName = typeof path === "string" ? path.trim() : ""; 344 | if (fileName.indexOf("~/") === 0) { 345 | fileName = fsPath.join(knownFolders.currentApp().path, fileName.replace("~/", "")); 346 | } 347 | 348 | return fileName; 349 | } 350 | 351 | function getImageData(instance, format: "png" | "jpeg" | "jpg", quality = 0.9) { 352 | let data = null; 353 | switch (format) { 354 | case "png": 355 | // data = UIImagePNGRepresentation(instance); 356 | break; 357 | case "jpeg": 358 | case "jpg": 359 | // data = UIImageJPEGRepresentation(instance, quality); 360 | break; 361 | } 362 | 363 | return data; 364 | } 365 | 366 | export function fromAsset(asset: ImageAsset): Promise { 367 | console.log("fromAsset() is deprecated. Use ImageSource.fromAsset() instead."); 368 | 369 | return ImageSource.fromAsset(asset); 370 | } 371 | 372 | export function fromResource(name: string): ImageSource { 373 | console.log("fromResource() is deprecated. Use ImageSource.fromResourceSync() instead."); 374 | 375 | return ImageSource.fromResourceSync(name); 376 | } 377 | 378 | export function fromFile(path: string): ImageSource { 379 | console.log("fromFile() is deprecated. Use ImageSource.fromFileSync() instead."); 380 | 381 | return ImageSource.fromFileSync(path); 382 | } 383 | 384 | export function fromData(data: any): ImageSource { 385 | console.log("fromData() is deprecated. Use ImageSource.fromDataSync() instead."); 386 | 387 | return ImageSource.fromDataSync(data); 388 | } 389 | 390 | export function fromFontIconCode(source: string, font: Font, color: Color): ImageSource { 391 | console.log("fromFontIconCode() is deprecated. Use ImageSource.fromFontIconCodeSync() instead."); 392 | 393 | return ImageSource.fromFontIconCodeSync(source, font, color); 394 | } 395 | 396 | export function fromBase64(source: string): ImageSource { 397 | console.log("fromBase64() is deprecated. Use ImageSource.fromBase64Sync() instead."); 398 | 399 | return ImageSource.fromBase64Sync(source); 400 | } 401 | 402 | export function fromNativeSource(nativeSource: any): ImageSource { 403 | console.log("fromNativeSource() is deprecated. Use ImageSource constructor instead."); 404 | 405 | return new ImageSource(nativeSource); 406 | } 407 | 408 | // export function fromUrl(url: string): Promise { 409 | // console.log("fromUrl() is deprecated. Use ImageSource.fromUrl() instead."); 410 | // 411 | // return ImageSource.fromUrl(url); 412 | // } 413 | 414 | export function fromFileOrResource(path: string): ImageSource { 415 | console.log("fromFileOrResource() is deprecated. Use ImageSource.fromFileOrResourceSync() instead."); 416 | 417 | return ImageSource.fromFileOrResourceSync(path); 418 | } 419 | -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-platform-desktop", 3 | "main": "index", 4 | "types": "index.d.ts", 5 | "description": "Telerik NativeScript Desktop Modules", 6 | "version": "0.0.1", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/bundyo/nativescript-desktop" 10 | }, 11 | "files": [ 12 | "**/*.d.ts", 13 | "**/*.js", 14 | "**/package.json" 15 | ], 16 | "license": "Apache-2.0", 17 | "devDependencies": { 18 | "@types/node": "~10.17.6" 19 | }, 20 | "nativescript": { 21 | "platforms": { 22 | "ios": "6.0.0", 23 | "android": "6.0.0" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/platform/platform.desktop.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:class-name */ 2 | import { Device as DeviceDefinition, ScreenMetrics as ScreenMetricsDefinition } from "@nativescript/core/platform"; 3 | import * as appModule from "@nativescript/core/application"; 4 | 5 | const MIN_TABLET_PIXELS = 600; 6 | 7 | export module platformNames { 8 | export const android = "Android"; 9 | export const ios = "iOS"; 10 | export const desktop = "Desktop"; 11 | } 12 | 13 | class Device implements DeviceDefinition { 14 | private _manufacturer: string; 15 | private _model: string; 16 | private _osVersion: string; 17 | private _sdkVersion: string; 18 | //private _deviceType: "Desktop"; 19 | private _uuid: string; 20 | private _language: string; 21 | private _region: string; 22 | 23 | get manufacturer(): string { 24 | if (!this._manufacturer) { 25 | //this._manufacturer = android.os.Build.MANUFACTURER; 26 | } 27 | 28 | return this._manufacturer; 29 | } 30 | 31 | get os(): string { 32 | return platformNames.android; 33 | } 34 | 35 | get osVersion(): string { 36 | if (!this._osVersion) { 37 | //this._osVersion = android.os.Build.VERSION.RELEASE; 38 | } 39 | 40 | return this._osVersion; 41 | } 42 | 43 | get model(): string { 44 | if (!this._model) { 45 | //this._model = android.os.Build.MODEL; 46 | } 47 | 48 | return this._model; 49 | } 50 | 51 | get sdkVersion(): string { 52 | if (!this._sdkVersion) { 53 | //this._sdkVersion = android.os.Build.VERSION.SDK; 54 | } 55 | 56 | return this._sdkVersion; 57 | } 58 | 59 | get deviceType(): "Desktop" | any { 60 | // if (!this._deviceType) { 61 | // const dips = Math.min(screen.mainScreen.widthPixels, screen.mainScreen.heightPixels) / screen.mainScreen.scale; 62 | // // If the device has more than 600 dips it is considered to be a tablet. 63 | // if (dips >= MIN_TABLET_PIXELS) { 64 | // this._deviceType = "Tablet"; 65 | // } 66 | // else { 67 | // this._deviceType = "Phone"; 68 | // } 69 | // } 70 | 71 | return "Desktop"; // this._deviceType; 72 | } 73 | 74 | get uuid(): string { 75 | if (!this._uuid) { 76 | // const nativeApp = appModule.android.nativeApp; 77 | // this._uuid = android.provider.Settings.Secure.getString( 78 | // nativeApp.getContentResolver(), 79 | // android.provider.Settings.Secure.ANDROID_ID 80 | // ); 81 | } 82 | 83 | return this._uuid; 84 | } 85 | 86 | get language(): string { 87 | if (!this._language) { 88 | // this._language = java.util.Locale.getDefault().getLanguage().replace("_", "-"); 89 | } 90 | 91 | return this._language; 92 | } 93 | 94 | get region(): string { 95 | if (!this._region) { 96 | // this._region = java.util.Locale.getDefault().getCountry(); 97 | } 98 | 99 | return this._region; 100 | } 101 | } 102 | 103 | class MainScreen implements ScreenMetricsDefinition { 104 | // private _metrics: android.util.DisplayMetrics; 105 | 106 | private reinitMetrics(): void { 107 | // if (!this._metrics) { 108 | // this._metrics = new android.util.DisplayMetrics(); 109 | // } 110 | this.initMetrics(); 111 | } 112 | 113 | private initMetrics(): void { 114 | // const nativeApp = appModule.getNativeApplication(); 115 | // nativeApp.getSystemService(android.content.Context.WINDOW_SERVICE).getDefaultDisplay().getRealMetrics(this._metrics); 116 | } 117 | 118 | private get metrics(): any { 119 | // if (!this._metrics) { 120 | // // NOTE: This will be memory leak but we MainScreen is singleton 121 | // appModule.on("cssChanged", this.reinitMetrics, this); 122 | // appModule.on(appModule.orientationChangedEvent, this.reinitMetrics, this); 123 | // 124 | // this._metrics = new android.util.DisplayMetrics(); 125 | // this.initMetrics(); 126 | // } 127 | 128 | return {}; // this._metrics; 129 | } 130 | 131 | get widthPixels(): number { 132 | return this.metrics.widthPixels; 133 | } 134 | get heightPixels(): number { 135 | return this.metrics.heightPixels; 136 | } 137 | get scale(): number { 138 | return this.metrics.density; 139 | } 140 | get widthDIPs(): number { 141 | return this.metrics.widthPixels / this.metrics.density; 142 | } 143 | get heightDIPs(): number { 144 | return this.metrics.heightPixels / this.metrics.density; 145 | } 146 | 147 | } 148 | 149 | export const device = new Device(); 150 | 151 | export module screen { 152 | export const mainScreen = new MainScreen(); 153 | } 154 | 155 | export const isAndroid = false; 156 | export const isIOS = false; 157 | export const isDesktop = true; 158 | -------------------------------------------------------------------------------- /core/text/text.desktop.ts: -------------------------------------------------------------------------------- 1 | export module encoding { 2 | export const ISO_8859_1 = "ISO-8859-1"; 3 | export const US_ASCII = "US-ASCII"; 4 | export const UTF_16 = "UTF-16"; 5 | export const UTF_16BE = "UTF-16BE"; 6 | export const UTF_16LE = "UTF-16LE"; 7 | export const UTF_8 = "UTF-8"; 8 | } 9 | -------------------------------------------------------------------------------- /core/timer/timer.desktop.ts: -------------------------------------------------------------------------------- 1 | // /** 2 | // * Android specific timer functions implementation. 3 | // */ 4 | // let timeoutHandler; 5 | // const timeoutCallbacks = {}; 6 | // let timerId = 0; 7 | // 8 | // function createHandlerAndGetId(): number { 9 | // if (!timeoutHandler) { 10 | // timeoutHandler = new android.os.Handler(android.os.Looper.myLooper()); 11 | // } 12 | // 13 | // timerId++; 14 | // 15 | // return timerId; 16 | // } 17 | // 18 | // export function setTimeout(callback: Function, milliseconds = 0, ...args): number { 19 | // // Cast to Number 20 | // milliseconds += 0; 21 | // 22 | // const id = createHandlerAndGetId(); 23 | // const invoke = () => callback(...args); 24 | // const zoneBound = zonedCallback(invoke); 25 | // 26 | // // const runnable = new java.lang.Runnable({ 27 | // // run: () => { 28 | // // zoneBound(); 29 | // // 30 | // // if (timeoutCallbacks[id]) { 31 | // // delete timeoutCallbacks[id]; 32 | // // } 33 | // // } 34 | // // }); 35 | // 36 | // if (!timeoutCallbacks[id]) { 37 | // // timeoutCallbacks[id] = runnable; 38 | // } 39 | // 40 | // // timeoutHandler.postDelayed(runnable, long(milliseconds)); 41 | // 42 | // return id; 43 | // } 44 | // 45 | // export function clearTimeout(id: number): void { 46 | // let index = id; 47 | // if (timeoutCallbacks[index]) { 48 | // timeoutHandler.removeCallbacks(timeoutCallbacks[index]); 49 | // delete timeoutCallbacks[index]; 50 | // } 51 | // } 52 | // 53 | // export function setInterval(callback: Function, milliseconds = 0, ...args): number { 54 | // // Cast to Number 55 | // milliseconds += 0; 56 | // 57 | // const id = createHandlerAndGetId(); 58 | // const handler = timeoutHandler; 59 | // const invoke = () => callback(...args); 60 | // const zoneBound = zonedCallback(invoke); 61 | // 62 | // // const runnable = new java.lang.Runnable({ 63 | // // run: () => { 64 | // // zoneBound(); 65 | // // if (timeoutCallbacks[id]) { 66 | // // handler.postDelayed(runnable, long(milliseconds)); 67 | // // } 68 | // // } 69 | // // }); 70 | // 71 | // if (!timeoutCallbacks[id]) { 72 | // // timeoutCallbacks[id] = runnable; 73 | // } 74 | // 75 | // // timeoutHandler.postDelayed(runnable, long(milliseconds)); 76 | // 77 | // return id; 78 | // } 79 | // 80 | // export const clearInterval = clearTimeout; 81 | -------------------------------------------------------------------------------- /core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmitOnError": false, 4 | "noEmitHelpers": true, 5 | "target": "es5", 6 | "module": "commonjs", 7 | "declaration": false, 8 | "noImplicitAny": false, 9 | "noImplicitUseStrict": true, 10 | "removeComments": true, 11 | "experimentalDecorators": true, 12 | "diagnostics": true, 13 | "sourceMap": true, 14 | "lib": [ 15 | "es6", 16 | "dom" 17 | ], 18 | "types": [ 19 | "node", 20 | "@nativescript/core" 21 | ], 22 | "baseUrl": ".", 23 | "paths": { 24 | "@nativescript/core/*": [ 25 | "core/*" 26 | ] 27 | } 28 | }, 29 | "include": [ 30 | "**/*.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /core/ui/action-bar/action-bar.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ActionBarBase, 3 | ActionItemBase, 4 | backgroundColorProperty, 5 | backgroundInternalProperty, 6 | Color, 7 | colorProperty, 8 | flatProperty, 9 | isVisible, 10 | layout 11 | } from "@nativescript/core/ui/action-bar/action-bar-common"; 12 | import { desktop as desktopUtils, uniqId } from "../../utils/utils.desktop"; 13 | import { FlexLayout, QLabel, QWidget, WidgetEventTypes } from "@nodegui/nodegui"; 14 | import { StyleList, View, ViewCommon } from "../core/view/view.desktop"; 15 | import {ViewBase} from "@nativescript/core"; 16 | 17 | export * from "@nativescript/core/ui/action-bar/action-bar-common"; 18 | 19 | const majorVersion = desktopUtils.MajorVersion; 20 | const UNSPECIFIED = layout.makeMeasureSpec(0, layout.UNSPECIFIED); 21 | 22 | // function loadActionIcon(item: ActionItemDefinition): any /* UIImage */ { 23 | // let is = null; 24 | // let img = null; 25 | // 26 | // const itemIcon = item.icon; 27 | // const itemStyle = item.style; 28 | // if (isFontIconURI(itemIcon)) { 29 | // const fontIconCode = itemIcon.split("//")[1]; 30 | // const font = itemStyle.fontInternal; 31 | // const color = itemStyle.color; 32 | // is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color); 33 | // } else { 34 | // is = ImageSource.fromFileOrResourceSync(itemIcon); 35 | // } 36 | // 37 | // if (is && is.ios) { 38 | // img = is.ios; 39 | // } else { 40 | // traceMissingIcon(itemIcon); 41 | // } 42 | // 43 | // return img; 44 | // } 45 | 46 | const menuItemClickListener: any = function onClick(v) { 47 | if (v) { 48 | v._emit(ActionItemBase.tapEvent); 49 | } 50 | }; 51 | 52 | export class ActionItem extends ActionItemBase { 53 | private _desktop: Object = { 54 | position: "right", 55 | systemIcon: undefined 56 | }; 57 | private _label: QLabel; 58 | public styles: StyleList = new StyleList(this); 59 | public nativeViewProtected: QWidget; 60 | 61 | public createNativeView() { 62 | const view = new QWidget(); 63 | view.setObjectName(uniqId()); 64 | view.setLayout(new FlexLayout()); 65 | 66 | return view; 67 | } 68 | 69 | public initNativeView(): void { 70 | super.initNativeView(); 71 | const view = this.nativeViewProtected; 72 | view.addEventListener(WidgetEventTypes.MouseButtonPress, menuItemClickListener.bind(this, this)); 73 | (view).menuItemClickListener = menuItemClickListener; 74 | 75 | this._label = new QLabel(); 76 | view.layout.addWidget(this._label); 77 | } 78 | 79 | public _addViewToNativeVisualTree(child: View): boolean { 80 | super._addViewToNativeVisualTree(child); 81 | 82 | this.styles 83 | .set("align-items", "center") 84 | .set("justify-content", "space-around") 85 | .apply(); 86 | 87 | if (this.nativeViewProtected && child.nativeViewProtected) { 88 | (this.nativeViewProtected.layout).removeWidget(this._label); 89 | this.nativeViewProtected.layout.addWidget(child.nativeViewProtected); 90 | 91 | return true; 92 | } 93 | 94 | return false; 95 | } 96 | 97 | public _removeViewFromNativeVisualTree(child: View): void { 98 | super._removeViewFromNativeVisualTree(child); 99 | 100 | if (child.nativeViewProtected) { 101 | (this.nativeViewProtected.layout).removeWidget(child.nativeViewProtected); 102 | } 103 | } 104 | 105 | public disposeNativeView() { 106 | (this.nativeViewProtected).menuItemClickListener = null; 107 | super.disposeNativeView(); 108 | } 109 | 110 | set text(value) { 111 | if (this._label) { 112 | this._label.setText(value); 113 | } 114 | } 115 | 116 | public get desktop(): Object { 117 | return this._desktop; 118 | } 119 | public set desktop(value: Object) { 120 | throw new Error("ActionItem.settings is read-only"); 121 | } 122 | } 123 | 124 | export class NavigationButton extends ActionItem { 125 | _navigationItem: any; 126 | 127 | public _onVisibilityChanged(visibility: string): void { 128 | if (this._navigationItem) { 129 | const visible: boolean = visibility === "visible"; 130 | this._navigationItem.setHidesBackButtonAnimated(!visible, true); 131 | } 132 | } 133 | } 134 | 135 | export class ActionBar extends ActionBarBase { 136 | nativeViewProtected: QWidget; 137 | public styles: StyleList; 138 | private _titleWidget: QWidget; 139 | private _leftWidget: QWidget; 140 | private _rightWidget: QWidget; 141 | 142 | get desktop(): QWidget { 143 | const page = super.page; 144 | if (!page || !page.parent) { 145 | return; 146 | } 147 | 148 | return this.nativeViewProtected; 149 | } 150 | 151 | public createNativeView(): QWidget { 152 | const view = new QWidget(); 153 | view.setObjectName(uniqId()); 154 | view.setLayout(new FlexLayout()); 155 | 156 | this._leftWidget = new QWidget(); 157 | this._leftWidget.setObjectName(uniqId()); 158 | this._leftWidget.setLayout(new FlexLayout()); 159 | this._leftWidget.setInlineStyle("background-color: red; flex-direction: row; flex: 1; align-items: center; justify-content: flex-start;"); 160 | 161 | view.layout.addWidget(this._leftWidget); 162 | 163 | this._titleWidget = new QWidget(); 164 | this._titleWidget.setObjectName(uniqId()); 165 | this._titleWidget.setLayout(new FlexLayout()); 166 | this._titleWidget.setInlineStyle("background-color: orange; flex: 0; align-items: center; justify-content: center;"); 167 | 168 | view.layout.addWidget(this._titleWidget); 169 | 170 | this._rightWidget = new QWidget(); 171 | this._rightWidget.setObjectName(uniqId()); 172 | this._rightWidget.setLayout(new FlexLayout()); 173 | this._rightWidget.setInlineStyle("background-color: red; flex-direction: row; flex: 1; align-items: center; justify-content: flex-end;"); 174 | 175 | view.layout.addWidget(this._rightWidget); 176 | 177 | return view; 178 | } 179 | 180 | initNativeView() { 181 | this.update(); 182 | 183 | this.styles 184 | .set("flex", 1) 185 | .set("height", "60") 186 | .apply(); 187 | } 188 | 189 | public _addChildFromBuilder(name: string, value: any) { 190 | if (value instanceof NavigationButton) { 191 | (this).navigationButton = value; 192 | } else if (value instanceof ActionItem) { 193 | (this).actionItems.addItem(value); 194 | } else if (value instanceof View) { 195 | (this).titleView = value; 196 | } 197 | } 198 | 199 | public get _getActualSize(): any { 200 | const page = super.page; 201 | // Page should be attached to frame to update the action bar. 202 | if (!page || !page.frame) { 203 | return; 204 | } 205 | if (!page) { 206 | return { width: 0, height: 0 }; 207 | } 208 | 209 | // const frame = navBar.frame; 210 | // const size = frame.size; 211 | // const width = layout.toDevicePixels(size.width); 212 | // const height = layout.toDevicePixels(size.height); 213 | 214 | return { width: "100%", height: 100 }; 215 | } 216 | 217 | public layoutInternal(): void { 218 | const { width, height } = this._getActualSize; 219 | const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY); 220 | const heightSpec = layout.makeMeasureSpec(height, layout.EXACTLY); 221 | 222 | (this).measure(widthSpec, heightSpec); 223 | (this).layout(0, 0, width, height, false); 224 | } 225 | 226 | public _updateTitleAndTitleView() { 227 | if (!(this).titleView) { 228 | // No title view - show the title 229 | let title = (this).title; 230 | if (title !== undefined) { 231 | if (this.desktop) { 232 | this.desktop.setWindowTitle(title); 233 | } 234 | 235 | const label = new QLabel(); 236 | label.setText(title); 237 | this._titleWidget.layout.addWidget(label); 238 | } 239 | } 240 | } 241 | 242 | public update() { 243 | const page = super.page; 244 | // Page should be attached to frame to update the action bar. 245 | if (!page || !page.frame) { 246 | return; 247 | } 248 | 249 | 250 | // Add menu items 251 | this._addActionItems(); 252 | 253 | // Set title 254 | this._updateTitleAndTitleView(); 255 | 256 | // Set home icon 257 | this._updateIcon(); 258 | 259 | // Set navigation button 260 | this._updateNavigationButton(); 261 | 262 | } 263 | 264 | public _updateNavigationButton() { 265 | const navButton = (this).navigationButton; 266 | if (navButton && isVisible(navButton)) { 267 | const systemIcon = navButton["desktop"].systemIcon; 268 | 269 | // Set navigation content descripion, used by screen readers for the vision-impaired users 270 | // this.nativeViewProtected.text = navButton.text || null; 271 | 272 | let navBtn = new WeakRef(navButton); 273 | this.nativeViewProtected.addEventListener(WidgetEventTypes.MouseButtonPress, function (v) { 274 | let owner = navBtn.get(); 275 | if (owner) { 276 | owner._raiseTap(); 277 | } 278 | }); 279 | } 280 | else { 281 | //this.nativeViewProtected.setNavigationIcon(null); 282 | } 283 | } 284 | 285 | public _updateIcon() { 286 | //this.nativeViewProtected.icon = this.desktop.icon; 287 | // let visibility = getIconVisibility(this.android.iconVisibility); 288 | // if (visibility) { 289 | // let icon = this.web.icon; 290 | // if (icon !== undefined) { 291 | // let drawableOrId = getDrawableOrResourceId(icon, appResources); 292 | // if (drawableOrId) { 293 | // this.nativeViewProtected.setLogo(drawableOrId); 294 | // } 295 | // } 296 | // else { 297 | // let defaultIcon = application.android.nativeApp.getApplicationInfo().icon; 298 | // this.nativeViewProtected.setLogo(defaultIcon); 299 | // } 300 | // } 301 | // else { 302 | // this.nativeViewProtected.setLogo(null); 303 | // } 304 | } 305 | 306 | public _addActionItems() { 307 | let items = (this).actionItems.getVisibleItems(); 308 | 309 | for (let i = 0; i < items.length; i++) { 310 | let item = items[i]; 311 | 312 | // if (item.icon) { 313 | // //item.nativeViewProtected["icon"] = item.icon; 314 | // } 315 | 316 | if (item.desktop["position"] === "left") { 317 | //item.nativeViewProtected["position"] = item.desktop["position"]; 318 | } 319 | 320 | if (item.nativeViewProtected) { 321 | const layout = this[`_${item.desktop["position"]}Widget`].layout; 322 | 323 | layout.removeWidget(item.nativeViewProtected); 324 | layout.addWidget(item.nativeViewProtected); 325 | } 326 | } 327 | } 328 | 329 | public _onTitlePropertyChanged() { 330 | if (this.nativeViewProtected) { 331 | this._updateTitleAndTitleView(); 332 | } 333 | } 334 | 335 | public _onIconPropertyChanged() { 336 | if (this.nativeViewProtected) { 337 | this._updateIcon(); 338 | } 339 | } 340 | 341 | public _addViewToNativeVisualTree(child: View, atIndex: number = Number.MAX_VALUE): boolean { 342 | super._addViewToNativeVisualTree(child); 343 | 344 | if (this.nativeViewProtected && child.nativeViewProtected) { 345 | const layout = this[`_${child.desktop["position"]}Widget`].layout; 346 | 347 | layout.removeWidget(child.nativeViewProtected); 348 | layout.addWidget(child.nativeViewProtected); 349 | 350 | return true; 351 | } 352 | 353 | return false; 354 | } 355 | 356 | public _removeViewFromNativeVisualTree(child: View): void { 357 | super._removeViewFromNativeVisualTree(child); 358 | 359 | if (child.nativeViewProtected) { 360 | this[`_${child.desktop["position"]}Widget`].layout.removeWidget(child.nativeViewProtected); 361 | } 362 | } 363 | 364 | // getTitle() { 365 | // return this.nativeViewProtected.querySelector(".ns-action-bar__title"); 366 | // } 367 | 368 | private updateColors(navBar: QWidget) { 369 | const color = (this).color; 370 | const bgColor = (this).backgroundColor; 371 | 372 | this.styles 373 | .set("color", color) 374 | .set("background-color", bgColor) 375 | .apply(); 376 | } 377 | 378 | private setColor(navBar: QWidget, color?: Color) { 379 | if (color) { 380 | this.styles.set("color", color.desktop).apply(); 381 | } else { 382 | this.styles.set("color", "default").apply(); 383 | } 384 | } 385 | 386 | private updateFlatness(navBar) { 387 | if ((this).flat) { 388 | // navBar.setBackgroundImageForBarMetrics(UIImage.new(), UIBarMetrics.Default); 389 | // navBar.shadowImage = UIImage.new(); 390 | navBar.translucent = false; 391 | } else { 392 | navBar.setBackgroundImageForBarMetrics(null, null); 393 | navBar.shadowImage = null; 394 | navBar.translucent = true; 395 | } 396 | } 397 | 398 | public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) { 399 | // const width = layout.getMeasureSpecSize(widthMeasureSpec); 400 | // const height = layout.getMeasureSpecSize(heightMeasureSpec); 401 | // 402 | // if (this.titleView) { 403 | // View.measureChild(this, this.titleView, UNSPECIFIED, UNSPECIFIED); 404 | // } 405 | // 406 | // this.actionItems.getItems().forEach((actionItem) => { 407 | // const actionView = actionItem.actionView; 408 | // if (actionView) { 409 | // View.measureChild(this, actionView, UNSPECIFIED, UNSPECIFIED); 410 | // } 411 | // }); 412 | // 413 | // // We ignore our width/height, minWidth/minHeight dimensions because it is against Apple policy to change height of NavigationBar. 414 | // this.setMeasuredDimension(width, height); 415 | } 416 | 417 | public onLayout(left: number, top: number, right: number, bottom: number) { 418 | const titleView = (this).titleView; 419 | if (titleView) { 420 | if (majorVersion > 10) { 421 | // On iOS 11 titleView is wrapped in another view that is centered with constraints. 422 | ViewCommon.layoutChild(this, titleView, 0, 0, titleView.getMeasuredWidth(), titleView.getMeasuredHeight()); 423 | } else { 424 | // On iOS <11 titleView is direct child of UINavigationBar so we give it full width and leave 425 | // the layout to center it. 426 | ViewCommon.layoutChild(this, titleView, 0, 0, right - left, bottom - top); 427 | } 428 | } 429 | 430 | (this).actionItems.getItems().forEach((actionItem) => { 431 | const actionView = actionItem.actionView; 432 | if (actionView && actionView.ios) { 433 | const measuredWidth = actionView.getMeasuredWidth(); 434 | const measuredHeight = actionView.getMeasuredHeight(); 435 | ViewCommon.layoutChild(this, actionView, 0, 0, measuredWidth, measuredHeight); 436 | } 437 | }); 438 | 439 | super.onLayout(left, top, right, bottom); 440 | } 441 | 442 | public layoutNativeView(left: number, top: number, right: number, bottom: number) { 443 | return; 444 | } 445 | 446 | private get navBar(): any { 447 | const page = super.page; 448 | // Page should be attached to frame to update the action bar. 449 | if (!page || !page.frame) { 450 | return undefined; 451 | } 452 | 453 | // return (page.frame.ios.controller).navigationBar; 454 | } 455 | 456 | [colorProperty.getDefault](): any { 457 | return this.styles.get("color"); 458 | } 459 | [colorProperty.setNative](value: Color) { 460 | let color = value instanceof Color ? value.desktop : value; 461 | this.styles.set("color", color).apply(); 462 | } 463 | 464 | [backgroundColorProperty.getDefault](): any { 465 | // This getter is never called. 466 | // CssAnimationProperty use default value form their constructor. 467 | return this.styles.get("background-color"); 468 | } 469 | [backgroundColorProperty.setNative](value: Color) { 470 | if (this.desktop) { 471 | let color = value instanceof Color ? value.desktop : value; 472 | this.styles.set("background-color", color).apply(); 473 | } 474 | } 475 | 476 | [backgroundInternalProperty.getDefault]() { 477 | return null; 478 | } 479 | [backgroundInternalProperty.setNative](value) { // tslint:disable-line 480 | } 481 | 482 | [flatProperty.setNative](value: boolean) { // tslint:disable-line 483 | const navBar = this.navBar; 484 | if (navBar) { 485 | this.updateFlatness(navBar); 486 | } 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /core/ui/button/button.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ButtonBase, 3 | PseudoClassHandler, 4 | TextAlignment, 5 | textAlignmentProperty, 6 | zIndexProperty 7 | } from "@nativescript/core/ui/button/button-common"; 8 | import {profile} from "@nativescript/core/profiling"; 9 | import {device} from "@nativescript/core/platform"; 10 | import lazy from "@nativescript/core/utils/lazy"; 11 | import {QPushButton, WidgetEventTypes} from "@nodegui/nodegui"; 12 | import {uniqId} from "../../utils/utils.desktop"; 13 | import {View} from "../core/view/view.desktop"; 14 | 15 | export * from "@nativescript/core/ui/button/button-common"; 16 | 17 | const sdkVersion = lazy(() => parseInt(device.sdkVersion)); 18 | 19 | let ClickListener: any; 20 | let APILEVEL: number; 21 | //let AndroidButton: typeof android.widget.Button; 22 | 23 | function initializeClickListener(): void { 24 | if (ClickListener) { 25 | return; 26 | } 27 | 28 | class ClickListenerImpl { 29 | private owner: any; 30 | 31 | constructor(owner) { 32 | this.owner = owner; 33 | } 34 | 35 | public onClick(v): void { 36 | if (this.owner) { 37 | this.owner._emit(ButtonBase.tapEvent); 38 | } 39 | } 40 | } 41 | 42 | ClickListener = ClickListenerImpl; 43 | } 44 | 45 | export class Button extends ButtonBase { 46 | nativeViewProtected: QPushButton; 47 | 48 | private _stateListAnimator: any; 49 | private _highlightedHandler: () => void; 50 | private _releasedHandler: () => void; 51 | 52 | @profile 53 | public createNativeView() { 54 | const button = new QPushButton(); 55 | button.setObjectName(uniqId()); 56 | 57 | return button; 58 | } 59 | 60 | public initNativeView(): void { 61 | super.initNativeView(); 62 | const nativeView = this.nativeViewProtected; 63 | initializeClickListener(); 64 | const clickListener = new ClickListener(this); 65 | nativeView.addEventListener(WidgetEventTypes.MouseButtonPress, clickListener.onClick.bind(clickListener)); 66 | (nativeView).clickListener = clickListener; 67 | this._updateButtonStateChangeHandler(true); 68 | 69 | (this).styles 70 | .apply(); 71 | } 72 | 73 | public disposeNativeView() { 74 | if (this.nativeViewProtected) { 75 | (this.nativeViewProtected).clickListener.owner = null; 76 | } 77 | super.disposeNativeView(); 78 | } 79 | 80 | public resetNativeView(): void { 81 | super.resetNativeView(); 82 | 83 | // if (this._stateListAnimator && APILEVEL >= 21) { 84 | // (this.nativeViewProtected).setStateListAnimator(this._stateListAnimator); 85 | // this._stateListAnimator = undefined; 86 | // } 87 | } 88 | 89 | @PseudoClassHandler("normal", "highlighted", "pressed", "active") 90 | _updateButtonStateChangeHandler(subscribe: boolean) { 91 | if (this.nativeViewProtected) { 92 | if (subscribe) { 93 | this._highlightedHandler = this._highlightedHandler || (() => { 94 | console.log(); 95 | super._goToVisualState("highlighted"); 96 | }); 97 | this._releasedHandler = this._releasedHandler || (() => { 98 | console.log(); 99 | super._goToVisualState("normal"); 100 | }); 101 | this.nativeViewProtected.addEventListener("pressed", this._highlightedHandler); 102 | this.nativeViewProtected.addEventListener("released", this._releasedHandler); 103 | } else { 104 | this.nativeViewProtected.removeEventListener("pressed", this._highlightedHandler); 105 | this.nativeViewProtected.removeEventListener("released", this._releasedHandler); 106 | } 107 | } 108 | } 109 | 110 | // [paddingTopProperty.getDefault](): Length { 111 | // return { value: this._defaultPaddingTop, unit: "px" }; 112 | // } 113 | // [paddingTopProperty.setNative](value: Length) { 114 | // this.nativeViewProtected.setInlineStyle(`padding-top: ${value}`); 115 | // } 116 | // 117 | // [paddingRightProperty.getDefault](): Length { 118 | // return { value: this._defaultPaddingRight, unit: "px" }; 119 | // } 120 | // [paddingRightProperty.setNative](value: Length) { 121 | // this.nativeViewProtected.setInlineStyle(`padding-right: ${value}`); 122 | // } 123 | // 124 | // [paddingBottomProperty.getDefault](): Length { 125 | // return { value: this._defaultPaddingBottom, unit: "px" }; 126 | // } 127 | // [paddingBottomProperty.setNative](value: Length) { 128 | // this.nativeViewProtected.setInlineStyle(`padding-bottom: ${value}`); 129 | // } 130 | // 131 | // [paddingLeftProperty.getDefault](): Length { 132 | // return { value: this._defaultPaddingLeft, unit: "px" }; 133 | // } 134 | // [paddingLeftProperty.setNative](value: Length) { 135 | // this.nativeViewProtected.setInlineStyle(`padding-left: ${value}`); 136 | // } 137 | 138 | [zIndexProperty.setNative](value: number) { 139 | // API >= 21 140 | // if (APILEVEL >= 21) { 141 | // const nativeView = this.nativeViewProtected; 142 | // if (!this._stateListAnimator) { 143 | // this._stateListAnimator = (nativeView).getStateListAnimator(); 144 | // } 145 | // (nativeView).setStateListAnimator(null); 146 | // } 147 | // 148 | // org.nativescript.widgets.ViewHelper.setZIndex(this.nativeViewProtected, value); 149 | } 150 | 151 | [textAlignmentProperty.setNative](value: TextAlignment) { 152 | // Button initial value is center. 153 | const newValue = value === "initial" ? "center" : value; 154 | super[textAlignmentProperty.setNative](newValue); 155 | } 156 | 157 | protected getDefaultElevation(): number { 158 | if (sdkVersion() < 21) { 159 | return 0; 160 | } 161 | 162 | // NOTE: Button widget has StateListAnimator that defines the elevation value and 163 | // at the time of the getDefault() query the animator is not applied yet so we 164 | // return the hardcoded @dimen/button_elevation_material value 2dp here instead 165 | return 2; 166 | } 167 | 168 | protected getDefaultDynamicElevationOffset(): number { 169 | if (sdkVersion() < 21) { 170 | return 0; 171 | } 172 | 173 | return 4; // 4dp @dimen/button_pressed_z_material 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /core/ui/core/view/view-helper/view-helper.desktop.ts: -------------------------------------------------------------------------------- 1 | export * from "@nativescript/core/ui/core/view/view-helper/view-helper-common"; 2 | -------------------------------------------------------------------------------- /core/ui/dialogs/dialogs.desktop.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Android specific dialogs functions implementation. 3 | */ 4 | import { DialogOptions, ConfirmOptions, PromptOptions, PromptResult, LoginOptions, LoginResult, ActionOptions } from "@nativescript/core/ui/dialogs"; 5 | import { getLabelColor, getButtonColors, isDialogOptions, inputType, capitalizationType, ALERT, OK, CONFIRM, CANCEL, PROMPT, parseLoginOptions } from "@nativescript/core/ui/dialogs/dialogs-common"; 6 | import { android as androidApp } from "@nativescript/core/application"; 7 | 8 | export * from "@nativescript/core/ui/dialogs/dialogs-common"; 9 | 10 | function isString(value): value is string { 11 | return typeof value === "string"; 12 | } 13 | 14 | function createAlertDialog(options?: DialogOptions): any { 15 | // const alert = new android.app.AlertDialog.Builder(androidApp.foregroundActivity); 16 | // alert.setTitle(options && isString(options.title) ? options.title : ""); 17 | // alert.setMessage(options && isString(options.message) ? options.message : ""); 18 | // if (options && options.cancelable === false) { 19 | // alert.setCancelable(false); 20 | // } 21 | // 22 | return alert; 23 | } 24 | 25 | function showDialog(builder: any) { 26 | // const dlg = builder.show(); 27 | // 28 | // const labelColor = getLabelColor(); 29 | // if (labelColor) { 30 | // const textViewId = dlg.getContext().getResources().getIdentifier("android:id/alertTitle", null, null); 31 | // if (textViewId) { 32 | // const tv = dlg.findViewById(textViewId); 33 | // if (tv) { 34 | // tv.setTextColor(labelColor.android); 35 | // } 36 | // } 37 | // 38 | // const messageTextViewId = dlg.getContext().getResources().getIdentifier("android:id/message", null, null); 39 | // if (messageTextViewId) { 40 | // const messageTextView = dlg.findViewById(messageTextViewId); 41 | // if (messageTextView) { 42 | // messageTextView.setTextColor(labelColor.android); 43 | // } 44 | // } 45 | // } 46 | // 47 | // let { color, backgroundColor } = getButtonColors(); 48 | // 49 | // if (color) { 50 | // let buttons: android.widget.Button[] = []; 51 | // for (let i = 0; i < 3; i++) { 52 | // let id = dlg.getContext().getResources().getIdentifier("android:id/button" + i, null, null); 53 | // buttons[i] = dlg.findViewById(id); 54 | // } 55 | // 56 | // buttons.forEach(button => { 57 | // if (button) { 58 | // if (color) { 59 | // button.setTextColor(color.android); 60 | // } 61 | // if (backgroundColor) { 62 | // button.setBackgroundColor(backgroundColor.android); 63 | // } 64 | // } 65 | // }); 66 | // } 67 | } 68 | 69 | function addButtonsToAlertDialog(alert, options: ConfirmOptions, 70 | callback: Function): void { 71 | // 72 | // if (!options) { 73 | // return; 74 | // } 75 | // 76 | // if (options.okButtonText) { 77 | // alert.setPositiveButton(options.okButtonText, new android.content.DialogInterface.OnClickListener({ 78 | // onClick: function (dialog: android.content.DialogInterface, id: number) { 79 | // dialog.cancel(); 80 | // callback(true); 81 | // } 82 | // })); 83 | // } 84 | // 85 | // if (options.cancelButtonText) { 86 | // alert.setNegativeButton(options.cancelButtonText, new android.content.DialogInterface.OnClickListener({ 87 | // onClick: function (dialog: android.content.DialogInterface, id: number) { 88 | // dialog.cancel(); 89 | // callback(false); 90 | // } 91 | // })); 92 | // } 93 | // 94 | // if (options.neutralButtonText) { 95 | // alert.setNeutralButton(options.neutralButtonText, new android.content.DialogInterface.OnClickListener({ 96 | // onClick: function (dialog: android.content.DialogInterface, id: number) { 97 | // dialog.cancel(); 98 | // callback(undefined); 99 | // } 100 | // })); 101 | // } 102 | // alert.setOnDismissListener(new android.content.DialogInterface.OnDismissListener({ 103 | // onDismiss: function () { 104 | // callback(false); 105 | // } 106 | // })); 107 | } 108 | 109 | export function alert(arg: any): Promise { 110 | return new Promise((resolve, reject) => { 111 | try { 112 | const options = !isDialogOptions(arg) ? { title: ALERT, okButtonText: OK, message: arg + "" } : arg; 113 | 114 | const alert = createAlertDialog(options); 115 | 116 | // alert.setPositiveButton(options.okButtonText, new android.content.DialogInterface.OnClickListener({ 117 | // onClick: function (dialog: android.content.DialogInterface, id: number) { 118 | // dialog.cancel(); 119 | // resolve(); 120 | // } 121 | // })); 122 | // alert.setOnDismissListener(new android.content.DialogInterface.OnDismissListener({ 123 | // onDismiss: function () { 124 | // resolve(); 125 | // } 126 | // })); 127 | 128 | showDialog(alert); 129 | 130 | } catch (ex) { 131 | reject(ex); 132 | } 133 | }); 134 | } 135 | 136 | export function confirm(arg: any): Promise { 137 | return new Promise((resolve, reject) => { 138 | try { 139 | const options = !isDialogOptions(arg) ? { title: CONFIRM, okButtonText: OK, cancelButtonText: CANCEL, message: arg + "" } : arg; 140 | const alert = createAlertDialog(options); 141 | 142 | addButtonsToAlertDialog(alert, options, function (result) { resolve(result); }); 143 | 144 | showDialog(alert); 145 | 146 | } catch (ex) { 147 | reject(ex); 148 | } 149 | }); 150 | } 151 | 152 | export function prompt(arg: any): Promise { 153 | let options: PromptOptions; 154 | 155 | const defaultOptions = { 156 | title: PROMPT, 157 | okButtonText: OK, 158 | cancelButtonText: CANCEL, 159 | inputType: inputType.text, 160 | }; 161 | 162 | if (arguments.length === 1) { 163 | if (isString(arg)) { 164 | options = defaultOptions; 165 | options.message = arg; 166 | } else { 167 | options = arg; 168 | } 169 | } else if (arguments.length === 2) { 170 | if (isString(arguments[0]) && isString(arguments[1])) { 171 | options = defaultOptions; 172 | options.message = arguments[0]; 173 | options.defaultText = arguments[1]; 174 | } 175 | } 176 | 177 | return new Promise((resolve, reject) => { 178 | try { 179 | const alert = createAlertDialog(options); 180 | 181 | // const input = new android.widget.EditText(androidApp.foregroundActivity); 182 | // 183 | // if (options) { 184 | // if (options.inputType === inputType.password) { 185 | // input.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); 186 | // } else if (options.inputType === inputType.email) { 187 | // input.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); 188 | // } else if (options.inputType === inputType.number) { 189 | // input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); 190 | // } else if (options.inputType === inputType.decimal) { 191 | // input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL); 192 | // } else if (options.inputType === inputType.phone) { 193 | // input.setInputType(android.text.InputType.TYPE_CLASS_PHONE); 194 | // } 195 | // 196 | // switch (options.capitalizationType) { 197 | // case capitalizationType.all: { 198 | // input.setInputType(input.getInputType() | android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS); 199 | // break; 200 | // } 201 | // case capitalizationType.sentences: { 202 | // input.setInputType(input.getInputType() | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 203 | // break; 204 | // } 205 | // case capitalizationType.words: { 206 | // input.setInputType(input.getInputType() | android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS); 207 | // break; 208 | // } 209 | // } 210 | // } 211 | // 212 | // input.setText(options && options.defaultText || ""); 213 | // 214 | // alert.setView(input); 215 | // 216 | // const getText = function () { return input.getText().toString(); }; 217 | // 218 | // addButtonsToAlertDialog(alert, options, function (r) { resolve({ result: r, text: getText() }); }); 219 | 220 | showDialog(alert); 221 | 222 | } catch (ex) { 223 | reject(ex); 224 | } 225 | 226 | }); 227 | } 228 | 229 | export function login(...args: any[]): Promise { 230 | let options: LoginOptions = parseLoginOptions(args); 231 | 232 | return new Promise((resolve, reject) => { 233 | try { 234 | // const context = androidApp.foregroundActivity; 235 | 236 | const alert = createAlertDialog(options); 237 | 238 | // const userNameInput = new android.widget.EditText(context); 239 | // 240 | // userNameInput.setHint(options.userNameHint ? options.userNameHint : ""); 241 | // userNameInput.setText(options.userName ? options.userName : ""); 242 | // 243 | // const passwordInput = new android.widget.EditText(context); 244 | // passwordInput.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); 245 | // passwordInput.setTypeface(android.graphics.Typeface.DEFAULT); 246 | // 247 | // passwordInput.setHint(options.passwordHint ? options.passwordHint : ""); 248 | // passwordInput.setText(options.password ? options.password : ""); 249 | // 250 | // const layout = new android.widget.LinearLayout(context); 251 | // layout.setOrientation(1); 252 | // layout.addView(userNameInput); 253 | // layout.addView(passwordInput); 254 | // 255 | // alert.setView(layout); 256 | // 257 | // addButtonsToAlertDialog(alert, options, function (r) { 258 | // resolve({ 259 | // result: r, 260 | // userName: userNameInput.getText().toString(), 261 | // password: passwordInput.getText().toString() 262 | // }); 263 | // }); 264 | 265 | showDialog(alert); 266 | } catch (ex) { 267 | reject(ex); 268 | } 269 | }); 270 | } 271 | 272 | export function action(arg: any): Promise { 273 | let options: ActionOptions; 274 | 275 | const defaultOptions = { title: null, cancelButtonText: CANCEL }; 276 | 277 | if (arguments.length === 1) { 278 | if (isString(arguments[0])) { 279 | options = defaultOptions; 280 | options.message = arguments[0]; 281 | } else { 282 | options = arguments[0]; 283 | } 284 | } else if (arguments.length === 2) { 285 | if (isString(arguments[0]) && isString(arguments[1])) { 286 | options = defaultOptions; 287 | options.message = arguments[0]; 288 | options.cancelButtonText = arguments[1]; 289 | } 290 | } else if (arguments.length === 3) { 291 | if (isString(arguments[0]) && isString(arguments[1]) && typeof arguments[2] !== "undefined") { 292 | options = defaultOptions; 293 | options.message = arguments[0]; 294 | options.cancelButtonText = arguments[1]; 295 | options.actions = arguments[2]; 296 | } 297 | } 298 | 299 | return new Promise((resolve, reject) => { 300 | try { 301 | // const activity = androidApp.foregroundActivity || androidApp.startActivity; 302 | // const alert = new android.app.AlertDialog.Builder(activity); 303 | // const message = options && isString(options.message) ? options.message : ""; 304 | // const title = options && isString(options.title) ? options.title : ""; 305 | // if (options && options.cancelable === false) { 306 | // alert.setCancelable(false); 307 | // } 308 | // 309 | // if (title) { 310 | // alert.setTitle(title); 311 | // if (!options.actions) { 312 | // alert.setMessage(message); 313 | // } 314 | // } 315 | // else { 316 | // alert.setTitle(message); 317 | // } 318 | // 319 | // if (options.actions) { 320 | // alert.setItems(options.actions, new android.content.DialogInterface.OnClickListener({ 321 | // onClick: function (dialog: android.content.DialogInterface, which: number) { 322 | // resolve(options.actions[which]); 323 | // } 324 | // })); 325 | // } 326 | // 327 | // if (isString(options.cancelButtonText)) { 328 | // alert.setNegativeButton(options.cancelButtonText, new android.content.DialogInterface.OnClickListener({ 329 | // onClick: function (dialog: android.content.DialogInterface, id: number) { 330 | // dialog.cancel(); 331 | // resolve(options.cancelButtonText); 332 | // } 333 | // })); 334 | // } 335 | // 336 | // alert.setOnDismissListener(new android.content.DialogInterface.OnDismissListener({ 337 | // onDismiss: function () { 338 | // if (isString(options.cancelButtonText)) { 339 | // resolve(options.cancelButtonText); 340 | // } else { 341 | // resolve(""); 342 | // } 343 | // } 344 | // })); 345 | 346 | showDialog(alert); 347 | 348 | } catch (ex) { 349 | reject(ex); 350 | } 351 | }); 352 | } 353 | -------------------------------------------------------------------------------- /core/ui/editable-text-base/editable-text-base.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EditableTextBase as EditableTextBaseCommon, keyboardTypeProperty, 3 | returnKeyTypeProperty, 4 | autocapitalizationTypeProperty, autocorrectProperty, FormattedString 5 | } from "@nativescript/core/ui/editable-text-base/editable-text-base-common"; 6 | 7 | export * from "@nativescript/core/ui/editable-text-base/editable-text-base-common"; 8 | 9 | export abstract class EditableTextBase extends EditableTextBaseCommon { 10 | public nativeViewProtected: any; 11 | public dismissSoftInput() { 12 | super.nativeTextViewProtected.resignFirstResponder(); 13 | super.notify({ eventName: EditableTextBaseCommon.blurEvent, object: this }); 14 | } 15 | 16 | [keyboardTypeProperty.getDefault](): "datetime" | "phone" | "number" | "url" | "email" | string { 17 | let keyboardType = super.nativeTextViewProtected.keyboardType; 18 | switch (keyboardType) { 19 | // case UIKeyboardType.NumbersAndPunctuation: 20 | // return "number"; 21 | // 22 | // case UIKeyboardType.PhonePad: 23 | // return "phone"; 24 | // 25 | // case UIKeyboardType.URL: 26 | // return "url"; 27 | // 28 | // case UIKeyboardType.EmailAddress: 29 | // return "email"; 30 | 31 | default: 32 | return keyboardType.toString(); 33 | } 34 | } 35 | [keyboardTypeProperty.setNative](value: "datetime" | "phone" | "number" | "url" | "email" | string) { 36 | // let newKeyboardType: UIKeyboardType; 37 | switch (value) { 38 | case "datetime": 39 | // newKeyboardType = UIKeyboardType.NumbersAndPunctuation; 40 | break; 41 | 42 | case "phone": 43 | // newKeyboardType = UIKeyboardType.PhonePad; 44 | break; 45 | 46 | case "number": 47 | // newKeyboardType = UIKeyboardType.NumbersAndPunctuation; 48 | break; 49 | 50 | case "url": 51 | // newKeyboardType = UIKeyboardType.URL; 52 | break 53 | ; 54 | case "email": 55 | // newKeyboardType = UIKeyboardType.EmailAddress; 56 | break; 57 | 58 | default: 59 | let kt = +value; 60 | if (!isNaN(kt)) { 61 | // newKeyboardType = kt; 62 | } else { 63 | // newKeyboardType = UIKeyboardType.Default; 64 | } 65 | break; 66 | } 67 | 68 | // this.nativeTextViewProtected.keyboardType = newKeyboardType; 69 | } 70 | 71 | [returnKeyTypeProperty.getDefault](): "done" | "next" | "go" | "search" | "send" | string { 72 | let returnKeyType = super.nativeTextViewProtected.returnKeyType; 73 | switch (returnKeyType) { 74 | // case UIReturnKeyType.Done: 75 | // return "done"; 76 | // 77 | // case UIReturnKeyType.Go: 78 | // return "go"; 79 | // 80 | // case UIReturnKeyType.Next: 81 | // return "next"; 82 | // 83 | // case UIReturnKeyType.Search: 84 | // return "search"; 85 | // 86 | // case UIReturnKeyType.Send: 87 | // return "send"; 88 | 89 | default: 90 | return returnKeyType.toString(); 91 | } 92 | } 93 | [returnKeyTypeProperty.setNative](value: "done" | "next" | "go" | "search" | "send" | string) { 94 | let newValue; 95 | switch (value) { 96 | case "done": 97 | // newValue = UIReturnKeyType.Done; 98 | break; 99 | case "go": 100 | // newValue = UIReturnKeyType.Go; 101 | break; 102 | case "next": 103 | // newValue = UIReturnKeyType.Next; 104 | break; 105 | case "search": 106 | // newValue = UIReturnKeyType.Search; 107 | break; 108 | case "send": 109 | // newValue = UIReturnKeyType.Send; 110 | break; 111 | default: 112 | let rkt = +value; 113 | if (!isNaN(rkt)) { 114 | // newValue = rkt; 115 | } else { 116 | // newValue = UIKeyboardType.Default; 117 | } 118 | break; 119 | } 120 | 121 | super.nativeTextViewProtected.returnKeyType = newValue; 122 | } 123 | 124 | [autocapitalizationTypeProperty.getDefault](): "none" | "words" | "sentences" | "allcharacters" { 125 | let autocapitalizationType = super.nativeTextViewProtected.autocapitalizationType; 126 | switch (autocapitalizationType) { 127 | // case UITextAutocapitalizationType.None: 128 | // return "none"; 129 | // 130 | // case UITextAutocapitalizationType.Words: 131 | // return "words"; 132 | // 133 | // case UITextAutocapitalizationType.Sentences: 134 | // return "sentences"; 135 | // 136 | // case UITextAutocapitalizationType.AllCharacters: 137 | // return "allcharacters"; 138 | 139 | default: 140 | throw new Error("Invalid autocapitalizationType value:" + autocapitalizationType); 141 | } 142 | } 143 | [autocapitalizationTypeProperty.setNative](value: "none" | "words" | "sentences" | "allcharacters") { 144 | // let newValue: UITextAutocapitalizationType; 145 | switch (value) { 146 | case "none": 147 | // newValue = UITextAutocapitalizationType.None; 148 | break; 149 | case "words": 150 | // newValue = UITextAutocapitalizationType.Words; 151 | break; 152 | case "sentences": 153 | // newValue = UITextAutocapitalizationType.Sentences; 154 | break; 155 | case "allcharacters": 156 | // newValue = UITextAutocapitalizationType.AllCharacters; 157 | break; 158 | default: 159 | // newValue = UITextAutocapitalizationType.Sentences; 160 | break; 161 | } 162 | 163 | // this.nativeTextViewProtected.autocapitalizationType = newValue; 164 | } 165 | 166 | [autocorrectProperty.getDefault](): boolean | number { 167 | let autocorrectionType = super.nativeTextViewProtected.autocorrectionType; 168 | switch (autocorrectionType) { 169 | // case UITextAutocorrectionType.Yes: 170 | // return true; 171 | // case UITextAutocorrectionType.No: 172 | // return false; 173 | // case UITextAutocorrectionType.Default: 174 | // return autocorrectionType; 175 | } 176 | 177 | return true; 178 | } 179 | [autocorrectProperty.setNative](value: boolean | number) { 180 | // let newValue: UITextAutocorrectionType; 181 | if (typeof value === "number") { 182 | // newValue = UITextAutocorrectionType.Default; 183 | } else if (value) { 184 | // newValue = UITextAutocorrectionType.Yes; 185 | } else { 186 | // newValue = UITextAutocorrectionType.No; 187 | } 188 | 189 | // this.nativeTextViewProtected.autocorrectionType = newValue; 190 | } 191 | } 192 | 193 | export function _updateCharactersInRangeReplacementString(formattedText: FormattedString, rangeLocation: number, rangeLength: number, replacementString: string): void { 194 | let deletingText = !replacementString; 195 | let currentLocation = 0; 196 | for (let i = 0, length = formattedText.spans.length; i < length; i++) { 197 | let span = formattedText.spans.getItem(i); 198 | if (currentLocation <= rangeLocation && rangeLocation < (currentLocation + span.text.length)) { 199 | let newText = splice(span.text, rangeLocation - currentLocation, deletingText ? rangeLength : 0, replacementString); 200 | span._setTextInternal(newText); 201 | 202 | return; 203 | } 204 | currentLocation += span.text.length; 205 | } 206 | } 207 | 208 | /* 209 | * @param {String} value The string to splice. 210 | * @param {number} start Index at which to start changing the string. 211 | * @param {number} delCount An integer indicating the number of old chars to remove. 212 | * @param {string} newSubStr The String that is spliced in. 213 | * @return {string} A new string with the spliced substring.function splice(value: string, start: number, delCount: number, newSubStr: string) { 214 | */ 215 | function splice(value: string, start: number, delCount: number, newSubStr: string) { 216 | return value.slice(0, start) + newSubStr + value.slice(start + Math.abs(delCount)); 217 | } 218 | -------------------------------------------------------------------------------- /core/ui/gestures/gestures.desktop.ts: -------------------------------------------------------------------------------- 1 | // Definitions. 2 | import {EventData, View} from "@nativescript/core/ui/core/view"; 3 | // Types. 4 | import {GesturesObserverBase, GestureTypes, SwipeDirection, toString, TouchAction} from "@nativescript/core/ui/gestures/gestures-common"; 5 | 6 | export * from "@nativescript/core/ui/gestures/gestures-common"; 7 | 8 | export interface GestureEventData extends EventData { 9 | /** 10 | * Gets the type of the gesture. 11 | */ 12 | type: GestureTypes; 13 | /** 14 | * Gets the view which originates the gesture. 15 | */ 16 | view: View; 17 | /** 18 | * Gets the underlying native iOS specific [UIGestureRecognizer](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/). 19 | */ 20 | ios: any /* UIGestureRecognizer */; 21 | /** 22 | * Gets the underlying native android specific [gesture detector](http://developer.android.com/reference/android/view/GestureDetector.html). 23 | */ 24 | android: any, 25 | 26 | desktop: any 27 | } 28 | 29 | export interface GestureEventDataWithState extends GestureEventData { 30 | state: number; 31 | } 32 | 33 | /** 34 | * Provides gesture event data for pinch gesture. 35 | */ 36 | export interface PinchGestureEventData extends GestureEventDataWithState { 37 | scale: number; 38 | 39 | getFocusX(): number; 40 | getFocusY(): number; 41 | } 42 | 43 | /** 44 | * Provides gesture event data for swipe gesture. 45 | */ 46 | export interface SwipeGestureEventData extends GestureEventData { 47 | direction: SwipeDirection; 48 | } 49 | 50 | /** 51 | * Provides gesture event data for pan gesture. 52 | */ 53 | export interface PanGestureEventData extends GestureEventDataWithState { 54 | deltaX: number; 55 | deltaY: number; 56 | } 57 | 58 | /** 59 | * Provides gesture event data for rotation gesture. 60 | */ 61 | export interface RotationGestureEventData extends GestureEventDataWithState { 62 | rotation: number; 63 | } 64 | 65 | 66 | 67 | export function observe(target: View, type: GestureTypes, callback: (args: GestureEventData) => void, context?: any): GesturesObserver { 68 | const observer = new GesturesObserver(target, callback, context); 69 | observer.observe(type); 70 | 71 | return observer; 72 | } 73 | 74 | // class UIGestureRecognizerDelegateImpl extends NSObject implements UIGestureRecognizerDelegate { 75 | // public static ObjCProtocols = [UIGestureRecognizerDelegate]; 76 | // 77 | // public gestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer(gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer): boolean { 78 | // return true; 79 | // } 80 | // 81 | // public gestureRecognizerShouldRequireFailureOfGestureRecognizer(gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer): boolean { 82 | // // If both gesture recognizers are of type UITapGestureRecognizer & one of them is a doubleTap, 83 | // // we must require a failure. 84 | // if (gestureRecognizer instanceof UITapGestureRecognizer 85 | // && otherGestureRecognizer instanceof UITapGestureRecognizer 86 | // && otherGestureRecognizer.numberOfTapsRequired === 2) { 87 | // return true; 88 | // } 89 | // 90 | // return false; 91 | // } 92 | // } 93 | // let recognizerDelegateInstance: UIGestureRecognizerDelegateImpl = UIGestureRecognizerDelegateImpl.new(); 94 | 95 | // class UIGestureRecognizerImpl extends NSObject { 96 | // public static ObjCExposedMethods = { 97 | // "recognize": { returns: interop.types.void, params: [UIGestureRecognizer] } 98 | // }; 99 | // 100 | // private _owner: WeakRef; 101 | // private _type: any; 102 | // private _callback: Function; 103 | // private _context: any; 104 | // 105 | // public static initWithOwnerTypeCallback(owner: WeakRef, type: any, callback?: Function, thisArg?: any): UIGestureRecognizerImpl { 106 | // const handler = UIGestureRecognizerImpl.new(); 107 | // handler._owner = owner; 108 | // handler._type = type; 109 | // 110 | // if (callback) { 111 | // handler._callback = callback; 112 | // } 113 | // 114 | // if (thisArg) { 115 | // handler._context = thisArg; 116 | // } 117 | // 118 | // return handler; 119 | // } 120 | // 121 | // public recognize(recognizer: UIGestureRecognizer): void { 122 | // const owner = this._owner.get(); 123 | // const callback = this._callback ? this._callback : (owner ? owner.callback : null); 124 | // const typeParam = this._type; 125 | // const target = owner ? owner.target : undefined; 126 | // 127 | // const args = { 128 | // type: typeParam, 129 | // view: target, 130 | // ios: recognizer, 131 | // android: undefined, 132 | // object: target, 133 | // eventName: toString(typeParam), 134 | // }; 135 | // 136 | // if (callback) { 137 | // callback.call(this._context, args); 138 | // } 139 | // } 140 | // } 141 | 142 | export class GesturesObserver extends GesturesObserverBase { 143 | private _recognizers: {}; 144 | 145 | private _onTargetLoaded: (data: EventData) => void; 146 | private _onTargetUnloaded: (data: EventData) => void; 147 | 148 | constructor(target: View, callback: (args: GestureEventData) => void, context: any) { 149 | super(target, callback, context); 150 | this._recognizers = {}; 151 | } 152 | 153 | public androidOnTouchEvent(motionEvent): void { 154 | // 155 | } 156 | 157 | public observe(type: GestureTypes) { 158 | if (super.target) { 159 | super.type = type; 160 | this._onTargetLoaded = args => { 161 | this._attach(super.target, type); 162 | }; 163 | this._onTargetUnloaded = args => { 164 | this._detach(); 165 | }; 166 | 167 | super.target.on("loaded", this._onTargetLoaded); 168 | super.target.on("unloaded", this._onTargetUnloaded); 169 | 170 | if (super.target.isLoaded) { 171 | this._attach(super.target, type); 172 | } 173 | } 174 | } 175 | 176 | private _attach(target: View, type: GestureTypes) { 177 | this._detach(); 178 | 179 | if (target && target.nativeViewProtected && target.nativeViewProtected.addGestureRecognizer) { 180 | const nativeView = target.nativeViewProtected; 181 | 182 | if (type & GestureTypes.tap) { 183 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.tap)); 184 | } 185 | 186 | if (type & GestureTypes.doubleTap) { 187 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.doubleTap)); 188 | } 189 | 190 | if (type & GestureTypes.pinch) { 191 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.pinch, args => { 192 | this._executeCallback(_getPinchData(args)); 193 | })); 194 | } 195 | 196 | if (type & GestureTypes.pan) { 197 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.pan, args => { 198 | this._executeCallback(_getPanData(args, target.nativeViewProtected)); 199 | })); 200 | } 201 | 202 | // if (type & GestureTypes.swipe) { 203 | // nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.swipe, args => { 204 | // this._executeCallback(_getSwipeData(args)); 205 | // }, UISwipeGestureRecognizerDirection.Down)); 206 | // 207 | // nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.swipe, args => { 208 | // this._executeCallback(_getSwipeData(args)); 209 | // }, UISwipeGestureRecognizerDirection.Left)); 210 | // 211 | // nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.swipe, args => { 212 | // this._executeCallback(_getSwipeData(args)); 213 | // }, UISwipeGestureRecognizerDirection.Right)); 214 | // 215 | // nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.swipe, args => { 216 | // this._executeCallback(_getSwipeData(args)); 217 | // }, UISwipeGestureRecognizerDirection.Up)); 218 | // } 219 | 220 | if (type & GestureTypes.rotation) { 221 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.rotation, args => { 222 | this._executeCallback(_getRotationData(args)); 223 | })); 224 | } 225 | 226 | if (type & GestureTypes.longPress) { 227 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.longPress)); 228 | } 229 | 230 | if (type & GestureTypes.touch) { 231 | nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.touch)); 232 | } 233 | } 234 | } 235 | 236 | private _detach() { 237 | if (super.target && super.target.nativeViewProtected) { 238 | for (let name in this._recognizers) { 239 | if (this._recognizers.hasOwnProperty(name)) { 240 | let item = this._recognizers[name]; 241 | super.target.nativeViewProtected.removeGestureRecognizer(item.recognizer); 242 | 243 | item.recognizer = null; 244 | item.target = null; 245 | } 246 | } 247 | this._recognizers = {}; 248 | } 249 | } 250 | 251 | public disconnect() { 252 | this._detach(); 253 | 254 | if (super.target) { 255 | super.target.off("loaded", this._onTargetLoaded); 256 | super.target.off("unloaded", this._onTargetUnloaded); 257 | 258 | this._onTargetLoaded = null; 259 | this._onTargetUnloaded = null; 260 | } 261 | // clears target, context and callback references 262 | super.disconnect(); 263 | } 264 | 265 | public _executeCallback(args: GestureEventData) { 266 | if (super.callback) { 267 | super.callback.call(super.context, args); 268 | } 269 | } 270 | 271 | private _createRecognizer(type: GestureTypes, callback?: (args: GestureEventData) => void, swipeDirection?: any): any { 272 | let recognizer: any; 273 | let name = toString(type); 274 | // const target = _createUIGestureRecognizerTarget(this, type, callback, this.context); 275 | // const recognizerType = _getUIGestureRecognizerType(type); 276 | 277 | // if (recognizerType) { 278 | // recognizer = recognizerType.alloc().initWithTargetAction(target, "recognize"); 279 | // 280 | // if (type === GestureTypes.swipe && swipeDirection) { 281 | // name = name + swipeDirection.toString(); 282 | // (recognizer).direction = swipeDirection; 283 | // } else if (type === GestureTypes.touch) { 284 | // (recognizer).observer = this; 285 | // } else if (type === GestureTypes.doubleTap) { 286 | // (recognizer).numberOfTapsRequired = 2; 287 | // } 288 | // 289 | // if (recognizer) { 290 | // recognizer.delegate = recognizerDelegateInstance; 291 | // this._recognizers[name] = { recognizer: recognizer, target: target }; 292 | // } 293 | // } 294 | 295 | return recognizer; 296 | } 297 | } 298 | 299 | function _createUIGestureRecognizerTarget(owner: GesturesObserver, type: GestureTypes, callback?: (args: GestureEventData) => void, context?: any): any { 300 | // return UIGestureRecognizerImpl.initWithOwnerTypeCallback(new WeakRef(owner), type, callback, context); 301 | } 302 | 303 | interface RecognizerCache { 304 | recognizer: any; 305 | target: any; 306 | } 307 | 308 | // function _getUIGestureRecognizerType(type: GestureTypes): any { 309 | // let nativeType = null; 310 | // 311 | // if (type === GestureTypes.tap) { 312 | // nativeType = UITapGestureRecognizer; 313 | // } else if (type === GestureTypes.doubleTap) { 314 | // nativeType = UITapGestureRecognizer; 315 | // } else if (type === GestureTypes.pinch) { 316 | // nativeType = UIPinchGestureRecognizer; 317 | // } else if (type === GestureTypes.pan) { 318 | // nativeType = UIPanGestureRecognizer; 319 | // } else if (type === GestureTypes.swipe) { 320 | // nativeType = UISwipeGestureRecognizer; 321 | // } else if (type === GestureTypes.rotation) { 322 | // nativeType = UIRotationGestureRecognizer; 323 | // } else if (type === GestureTypes.longPress) { 324 | // nativeType = UILongPressGestureRecognizer; 325 | // } else if (type === GestureTypes.touch) { 326 | // nativeType = TouchGestureRecognizer; 327 | // } 328 | // 329 | // return nativeType; 330 | // } 331 | 332 | function getState(recognizer: any): any { 333 | // if (recognizer.state === UIGestureRecognizerState.Began) { 334 | // return GestureStateTypes.began; 335 | // } else if (recognizer.state === UIGestureRecognizerState.Cancelled) { 336 | // return GestureStateTypes.cancelled; 337 | // } else if (recognizer.state === UIGestureRecognizerState.Changed) { 338 | // return GestureStateTypes.changed; 339 | // } else if (recognizer.state === UIGestureRecognizerState.Ended) { 340 | // return GestureStateTypes.ended; 341 | // } 342 | } 343 | 344 | function _getSwipeDirection(direction: any): SwipeDirection { 345 | // if (direction === UISwipeGestureRecognizerDirection.Down) { 346 | // return SwipeDirection.down; 347 | // } else if (direction === UISwipeGestureRecognizerDirection.Left) { 348 | // return SwipeDirection.left; 349 | // } else if (direction === UISwipeGestureRecognizerDirection.Right) { 350 | // return SwipeDirection.right; 351 | // } else if (direction === UISwipeGestureRecognizerDirection.Up) { 352 | // return SwipeDirection.up; 353 | // } 354 | return SwipeDirection.down; 355 | } 356 | 357 | function _getPinchData(args: GestureEventData): PinchGestureEventData { 358 | const recognizer = args.desktop; 359 | const center = recognizer.locationInView(args.view.nativeViewProtected); 360 | 361 | return { 362 | type: args.type, 363 | view: args.view, 364 | ios: undefined, 365 | android: undefined, 366 | desktop: args.desktop, 367 | scale: recognizer.scale, 368 | getFocusX: () => center.x, 369 | getFocusY: () => center.y, 370 | object: args.view, 371 | eventName: toString(args.type), 372 | state: getState(recognizer) 373 | }; 374 | } 375 | 376 | function _getSwipeData(args: GestureEventData): SwipeGestureEventData { 377 | const recognizer = args.desktop; 378 | 379 | return { 380 | type: args.type, 381 | view: args.view, 382 | ios: undefined, 383 | android: undefined, 384 | desktop: args.desktop, 385 | direction: _getSwipeDirection(recognizer.direction), 386 | object: args.view, 387 | eventName: toString(args.type), 388 | }; 389 | } 390 | 391 | function _getPanData(args: GestureEventData, view: any): PanGestureEventData { 392 | const recognizer = args.desktop; 393 | 394 | return { 395 | type: args.type, 396 | view: args.view, 397 | ios: undefined, 398 | android: undefined, 399 | desktop: args.desktop, 400 | deltaX: recognizer.translationInView(view).x, 401 | deltaY: recognizer.translationInView(view).y, 402 | object: args.view, 403 | eventName: toString(args.type), 404 | state: getState(recognizer) 405 | }; 406 | } 407 | 408 | function _getRotationData(args: GestureEventData): RotationGestureEventData { 409 | const recognizer = args.desktop; 410 | 411 | return { 412 | type: args.type, 413 | view: args.view, 414 | ios: undefined, 415 | android: undefined, 416 | desktop: args.desktop, 417 | rotation: recognizer.rotation * (180.0 / Math.PI), 418 | object: args.view, 419 | eventName: toString(args.type), 420 | state: getState(recognizer) 421 | }; 422 | } 423 | 424 | class TouchGestureRecognizer { 425 | public observer: GesturesObserver; 426 | private _eventData: TouchGestureEventData; 427 | 428 | touchesBeganWithEvent(touches: Set, event: any): void { 429 | this.executeCallback(TouchAction.down, touches, event); 430 | // if (this.view) { 431 | // this.view.touchesBeganWithEvent(touches, event); 432 | // } 433 | } 434 | 435 | touchesMovedWithEvent(touches: Set, event: any): void { 436 | this.executeCallback(TouchAction.move, touches, event); 437 | // if (this.view) { 438 | // this.view.touchesMovedWithEvent(touches, event); 439 | // } 440 | } 441 | 442 | touchesEndedWithEvent(touches: Set, event: any): void { 443 | this.executeCallback(TouchAction.up, touches, event); 444 | // if (this.view) { 445 | // this.view.touchesEndedWithEvent(touches, event); 446 | // } 447 | } 448 | 449 | touchesCancelledWithEvent(touches: Set, event: any): void { 450 | this.executeCallback(TouchAction.cancel, touches, event); 451 | // if (this.view) { 452 | // this.view.touchesCancelledWithEvent(touches, event); 453 | // } 454 | } 455 | 456 | private executeCallback(action: string, touches: Set, event: any): void { 457 | if (!this._eventData) { 458 | this._eventData = new TouchGestureEventData(); 459 | } 460 | 461 | this._eventData.prepare((this.observer).target, action, touches, event); 462 | // this.observer._executeCallback(this._eventData); 463 | } 464 | } 465 | 466 | class Pointer implements Pointer { 467 | public android: any = undefined; 468 | public ios = undefined; 469 | public desktop = undefined; 470 | 471 | private _view: View; 472 | 473 | private _location: any; 474 | 475 | private get location(): any { 476 | if (!this._location) { 477 | this._location = this.desktop.locationInView(this._view.nativeViewProtected); 478 | } 479 | 480 | return this._location; 481 | } 482 | 483 | constructor(touch: any, targetView: View) { 484 | this.desktop = touch; 485 | this._view = targetView; 486 | } 487 | 488 | getX(): number { 489 | return this.location.x; 490 | } 491 | 492 | getY(): number { 493 | return this.location.y; 494 | } 495 | } 496 | 497 | class TouchGestureEventData implements TouchGestureEventData { 498 | eventName: string = toString(GestureTypes.touch); 499 | type: GestureTypes = GestureTypes.touch; 500 | android: any = undefined; 501 | action: string; 502 | view: View; 503 | desktop: { touches: Set, event: any }; 504 | object: any; 505 | 506 | private _activePointers: Array; 507 | private _allPointers: Array; 508 | private _mainPointer: any; 509 | 510 | public prepare(view: View, action: string, touches: Set, event: any) { 511 | this.action = action; 512 | this.view = view; 513 | this.object = view; 514 | this.desktop = { 515 | touches: touches, 516 | event: event 517 | }; 518 | 519 | this._mainPointer = undefined; 520 | this._activePointers = undefined; 521 | this._allPointers = undefined; 522 | } 523 | 524 | getPointerCount(): number { 525 | return this.desktop.event.allTouches.count; 526 | } 527 | 528 | private getMainPointer(): any { 529 | if (this._mainPointer === undefined) { 530 | this._mainPointer = this.desktop.touches; 531 | } 532 | 533 | return this._mainPointer; 534 | } 535 | 536 | getActivePointers(): Array { 537 | if (!this._activePointers) { 538 | this._activePointers = []; 539 | 540 | // for (let i = 0, nsArr = this.desktop.touches; i < nsArr.size; i++) { 541 | // this._activePointers.push(new Pointer(nsArr.objectAtIndex(i), this.view)); 542 | // } 543 | } 544 | 545 | return this._activePointers; 546 | } 547 | 548 | getAllPointers(): Array { 549 | if (!this._allPointers) { 550 | this._allPointers = []; 551 | 552 | let nsArr = this.desktop.event.allTouches.allObjects; 553 | for (let i = 0; i < nsArr.count; i++) { 554 | this._allPointers.push(new Pointer(nsArr.objectAtIndex(i), this.view)); 555 | } 556 | } 557 | 558 | return this._allPointers; 559 | } 560 | 561 | getX(): number { 562 | return this.getMainPointer().locationInView(this.view.nativeViewProtected).x; 563 | } 564 | 565 | getY(): number { 566 | return this.getMainPointer().locationInView(this.view.nativeViewProtected).y; 567 | } 568 | } 569 | -------------------------------------------------------------------------------- /core/ui/label/label.desktop.ts: -------------------------------------------------------------------------------- 1 | import { Label as LabelDefinition } from "@nativescript/core/ui/label"; 2 | import { TextBase, WhiteSpace, whiteSpaceProperty, booleanConverter, CSSType } from "@nativescript/core/ui/text-base"; 3 | import { profile } from "@nativescript/core/profiling"; 4 | import { QLabel } from "@nodegui/nodegui"; 5 | import {uniqId} from "../../utils/utils.desktop"; 6 | 7 | export * from "@nativescript/core/ui/text-base"; 8 | 9 | //let TextView: typeof android.widget.TextView; 10 | 11 | @CSSType("Label") 12 | export class Label extends TextBase implements LabelDefinition { 13 | nativeViewProtected: QLabel; 14 | 15 | get desktop(): QLabel { 16 | return this.nativeTextViewProtected; 17 | } 18 | 19 | get textWrap(): boolean { 20 | return this.style.whiteSpace === "normal"; 21 | } 22 | set textWrap(value: boolean) { 23 | if (typeof value === "string") { 24 | value = booleanConverter(value); 25 | } 26 | 27 | this.style.whiteSpace = value ? "normal" : "nowrap"; 28 | } 29 | 30 | @profile 31 | public createNativeView() { 32 | const label = new QLabel(); 33 | label.setObjectName(uniqId()); 34 | //label.setInlineStyle("background-color: red"); 35 | 36 | return label; // new TextView(this._context); 37 | } 38 | 39 | public initNativeView(): void { 40 | super.initNativeView(); 41 | const textView = this.nativeTextViewProtected; 42 | //textView.setSingleLine(true); 43 | // textView.setEllipsize(android.text.TextUtils.TruncateAt.END); 44 | } 45 | 46 | [whiteSpaceProperty.setNative](value: WhiteSpace) { 47 | // Label initial value is no-wrap. set in initNativeView 48 | const newValue = value === "initial" ? "nowrap" : value; 49 | super[whiteSpaceProperty.setNative](newValue); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/ui/layouts/grid-layout/grid-layout.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GridLayoutBase, ItemSpec as ItemSpecBase, View, layout, 3 | rowProperty, columnProperty, rowSpanProperty, columnSpanProperty, GridUnitType 4 | } from "@nativescript/core/ui/layouts/grid-layout/grid-layout-common"; 5 | import { QGridLayout, QWidget } from "@nodegui/nodegui"; 6 | import { uniqId } from "../../../utils/utils.desktop"; 7 | 8 | export * from "@nativescript/core/ui/layouts/grid-layout/grid-layout-common"; 9 | 10 | // function makeNativeSetter(setter: (lp: org.nativescript.widgets.CommonLayoutParams, value: T) => void) { 11 | // return function (this: View, value: T) { 12 | // const nativeView: android.view.View = this.nativeViewProtected; 13 | // const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams(); 14 | // if (lp instanceof org.nativescript.widgets.CommonLayoutParams) { 15 | // setter(lp, value); 16 | // nativeView.setLayoutParams(lp); 17 | // } 18 | // }; 19 | // } 20 | // 21 | // View.prototype[rowProperty.setNative] = makeNativeSetter((lp, value) => lp.row = value); 22 | // View.prototype[columnProperty.setNative] = makeNativeSetter((lp, value) => lp.column = value); 23 | // View.prototype[rowSpanProperty.setNative] = makeNativeSetter((lp, value) => lp.rowSpan = value); 24 | // View.prototype[columnSpanProperty.setNative] = makeNativeSetter((lp, value) => lp.columnSpan = value); 25 | 26 | function createNativeSpec(itemSpec: ItemSpecBase): any { 27 | switch (itemSpec.gridUnitType) { 28 | // case GridUnitType.AUTO: 29 | // return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.auto); 30 | // 31 | // case GridUnitType.STAR: 32 | // return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.star); 33 | // 34 | // case GridUnitType.PIXEL: 35 | // return new org.nativescript.widgets.ItemSpec(itemSpec.value * layout.getDisplayDensity(), org.nativescript.widgets.GridUnitType.pixel); 36 | 37 | default: 38 | throw new Error("Invalid gridUnitType: " + itemSpec.gridUnitType); 39 | } 40 | } 41 | 42 | export class ItemSpec extends ItemSpecBase { 43 | nativeSpec: any; 44 | 45 | public get actualLength(): number { 46 | if (this.nativeSpec) { 47 | return Math.round(this.nativeSpec.getActualLength() / layout.getDisplayDensity()); 48 | } 49 | 50 | return 0; 51 | } 52 | } 53 | 54 | export class GridLayout extends GridLayoutBase { 55 | nativeViewProtected: QWidget; 56 | 57 | public createNativeView() { 58 | const view = new QWidget(); 59 | const layout = new QGridLayout(); 60 | view.setObjectName(uniqId()); 61 | view.setLayout(layout); 62 | 63 | return view; // new org.nativescript.widgets.GridLayout(this._context); 64 | } 65 | 66 | public initNativeView(): void { 67 | super.initNativeView(); 68 | // Update native GridLayout 69 | super.rowsInternal.forEach((itemSpec: ItemSpec, index, rows) => { super._onRowAdded(itemSpec); }, this); 70 | super.columnsInternal.forEach((itemSpec: ItemSpec, index, rows) => { super._onColumnAdded(itemSpec); }, this); 71 | } 72 | 73 | public resetNativeView() { 74 | // Update native GridLayout 75 | for (let i = super.rowsInternal.length; i--; i >= 0) { 76 | const itemSpec = super.rowsInternal[i]; 77 | super._onRowRemoved(itemSpec, i); 78 | } 79 | 80 | for (let i = super.columnsInternal.length; i--; i >= 0) { 81 | const itemSpec = super.columnsInternal[i]; 82 | super._onColumnRemoved(itemSpec, i); 83 | } 84 | 85 | super.resetNativeView(); 86 | } 87 | 88 | // public _onRowAdded(itemSpec: ItemSpec) { 89 | // if (this.nativeViewProtected) { 90 | // const nativeSpec = createNativeSpec(itemSpec); 91 | // itemSpec.nativeSpec = nativeSpec; 92 | // this.nativeViewProtected.addRow(nativeSpec); 93 | // } 94 | // } 95 | // 96 | // public _onColumnAdded(itemSpec: ItemSpec) { 97 | // if (this.nativeViewProtected) { 98 | // const nativeSpec = createNativeSpec(itemSpec); 99 | // itemSpec.nativeSpec = nativeSpec; 100 | // this.nativeViewProtected.addColumn(nativeSpec); 101 | // } 102 | // } 103 | // 104 | // public _onRowRemoved(itemSpec: ItemSpec, index: number) { 105 | // itemSpec.nativeSpec = null; 106 | // if (this.nativeViewProtected) { 107 | // this.nativeViewProtected.removeRowAt(index); 108 | // } 109 | // } 110 | // 111 | // public _onColumnRemoved(itemSpec: ItemSpec, index: number) { 112 | // itemSpec.nativeSpec = null; 113 | // if (this.nativeViewProtected) { 114 | // this.nativeViewProtected.removeColumnAt(index); 115 | // } 116 | // } 117 | 118 | protected invalidate(): void { 119 | // No need to request layout for android because it will be done in the native call. 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /core/ui/layouts/layout-base.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty, 3 | paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length 4 | } from "@nativescript/core/ui/layouts/layout-base-common"; 5 | 6 | export * from "@nativescript/core/ui/layouts/layout-base-common"; 7 | 8 | export class LayoutBase extends LayoutBaseCommon { 9 | 10 | [clipToBoundsProperty.getDefault](): boolean { 11 | return true; 12 | } 13 | [clipToBoundsProperty.setNative](value: boolean) { 14 | console.warn(`clipToBounds with value false is not supported on Android. You can use this.android.getParent().setClipChildren(false) as an alternative`); 15 | } 16 | 17 | [isPassThroughParentEnabledProperty.setNative](value: boolean) { 18 | (this).nativeViewProtected.setPassThroughParent(value); 19 | } 20 | 21 | [paddingTopProperty.getDefault](): Length { 22 | return { value: (this)._defaultPaddingTop, unit: "px" }; 23 | } 24 | [paddingTopProperty.setNative](value: Length) { 25 | // org.nativescript.widgets.ViewHelper.setPaddingTop(this.nativeViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderTopWidth, 0)); 26 | } 27 | 28 | [paddingRightProperty.getDefault](): Length { 29 | return { value: (this)._defaultPaddingRight, unit: "px" }; 30 | } 31 | [paddingRightProperty.setNative](value: Length) { 32 | // org.nativescript.widgets.ViewHelper.setPaddingRight(this.nativeViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderRightWidth, 0)); 33 | } 34 | 35 | [paddingBottomProperty.getDefault](): Length { 36 | return { value: (this)._defaultPaddingBottom, unit: "px" }; 37 | } 38 | [paddingBottomProperty.setNative](value: Length) { 39 | // org.nativescript.widgets.ViewHelper.setPaddingBottom(this.nativeViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderBottomWidth, 0)); 40 | } 41 | 42 | [paddingLeftProperty.getDefault](): Length { 43 | return { value: (this)._defaultPaddingLeft, unit: "px" }; 44 | } 45 | [paddingLeftProperty.setNative](value: Length) { 46 | // org.nativescript.widgets.ViewHelper.setPaddingLeft(this.nativeViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/ui/layouts/stack-layout/stack-layout.desktop.ts: -------------------------------------------------------------------------------- 1 | import { StackLayoutBase, orientationProperty } from "@nativescript/core/ui/layouts/stack-layout/stack-layout-common"; 2 | import { FlexLayout, QWidget } from "@nodegui/nodegui"; 3 | import { uniqId } from "../../../utils/utils.desktop"; 4 | import { StyleList } from "../../core/view/view.desktop"; 5 | 6 | export * from "@nativescript/core/ui/layouts/stack-layout/stack-layout-common"; 7 | 8 | export class StackLayout extends StackLayoutBase { 9 | nativeViewProtected: QWidget; 10 | styles: StyleList = new StyleList(this); 11 | 12 | public createNativeView() { 13 | const view = new QWidget(); 14 | view.setObjectName(uniqId()); 15 | view.setLayout(new FlexLayout()); 16 | 17 | return view; 18 | } 19 | 20 | initNativeView(): void { 21 | this.styles 22 | .set("flex-direction", "column") 23 | .set("height", "auto") 24 | .apply(); 25 | } 26 | 27 | [orientationProperty.setNative](value: "horizontal" | "vertical") { 28 | this.styles 29 | .set("flex-direction", value === "vertical" ? "column" : "row") 30 | .apply(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/ui/page/page.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PageBase, 3 | Color, 4 | actionBarHiddenProperty, 5 | statusBarStyleProperty, 6 | ViewBase 7 | } from "@nativescript/core/ui/page/page-common"; 8 | import {ActionBar} from "@nativescript/core/ui/action-bar"; 9 | import {StyleList, View} from "../core/view/view.desktop" 10 | // @ts-ignore 11 | import {FlexLayout, QWidget, WidgetEventTypes} from "@nodegui/nodegui"; 12 | import { device } from "@nativescript/core/platform"; 13 | import { profile } from "@nativescript/core/profiling"; 14 | import { uniqId } from "../../utils/utils.desktop"; 15 | 16 | export * from "@nativescript/core/ui/page/page-common"; 17 | 18 | const SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000; 19 | const STATUS_BAR_LIGHT_BCKG = -657931; 20 | const STATUS_BAR_DARK_BCKG = 1711276032; 21 | 22 | export class Page extends PageBase { 23 | public nativeViewProtected: QWidget; 24 | // public styles: StyleList = new StyleList(this); 25 | private _actionBarView: View; 26 | private _contentView: View; 27 | private _actionBarWidget: QWidget; 28 | private _contentWidget: QWidget; 29 | 30 | public createNativeView() { 31 | const view = new QWidget(); 32 | view.setObjectName(uniqId()); 33 | view.setLayout(new FlexLayout()); 34 | 35 | this._actionBarWidget = new QWidget; 36 | this._actionBarWidget.setObjectName(uniqId()); 37 | this._actionBarWidget.setLayout(new FlexLayout()); 38 | 39 | this._contentWidget = new QWidget; 40 | this._contentWidget.setObjectName(uniqId()); 41 | this._contentWidget.setLayout(new FlexLayout()); 42 | 43 | view.layout.addWidget(this._actionBarWidget); 44 | view.layout.addWidget(this._contentWidget); 45 | 46 | return view; 47 | } 48 | 49 | public initNativeView(): void { 50 | super.initNativeView(); 51 | 52 | (this).styles 53 | .set("flex-direction", "column") 54 | .apply(); 55 | } 56 | 57 | public _addViewToNativeVisualTree(view: View, atIndex?: number): boolean { 58 | if (this.nativeViewProtected && view.nativeViewProtected) { 59 | if (view instanceof ActionBar) { 60 | this._actionBarView = view; 61 | this._actionBarWidget.layout.addWidget(view.nativeViewProtected); 62 | } else { 63 | this._contentView = view; 64 | this._contentWidget.layout.addWidget(view.nativeViewProtected); 65 | 66 | (view).styles 67 | .set("flex", "1") 68 | } 69 | 70 | (view).styles.apply(); 71 | } 72 | 73 | this.nativeViewProtected.removeEventListener(WidgetEventTypes.Resize, this._resizeHandler.bind(this)); 74 | this.nativeViewProtected.addEventListener(WidgetEventTypes.Resize, this._resizeHandler.bind(this)); 75 | 76 | return true; 77 | } 78 | 79 | _resizeHandler() { 80 | super.eachChild((view): boolean => { 81 | const size = this.nativeViewProtected.size(); 82 | 83 | this._actionBarWidget.setInlineStyle(`max-height: 60; width: ${size.width()}`); 84 | this._contentWidget.setInlineStyle(`width: ${size.width()}; height: ${size.height() - 60}`); 85 | 86 | this._actionBarWidget.layout.update(); 87 | this._contentWidget.layout.update(); 88 | 89 | return true; 90 | }); 91 | } 92 | 93 | _removeViewFromNativeVisualTree(view: ViewBase): void { 94 | this.nativeViewProtected.removeEventListener(WidgetEventTypes.Resize, this._resizeHandler.bind(this)); 95 | } 96 | 97 | @profile 98 | public onLoaded() { 99 | super.onLoaded(); 100 | if (super.actionBarHidden !== undefined) { 101 | this.updateActionBar(); 102 | } 103 | } 104 | 105 | private updateActionBar() { 106 | super.actionBar.update(); 107 | } 108 | 109 | [actionBarHiddenProperty.setNative](value: boolean) { 110 | this.updateActionBar(); 111 | } 112 | 113 | [statusBarStyleProperty.getDefault](): { color: number, systemUiVisibility: number } { 114 | // if (device.sdkVersion >= "21") { 115 | // const window = (this._context).getWindow(); 116 | // const decorView = window.getDecorView(); 117 | // 118 | // return { 119 | // color: (window).getStatusBarColor(), 120 | // systemUiVisibility: decorView.getSystemUiVisibility() 121 | // }; 122 | // } 123 | 124 | return null; 125 | } 126 | 127 | [statusBarStyleProperty.setNative](value: "dark" | "light" | { color: number, systemUiVisibility: number }) { 128 | if (device.sdkVersion >= "21") { 129 | //const window = (this._context).getWindow(); 130 | //const decorView = window.getDecorView(); 131 | 132 | // if (value === "light") { 133 | // (window).setStatusBarColor(STATUS_BAR_LIGHT_BCKG); 134 | // decorView.setSystemUiVisibility(SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 135 | // 136 | // } else if (value === "dark") { 137 | // (window).setStatusBarColor(STATUS_BAR_DARK_BCKG); 138 | // decorView.setSystemUiVisibility(0); 139 | // } else { 140 | // (window).setStatusBarColor(value.color); 141 | // decorView.setSystemUiVisibility(value.systemUiVisibility); 142 | // } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /core/ui/progress/progress.desktop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ProgressBase, Color, valueProperty, maxValueProperty, 3 | backgroundInternalProperty, Style, CssProperty, makeParser, makeValidator, InheritedCssProperty 4 | } from "@nativescript/core/ui/progress/progress-common"; 5 | import { QProgressBar } from "@nodegui/nodegui"; 6 | import { StyleList, View } from "../core/view/view.desktop"; 7 | import { uniqId } from "../../utils/utils.desktop"; 8 | import { TextAlignment } from "@nativescript/core/ui/text-base"; 9 | 10 | export * from "@nativescript/core/ui/progress/progress-common"; 11 | 12 | export const desktopChunkColorProperty = new CssProperty({ 13 | name: "desktopChunkColor", 14 | cssName: "desktop-chunk-color", 15 | equalityComparer: Color.equals, 16 | valueConverter: (v) => new Color(v) 17 | }); 18 | desktopChunkColorProperty.register(Style); 19 | 20 | export const desktopChunkMarginProperty = new CssProperty({ 21 | name: "desktopChunkMargin", 22 | cssName: "desktop-chunk-margin" 23 | }); 24 | desktopChunkMarginProperty.register(Style); 25 | 26 | export const desktopChunkWidthProperty = new CssProperty({ 27 | name: "desktopChunkWidth", 28 | cssName: "desktop-chunk-width" 29 | }); 30 | desktopChunkWidthProperty.register(Style); 31 | 32 | const textAlignmentConverter = makeParser(makeValidator("initial", "left", "center", "right")); 33 | export const textAlignmentProperty = new InheritedCssProperty({ name: "textAlignment", cssName: "text-align", defaultValue: "initial", valueConverter: textAlignmentConverter }); 34 | textAlignmentProperty.register(Style); 35 | 36 | export class Progress extends ProgressBase { 37 | nativeViewProtected: QProgressBar; 38 | styles: StyleList = new StyleList(this, "QProgressBar", ["chunk"]); 39 | 40 | createNativeView() { 41 | const view = new QProgressBar(); 42 | view.setObjectName(uniqId()); 43 | 44 | return view; 45 | } 46 | 47 | get desktop() { 48 | return this.nativeViewProtected; 49 | } 50 | 51 | [valueProperty.getDefault](): number { 52 | return 0; 53 | } 54 | [valueProperty.setNative](value: number) { 55 | this.desktop.setValue(value); 56 | } 57 | 58 | [maxValueProperty.getDefault](): number { 59 | return 100; 60 | } 61 | [maxValueProperty.setNative](value: number) { 62 | this.desktop.setMaximum(value); 63 | } 64 | 65 | [desktopChunkColorProperty.setNative](value: Color) { 66 | this.styles 67 | .set("background-color", value instanceof Color ? value.desktop : value, "chunk") 68 | .apply(); 69 | } 70 | 71 | [desktopChunkMarginProperty.setNative](value: number) { 72 | this.styles 73 | .set("margin", value, "chunk") 74 | .apply(); 75 | } 76 | 77 | [desktopChunkWidthProperty.setNative](value: Color) { 78 | this.styles 79 | .set("width", value, "chunk") 80 | .apply(); 81 | } 82 | 83 | [textAlignmentProperty.setNative](value: string) { 84 | this.styles 85 | .set("text-align", value) 86 | .apply(); 87 | } 88 | 89 | [backgroundInternalProperty.getDefault]() { 90 | return null; 91 | } 92 | [backgroundInternalProperty.setNative](value: Color) { 93 | // 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /core/ui/scroll-view/scroll-view.desktop.ts: -------------------------------------------------------------------------------- 1 | import { ScrollEventData } from "@nativescript/core"; 2 | import { 3 | ScrollViewBase, layout, scrollBarIndicatorVisibleProperty, 4 | isUserInteractionEnabledProperty, isScrollEnabledProperty, ViewBase 5 | } from "@nativescript/core/ui/scroll-view/scroll-view-common"; 6 | import {QApplication, QScrollArea, QStylePixelMetric, WidgetEventTypes} from "@nodegui/nodegui"; 7 | import {View, ViewCommon} from "../core/view/view.desktop"; 8 | import { uniqId } from "../../utils/utils.desktop"; 9 | 10 | export * from "@nativescript/core/ui/scroll-view/scroll-view-common"; 11 | 12 | export class ScrollView extends ScrollViewBase { 13 | nativeViewProtected: QScrollArea; 14 | private _scrollBarSize: number; 15 | 16 | get horizontalOffset(): number { 17 | const nativeView = this.nativeViewProtected; 18 | if (!nativeView) { 19 | return 0; 20 | } 21 | 22 | // return nativeView.getScrollX() / layout.getDisplayDensity(); 23 | } 24 | 25 | get verticalOffset(): number { 26 | const nativeView = this.nativeViewProtected; 27 | if (!nativeView) { 28 | return 0; 29 | } 30 | 31 | // return nativeView.getScrollY() / layout.getDisplayDensity(); 32 | } 33 | 34 | get scrollableWidth(): number { 35 | const nativeView = this.nativeViewProtected; 36 | if (!nativeView || (this).orientation !== "horizontal") { 37 | return 0; 38 | } 39 | 40 | // return nativeView.getScrollableLength() / layout.getDisplayDensity(); 41 | } 42 | 43 | get scrollableHeight(): number { 44 | const nativeView = this.nativeViewProtected; 45 | if (!nativeView || (this).orientation !== "vertical") { 46 | return 0; 47 | } 48 | 49 | // return nativeView.getScrollableLength() / layout.getDisplayDensity(); 50 | } 51 | 52 | [isUserInteractionEnabledProperty.setNative](value: boolean) { 53 | // NOTE: different behavior on iOS & Android: 54 | // iOS disables user interaction recursively for all subviews as well 55 | // this.nativeViewProtected.setClickable(value); 56 | // this.nativeViewProtected.setFocusable(value); 57 | // this.nativeViewProtected.setScrollEnabled(value); 58 | } 59 | 60 | [isScrollEnabledProperty.getDefault]() { 61 | // return this.nativeViewProtected.getScrollEnabled(); 62 | } 63 | [isScrollEnabledProperty.setNative](value: boolean) { 64 | // this.nativeViewProtected.setScrollEnabled(value); 65 | } 66 | 67 | [scrollBarIndicatorVisibleProperty.getDefault](): boolean { 68 | return true; 69 | } 70 | [scrollBarIndicatorVisibleProperty.setNative](value: boolean) { 71 | if ((this).orientation === "horizontal") { 72 | // this.nativeViewProtected.setHorizontalScrollBarEnabled(value); 73 | } else { 74 | // this.nativeViewProtected.setVerticalScrollBarEnabled(value); 75 | } 76 | } 77 | 78 | public scrollToVerticalOffset(value: number, animated: boolean) { 79 | const nativeView = this.nativeViewProtected; 80 | if (nativeView && (this).orientation === "vertical" && (this).isScrollEnabled) { 81 | value *= layout.getDisplayDensity(); 82 | 83 | if (animated) { 84 | // nativeView.smoothScrollTo(0, value); 85 | } else { 86 | // nativeView.scrollTo(0, value); 87 | } 88 | } 89 | } 90 | 91 | public scrollToHorizontalOffset(value: number, animated: boolean) { 92 | const nativeView = this.nativeViewProtected; 93 | if (nativeView && (this).orientation === "horizontal" && (this).isScrollEnabled) { 94 | value *= layout.getDisplayDensity(); 95 | 96 | if (animated) { 97 | // nativeView.smoothScrollTo(value, 0); 98 | } else { 99 | // nativeView.scrollTo(value, 0); 100 | } 101 | } 102 | } 103 | 104 | public createNativeView() { 105 | const view = new QScrollArea(); 106 | view.setObjectName(uniqId()); 107 | view.setWidgetResizable(true); 108 | 109 | this._scrollBarSize = QApplication.style().pixelMetric(QStylePixelMetric.PM_ScrollBarExtent); 110 | 111 | return view; 112 | } 113 | 114 | _addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean { 115 | view.nativeViewProtected.show(); 116 | this.nativeViewProtected.setWidget(view.nativeViewProtected); 117 | 118 | (this).parentNode.nativeViewProtected.removeEventListener(WidgetEventTypes.Resize, this._resizeHandler.bind(this)); 119 | (this).parentNode.nativeViewProtected.addEventListener(WidgetEventTypes.Resize, this._resizeHandler.bind(this)); 120 | 121 | this._resizeHandler(); 122 | 123 | return true; 124 | } 125 | 126 | _removeViewFromNativeVisualTree(view: ViewBase): void { 127 | (this).parentNode.nativeViewProtected.removeEventListener(WidgetEventTypes.Resize, this._resizeHandler.bind(this)); 128 | } 129 | 130 | public initNativeView(): void { 131 | super.initNativeView(); 132 | // if (this._androidViewId < 0) { 133 | // this._androidViewId = android.view.View.generateViewId(); 134 | // } 135 | 136 | // this.nativeViewProtected.setId(this._androidViewId); 137 | } 138 | 139 | _resizeHandler() { 140 | super.eachChild((view): boolean => { 141 | const size = this.nativeViewProtected.size(); 142 | 143 | (view).styles 144 | .set("min-width", size.width() - this._scrollBarSize) 145 | .set("min-height", size.height() - this._scrollBarSize) 146 | .apply(); 147 | 148 | return true; 149 | }); 150 | } 151 | 152 | public _onOrientationChanged() { 153 | if (this.nativeViewProtected) { 154 | const parent = (this).parent; 155 | if (parent) { 156 | parent._removeView(this); 157 | parent._addView(this); 158 | } 159 | } 160 | } 161 | 162 | protected attachNative() { 163 | // const that = new WeakRef(this); 164 | // this.handler = new android.view.ViewTreeObserver.OnScrollChangedListener({ 165 | // onScrollChanged: function () { 166 | // const owner: ScrollView = that.get(); 167 | // if (owner) { 168 | // owner._onScrollChanged(); 169 | // } 170 | // } 171 | // }); 172 | // 173 | // this.nativeViewProtected.getViewTreeObserver().addOnScrollChangedListener(this.handler); 174 | } 175 | 176 | private _lastScrollX: number = -1; 177 | private _lastScrollY: number = -1; 178 | private _onScrollChanged() { 179 | const nativeView = this.nativeViewProtected; 180 | if (nativeView) { 181 | // Event is only raised if the scroll values differ from the last time in order to wokraround a native Android bug. 182 | // https://github.com/NativeScript/NativeScript/issues/2362 183 | // let newScrollX = nativeView.getScrollX(); 184 | // let newScrollY = nativeView.getScrollY(); 185 | // if (newScrollX !== this._lastScrollX || newScrollY !== this._lastScrollY) { 186 | // this.notify({ 187 | // object: this, 188 | // eventName: ScrollView.scrollEvent, 189 | // scrollX: newScrollX / layout.getDisplayDensity(), 190 | // scrollY: newScrollY / layout.getDisplayDensity() 191 | // }); 192 | // this._lastScrollX = newScrollX; 193 | // this._lastScrollY = newScrollY; 194 | // } 195 | } 196 | } 197 | 198 | protected dettachNative() { 199 | // this.nativeViewProtected.getViewTreeObserver().removeOnScrollChangedListener(this.handler); 200 | // this.handler = null; 201 | } 202 | } 203 | 204 | -------------------------------------------------------------------------------- /core/ui/styling/font.desktop.ts: -------------------------------------------------------------------------------- 1 | import { Font as FontBase, parseFontFamily, genericFontFamilies, FontWeight } from "@nativescript/core/ui/styling/font-common"; 2 | import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceMessageType } from "@nativescript/core/trace"; 3 | import * as application from "@nativescript/core/application"; 4 | import * as fs from "@nativescript/core/file-system"; 5 | 6 | export * from "@nativescript/core/ui/styling/font-common"; 7 | 8 | const FONTS_BASE_PATH = "/fonts/"; 9 | let typefaceCache;// = new Map(); 10 | let appAssets;//: android.content.res.AssetManager; 11 | 12 | export class Font extends FontBase { 13 | public static default = new Font(undefined, undefined, "normal", "normal"); 14 | 15 | //private _typeface: android.graphics.Typeface; 16 | 17 | constructor(family: string, size: number, style: "normal" | "italic", weight: FontWeight) { 18 | super(family, size, style, weight); 19 | } 20 | 21 | public withFontFamily(family: string): Font { 22 | return new Font(family, super.fontSize, super.fontStyle, super.fontWeight); 23 | } 24 | 25 | public withFontStyle(style: "normal" | "italic"): Font { 26 | return new Font(super.fontFamily, super.fontSize, style, super.fontWeight); 27 | } 28 | 29 | public withFontWeight(weight: FontWeight): Font { 30 | return new Font(super.fontFamily, super.fontSize, super.fontStyle, weight); 31 | } 32 | 33 | public withFontSize(size: number): Font { 34 | return new Font(super.fontFamily, size, super.fontStyle, super.fontWeight); 35 | } 36 | 37 | public getAndroidTypeface(): any { 38 | // if (!this._typeface) { 39 | // this._typeface = createTypeface(this); 40 | // } 41 | // 42 | // return this._typeface; 43 | } 44 | 45 | public getUIFont(defaultFont) { 46 | return undefined; 47 | } 48 | } 49 | 50 | function loadFontFromFile(fontFamily: string): any { 51 | appAssets = appAssets || application.android.context.getAssets(); 52 | if (!appAssets) { 53 | return null; 54 | } 55 | 56 | let result = typefaceCache.get(fontFamily); 57 | // Check for undefined explicitly as null mean we tried to load the font, but failed. 58 | if (result === undefined) { 59 | result = null; 60 | 61 | let fontAssetPath: string; 62 | const basePath = fs.path.join(fs.knownFolders.currentApp().path, "fonts", fontFamily); 63 | if (fs.File.exists(basePath + ".ttf")) { 64 | fontAssetPath = FONTS_BASE_PATH + fontFamily + ".ttf"; 65 | } 66 | else if (fs.File.exists(basePath + ".otf")) { 67 | fontAssetPath = FONTS_BASE_PATH + fontFamily + ".otf"; 68 | } 69 | else { 70 | if (traceEnabled()) { 71 | traceWrite("Could not find font file for " + fontFamily, traceCategories.Error, traceMessageType.error); 72 | } 73 | } 74 | 75 | if (fontAssetPath) { 76 | try { 77 | fontAssetPath = fs.path.join(fs.knownFolders.currentApp().path, fontAssetPath); 78 | //result = android.graphics.Typeface.createFromFile(fontAssetPath); 79 | } catch (e) { 80 | if (traceEnabled()) { 81 | traceWrite("Error loading font asset: " + fontAssetPath, traceCategories.Error, traceMessageType.error); 82 | } 83 | } 84 | } 85 | typefaceCache.set(fontFamily, result); 86 | } 87 | 88 | return result; 89 | } 90 | 91 | function createTypeface(font: Font): any { 92 | // let fontStyle = 0; 93 | // if (font.isBold) { 94 | // fontStyle |= android.graphics.Typeface.BOLD; 95 | // } 96 | // if (font.isItalic) { 97 | // fontStyle |= android.graphics.Typeface.ITALIC; 98 | // } 99 | // 100 | // //http://stackoverflow.com/questions/19691530/valid-values-for-androidfontfamily-and-what-they-map-to 101 | // const fonts = parseFontFamily(font.fontFamily); 102 | let result = null; 103 | // for (let i = 0; i < fonts.length; i++) { 104 | // switch (fonts[i].toLowerCase()) { 105 | // case genericFontFamilies.serif: 106 | // result = android.graphics.Typeface.create("serif" + getFontWeightSuffix(font.fontWeight), fontStyle); 107 | // break; 108 | // 109 | // case genericFontFamilies.sansSerif: 110 | // case genericFontFamilies.system: 111 | // result = android.graphics.Typeface.create("sans-serif" + getFontWeightSuffix(font.fontWeight), fontStyle); 112 | // break; 113 | // 114 | // case genericFontFamilies.monospace: 115 | // result = android.graphics.Typeface.create("monospace" + getFontWeightSuffix(font.fontWeight), fontStyle); 116 | // break; 117 | // 118 | // default: 119 | // result = loadFontFromFile(fonts[i]); 120 | // if (result && fontStyle) { 121 | // result = android.graphics.Typeface.create(result, fontStyle); 122 | // } 123 | // break; 124 | // } 125 | // 126 | // if (result) { 127 | // // Found the font! 128 | // break; 129 | // } 130 | // } 131 | // 132 | // if (!result) { 133 | // result = android.graphics.Typeface.create("sans-serif" + getFontWeightSuffix(font.fontWeight), fontStyle); 134 | // } 135 | 136 | return result; 137 | } 138 | 139 | // function getFontWeightSuffix(fontWeight: FontWeight): string { 140 | // switch (fontWeight) { 141 | // case FontWeight.THIN: 142 | // return android.os.Build.VERSION.SDK_INT >= 16 ? "-thin" : ""; 143 | // case FontWeight.EXTRA_LIGHT: 144 | // case FontWeight.LIGHT: 145 | // return android.os.Build.VERSION.SDK_INT >= 16 ? "-light" : ""; 146 | // case FontWeight.NORMAL: 147 | // case "400": 148 | // case undefined: 149 | // case null: 150 | // return ""; 151 | // case FontWeight.MEDIUM: 152 | // case FontWeight.SEMI_BOLD: 153 | // return android.os.Build.VERSION.SDK_INT >= 21 ? "-medium" : ""; 154 | // case FontWeight.BOLD: 155 | // case "700": 156 | // case FontWeight.EXTRA_BOLD: 157 | // return ""; 158 | // case FontWeight.BLACK: 159 | // return android.os.Build.VERSION.SDK_INT >= 21 ? "-black" : ""; 160 | // default: 161 | // throw new Error(`Invalid font weight: "${fontWeight}"`); 162 | // } 163 | // } 164 | -------------------------------------------------------------------------------- /core/ui/text-base/text-base.desktop.ts: -------------------------------------------------------------------------------- 1 | import {TextDecoration, TextAlignment, TextTransform, WhiteSpace} from "@nativescript/core/ui/text-base/text-base"; 2 | import { Font } from "@nativescript/core/ui/styling/font"; 3 | import { 4 | TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, 5 | textTransformProperty, letterSpacingProperty, colorProperty, fontInternalProperty, lineHeightProperty, 6 | FormattedString, Span, Color, isBold, resetSymbol, whiteSpaceProperty 7 | } from "@nativescript/core/ui/text-base/text-base-common"; 8 | 9 | export * from "@nativescript/core/ui/text-base/text-base-common"; 10 | 11 | import { isString } from "@nativescript/core/utils/types"; 12 | import { desktop } from "../../utils/utils.desktop"; 13 | import { QLabel, QPushButton } from "@nodegui/nodegui"; 14 | 15 | const majorVersion = desktop.MajorVersion; 16 | 17 | export class TextBase extends TextBaseCommon { 18 | 19 | public nativeViewProtected: QPushButton | QLabel; 20 | public nativeTextViewProtected: QPushButton | QLabel; 21 | 22 | [textProperty.getDefault](): number | symbol { 23 | return resetSymbol; 24 | } 25 | [textProperty.setNative](value: string | number | symbol) { 26 | const reset = value === resetSymbol; 27 | if (!reset && super.formattedText) { 28 | return; 29 | } 30 | 31 | this._setNativeText(reset); 32 | super._requestLayoutOnTextChanged(); 33 | } 34 | 35 | [formattedTextProperty.setNative](value: FormattedString) { 36 | this._setNativeText(); 37 | textProperty.nativeValueChange(this, !value ? "" : value.toString()); 38 | super._requestLayoutOnTextChanged(); 39 | } 40 | 41 | [colorProperty.getDefault](): any { 42 | let nativeView = this.nativeTextViewProtected; 43 | // if (nativeView instanceof UIButton) { 44 | // return nativeView.titleColorForState(UIControlState.Normal); 45 | // } else { 46 | //return nativeView.textColor; 47 | // } 48 | } 49 | [colorProperty.setNative](value: any) { 50 | const color = value instanceof Color ? value.desktop : value; 51 | const nativeView = this.nativeTextViewProtected; 52 | // if (nativeView instanceof UIButton) { 53 | // nativeView.setTitleColorForState(color, UIControlState.Normal); 54 | // nativeView.titleLabel.textColor = color; 55 | // } else { 56 | nativeView.setInlineStyle(`color: ${color}`); 57 | // } 58 | } 59 | 60 | [fontInternalProperty.getDefault](): any { 61 | let nativeView = this.nativeTextViewProtected; 62 | // nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView; 63 | 64 | //return nativeView.font; 65 | } 66 | [fontInternalProperty.setNative](value: any) { 67 | if (!(value instanceof Font) || !super.formattedText) { 68 | let nativeView = this.nativeTextViewProtected; 69 | // nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView; 70 | // const font = value instanceof Font ? value.getUIFont(nativeView.font) : value; 71 | //nativeView.font = value; // font; 72 | } 73 | } 74 | 75 | [textAlignmentProperty.setNative](value: TextAlignment) { 76 | const nativeView = this.nativeTextViewProtected; 77 | switch (value) { 78 | case "initial": 79 | case "left": 80 | //nativeView.textAlignment = NSTextAlignment.Left; 81 | break; 82 | case "center": 83 | //nativeView.textAlignment = NSTextAlignment.Center; 84 | break; 85 | case "right": 86 | //nativeView.textAlignment = NSTextAlignment.Right; 87 | break; 88 | } 89 | } 90 | 91 | [textDecorationProperty.setNative](value: TextDecoration) { 92 | this._setNativeText(); 93 | } 94 | 95 | [textTransformProperty.setNative](value: TextTransform) { 96 | this._setNativeText(); 97 | } 98 | 99 | [letterSpacingProperty.setNative](value: number) { 100 | this._setNativeText(); 101 | } 102 | 103 | [lineHeightProperty.setNative](value: number) { 104 | this._setNativeText(); 105 | } 106 | 107 | [whiteSpaceProperty.setNative](value: WhiteSpace) { 108 | const nativeView = this.nativeTextViewProtected; 109 | switch (value) { 110 | case "initial": 111 | case "normal": 112 | // nativeView.setSingleLine(false); 113 | // nativeView.setEllipsize(null); 114 | break; 115 | case "nowrap": 116 | // nativeView.setSingleLine(true); 117 | // nativeView.setEllipsize(android.text.TextUtils.TruncateAt.END); 118 | break; 119 | } 120 | } 121 | 122 | _setNativeText(reset: boolean = false): void { 123 | if (reset) { 124 | const nativeView = this.nativeTextViewProtected; 125 | nativeView.setText(""); 126 | 127 | return; 128 | } 129 | 130 | if ((this).formattedText) { 131 | this.setFormattedTextDecorationAndTransform(); 132 | } else { 133 | this.setTextDecorationAndTransform(); 134 | } 135 | } 136 | 137 | setFormattedTextDecorationAndTransform() { 138 | // const attrText = this.createNSMutableAttributedString(this.formattedText); 139 | // TODO: letterSpacing should be applied per Span. 140 | if ((this).letterSpacing !== 0) { 141 | // attrText.addAttributeValueRange(NSKernAttributeName, this.letterSpacing * this.nativeTextViewProtected.font.pointSize, { location: 0, length: attrText.length }); 142 | } 143 | 144 | // if (this.style.lineHeight) { 145 | // const paragraphStyle = NSMutableParagraphStyle.alloc().init(); 146 | // paragraphStyle.lineSpacing = this.lineHeight; 147 | // // make sure a possible previously set text alignment setting is not lost when line height is specified 148 | // paragraphStyle.alignment = (this.nativeTextViewProtected).textAlignment; 149 | // if (this.nativeTextViewProtected instanceof UILabel) { 150 | // // make sure a possible previously set line break mode is not lost when line height is specified 151 | // paragraphStyle.lineBreakMode = this.nativeTextViewProtected.lineBreakMode; 152 | // } 153 | // attrText.addAttributeValueRange(NSParagraphStyleAttributeName, paragraphStyle, { location: 0, length: attrText.length }); 154 | // } else if (this.nativeTextViewProtected instanceof UITextView) { 155 | // const paragraphStyle = NSMutableParagraphStyle.alloc().init(); 156 | // paragraphStyle.alignment = (this.nativeTextViewProtected).textAlignment; 157 | // attrText.addAttributeValueRange(NSParagraphStyleAttributeName, paragraphStyle, { location: 0, length: attrText.length }); 158 | // } 159 | 160 | // if (this.nativeTextViewProtected instanceof UIButton) { 161 | // this.nativeTextViewProtected.setAttributedTitleForState(attrText, UIControlState.Normal); 162 | // } 163 | // else { 164 | // this.nativeTextViewProtected.attributedText = attrText; 165 | // } 166 | 167 | this.nativeTextViewProtected.setText((this).formattedText.toString()); 168 | } 169 | 170 | setTextDecorationAndTransform() { 171 | const style = (this).style; 172 | const dict = new Map(); 173 | switch (style.textDecoration) { 174 | case "none": 175 | break; 176 | case "underline": 177 | // dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.Single); 178 | break; 179 | case "line-through": 180 | // dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.Single); 181 | break; 182 | case "underline line-through": 183 | // dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.Single); 184 | // dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.Single); 185 | break; 186 | default: 187 | throw new Error(`Invalid text decoration value: ${style.textDecoration}. Valid values are: 'none', 'underline', 'line-through', 'underline line-through'.`); 188 | } 189 | 190 | if (style.letterSpacing !== 0) { 191 | // dict.set(NSKernAttributeName, style.letterSpacing * this.nativeTextViewProtected.font.pointSize); 192 | } 193 | 194 | // const isTextView = this.nativeTextViewProtected instanceof UITextView; 195 | // if (style.lineHeight) { 196 | // const paragraphStyle = NSMutableParagraphStyle.alloc().init(); 197 | // paragraphStyle.lineSpacing = style.lineHeight; 198 | // // make sure a possible previously set text alignment setting is not lost when line height is specified 199 | // paragraphStyle.alignment = (this.nativeTextViewProtected).textAlignment; 200 | // if (this.nativeTextViewProtected instanceof UILabel) { 201 | // // make sure a possible previously set line break mode is not lost when line height is specified 202 | // paragraphStyle.lineBreakMode = this.nativeTextViewProtected.lineBreakMode; 203 | // } 204 | // dict.set(NSParagraphStyleAttributeName, paragraphStyle); 205 | // } else if (isTextView) { 206 | // const paragraphStyle = NSMutableParagraphStyle.alloc().init(); 207 | // paragraphStyle.alignment = (this.nativeTextViewProtected).textAlignment; 208 | // dict.set(NSParagraphStyleAttributeName, paragraphStyle); 209 | // } 210 | // 211 | // if (dict.size > 0 || isTextView) { 212 | // if (style.color) { 213 | // dict.set(NSForegroundColorAttributeName, style.color.ios); 214 | // } else if (majorVersion >= 13) { 215 | // dict.set(NSForegroundColorAttributeName, UIColor.labelColor); 216 | // } 217 | // } 218 | // 219 | const text = (this).text; 220 | const string = (text === undefined || text === null) ? "" : text.toString(); 221 | // const source = getTransformedText(string, this.textTransform); 222 | // if (dict.size > 0 || isTextView) { 223 | // if (isTextView) { 224 | // // UITextView's font seems to change inside. 225 | // dict.set(NSFontAttributeName, this.nativeTextViewProtected.font); 226 | // } 227 | // 228 | // const result = NSMutableAttributedString.alloc().initWithString(source); 229 | // result.setAttributesRange(dict, { location: 0, length: source.length }); 230 | // if (this.nativeTextViewProtected instanceof UIButton) { 231 | // this.nativeTextViewProtected.setAttributedTitleForState(result, UIControlState.Normal); 232 | // } else { 233 | // this.nativeTextViewProtected.attributedText = result; 234 | // } 235 | // } else { 236 | // if (this.nativeTextViewProtected instanceof UIButton) { 237 | // // Clear attributedText or title won't be affected. 238 | // this.nativeTextViewProtected.setAttributedTitleForState(null, UIControlState.Normal); 239 | // this.nativeTextViewProtected.setTitleForState(source, UIControlState.Normal); 240 | // } else { 241 | // // Clear attributedText or text won't be affected. 242 | // this.nativeTextViewProtected.attributedText = undefined; 243 | // this.nativeTextViewProtected.text = source; 244 | // } 245 | // } 246 | this.nativeTextViewProtected.setText(string); 247 | } 248 | 249 | createNSMutableAttributedString(formattedString: FormattedString): any { 250 | // let mas = NSMutableAttributedString.alloc().init(); 251 | // if (formattedString && formattedString.parent) { 252 | // for (let i = 0, spanStart = 0, length = formattedString.spans.length; i < length; i++) { 253 | // const span = formattedString.spans.getItem(i); 254 | // const text = span.text; 255 | // const textTransform = (formattedString.parent).textTransform; 256 | // let spanText = (text === null || text === undefined) ? "" : text.toString(); 257 | // if (textTransform !== "none" && textTransform !== "initial") { 258 | // spanText = getTransformedText(spanText, textTransform); 259 | // } 260 | // 261 | // const nsAttributedString = this.createMutableStringForSpan(span, spanText); 262 | // mas.insertAttributedStringAtIndex(nsAttributedString, spanStart); 263 | // spanStart += spanText.length; 264 | // } 265 | // } 266 | // 267 | // return mas; 268 | } 269 | 270 | createMutableStringForSpan(span: Span, text: string): any { 271 | // const viewFont = this.nativeTextViewProtected.font; 272 | // let attrDict = <{ key: string, value: any }>{}; 273 | // const style = span.style; 274 | // const bold = isBold(style.fontWeight); 275 | // const italic = style.fontStyle === "italic"; 276 | // 277 | // let fontFamily = span.fontFamily; 278 | // let fontSize = span.fontSize; 279 | // 280 | // if (bold || italic || fontFamily || fontSize) { 281 | // let font = new Font(style.fontFamily, style.fontSize, style.fontStyle, style.fontWeight); 282 | // let iosFont = font.getUIFont(viewFont); 283 | // attrDict[NSFontAttributeName] = iosFont; 284 | // } 285 | // 286 | // const color = span.color; 287 | // if (color) { 288 | // attrDict[NSForegroundColorAttributeName] = color.ios; 289 | // } 290 | // 291 | // // We don't use isSet function here because defaultValue for backgroundColor is null. 292 | // const backgroundColor = (style.backgroundColor 293 | // || (span.parent).backgroundColor 294 | // || ((span.parent).parent).backgroundColor); 295 | // if (backgroundColor) { 296 | // attrDict[NSBackgroundColorAttributeName] = backgroundColor.ios; 297 | // } 298 | // 299 | // let valueSource: typeof style; 300 | // if (textDecorationProperty.isSet(style)) { 301 | // valueSource = style; 302 | // } else if (textDecorationProperty.isSet(span.parent.style)) { 303 | // // span.parent is FormattedString 304 | // valueSource = span.parent.style; 305 | // } else if (textDecorationProperty.isSet(span.parent.parent.style)) { 306 | // // span.parent.parent is TextBase 307 | // valueSource = span.parent.parent.style; 308 | // } 309 | // 310 | // if (valueSource) { 311 | // const textDecorations = valueSource.textDecoration; 312 | // const underline = textDecorations.indexOf("underline") !== -1; 313 | // if (underline) { 314 | // attrDict[NSUnderlineStyleAttributeName] = underline; 315 | // } 316 | // 317 | // const strikethrough = textDecorations.indexOf("line-through") !== -1; 318 | // if (strikethrough) { 319 | // attrDict[NSStrikethroughStyleAttributeName] = strikethrough; 320 | // } 321 | // } 322 | // 323 | // return NSMutableAttributedString.alloc().initWithStringAttributes(text, attrDict); 324 | } 325 | } 326 | 327 | export function getTransformedText(text: string, textTransform: TextTransform): string { 328 | if (!text || !isString(text)) { 329 | return ""; 330 | } 331 | 332 | switch (textTransform) { 333 | case "uppercase": 334 | // return NSStringFromNSAttributedString(text).uppercaseString; 335 | case "lowercase": 336 | // return NSStringFromNSAttributedString(text).lowercaseString; 337 | case "capitalize": 338 | // return NSStringFromNSAttributedString(text).capitalizedString; 339 | default: 340 | return text; 341 | } 342 | } 343 | 344 | // function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString { 345 | // return NSString.stringWithString(source instanceof NSAttributedString && source.string || source); 346 | // } 347 | -------------------------------------------------------------------------------- /core/ui/text-field/text-field.desktop.ts: -------------------------------------------------------------------------------- 1 | import { TextFieldBase, secureProperty, whiteSpaceProperty, WhiteSpace, keyboardTypeProperty } from "@nativescript/core/ui/text-field/text-field-common"; 2 | 3 | export * from "@nativescript/core/ui/text-field/text-field-common"; 4 | 5 | export class TextField extends TextFieldBase { 6 | public _configureEditText(editText: any) { 7 | // editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 8 | editText.setLines(1); 9 | editText.setMaxLines(1); 10 | editText.setHorizontallyScrolling(true); 11 | } 12 | 13 | public _onReturnPress() { 14 | super.notify({ eventName: TextFieldBase.returnPressEvent, object: this }); 15 | } 16 | 17 | [secureProperty.setNative]() { 18 | this.setSecureAndKeyboardType(); 19 | } 20 | 21 | [keyboardTypeProperty.setNative]() { 22 | this.setSecureAndKeyboardType(); 23 | } 24 | 25 | setSecureAndKeyboardType(): void { 26 | 27 | let inputType: number; 28 | 29 | // Password variations are supported only for Text and Number classes. 30 | if (super.secure) { 31 | if (super.keyboardType === "number") { 32 | // inputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD; 33 | } else { 34 | // inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD; 35 | } 36 | } else { 37 | // default 38 | // inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL; 39 | 40 | // add autocorrect flags 41 | if (super.autocorrect) { 42 | // inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 43 | // inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; 44 | // inputType = inputType & ~android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; 45 | } 46 | 47 | // add autocapitalization type 48 | switch (super.autocapitalizationType) { 49 | case "words": 50 | // inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS; //8192 (0x00020000) 14th bit 51 | break; 52 | case "sentences": 53 | // inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; //16384(0x00040000) 15th bit 54 | break; 55 | case "allcharacters": 56 | // inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; //4096 (0x00010000) 13th bit 57 | break; 58 | default: 59 | break; 60 | } 61 | 62 | // add keyboardType flags. 63 | // They override previous if set. 64 | switch (super.keyboardType) { 65 | case "datetime": 66 | // inputType = android.text.InputType.TYPE_CLASS_DATETIME | android.text.InputType.TYPE_DATETIME_VARIATION_NORMAL; 67 | break; 68 | case "phone": 69 | // inputType = android.text.InputType.TYPE_CLASS_PHONE; 70 | break; 71 | case "number": 72 | // inputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_NORMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL; 73 | break; 74 | case "url": 75 | // inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_URI; 76 | break; 77 | case "email": 78 | // inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; 79 | break; 80 | default: 81 | break; 82 | } 83 | } 84 | 85 | super._setInputType(inputType); 86 | } 87 | 88 | [whiteSpaceProperty.getDefault](): WhiteSpace { 89 | return "nowrap"; 90 | } 91 | [whiteSpaceProperty.setNative](value: WhiteSpace) { 92 | // Don't change it otherwise TextField will go to multiline mode. 93 | } 94 | } 95 | 96 | (TextField.prototype)._isSingleLine = true; 97 | -------------------------------------------------------------------------------- /core/ui/text-view/text-view.desktop.ts: -------------------------------------------------------------------------------- 1 | import { TextView as TextViewDefinition } from "@nativescript/core/ui/text-view"; 2 | import { EditableTextBase, CSSType } from "@nativescript/core/ui/editable-text-base/editable-text-base"; 3 | 4 | export * from "@nativescript/core/ui/text-base/text-base"; 5 | 6 | @CSSType("TextView") 7 | export class TextView extends EditableTextBase implements TextViewDefinition { 8 | 9 | public _configureEditText(editText: any) { 10 | // editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE | android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 11 | // editText.setGravity(android.view.Gravity.TOP | android.view.Gravity.START); 12 | } 13 | 14 | public resetNativeView(): void { 15 | super.resetNativeView(); 16 | // this.nativeTextViewProtected.setGravity(android.view.Gravity.TOP | android.view.Gravity.START); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/utils/layout-helper/layout-helper.desktop.ts: -------------------------------------------------------------------------------- 1 | import {MODE_MASK, round} from "@nativescript/core/utils/layout-helper/layout-helper-common"; 2 | 3 | export * from "@nativescript/core/utils/layout-helper/layout-helper-common"; 4 | 5 | let mainScreenScale; 6 | 7 | export function makeMeasureSpec(size: number, mode: number): number { 8 | return (Math.round(Math.max(0, size)) & ~MODE_MASK) | (mode & MODE_MASK); 9 | } 10 | 11 | export function getDisplayDensity(): number { 12 | return mainScreenScale; 13 | } 14 | 15 | export function toDevicePixels(value: number): number { 16 | return value * mainScreenScale; 17 | } 18 | 19 | export function toDeviceIndependentPixels(value: number): number { 20 | return value / mainScreenScale; 21 | } 22 | 23 | export function measureNativeView(nativeView: any, width: number, widthMode: number, height: number, heightMode: number): { width: number, height: number } { 24 | const nativeSize = nativeView.sizeThatFits({ 25 | width: widthMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : toDeviceIndependentPixels(width), 26 | height: heightMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : toDeviceIndependentPixels(height) 27 | }); 28 | 29 | nativeSize.width = round(toDevicePixels(nativeSize.width)); 30 | nativeSize.height = round(toDevicePixels(nativeSize.height)); 31 | 32 | return nativeSize; 33 | } 34 | 35 | mainScreenScale = 1; 36 | -------------------------------------------------------------------------------- /core/utils/mainthread-helper.desktop.ts: -------------------------------------------------------------------------------- 1 | export function dispatchToMainThread(func: () => void) { 2 | //NSOperationQueue.mainQueue.addOperationWithBlock(func); 3 | } 4 | 5 | export function isMainThread(): Boolean { 6 | return true; //NSThread.isMainThread; 7 | } 8 | -------------------------------------------------------------------------------- /core/utils/utils.desktop.d.ts: -------------------------------------------------------------------------------- 1 | export module desktop { 2 | export const MajorVersion: number; 3 | } 4 | -------------------------------------------------------------------------------- /core/utils/utils.desktop.ts: -------------------------------------------------------------------------------- 1 | import { categories as traceCategories, messageType as traceMessageType, write as traceWrite } from "@nativescript/core/trace"; 2 | 3 | import { layoutCommon } from "@nativescript/core/utils/utils-common"; 4 | 5 | export * from "@nativescript/core/utils/utils-common"; 6 | 7 | let mainScreenScale; 8 | 9 | function isOrientationLandscape(orientation: number) { 10 | return false; // orientation === UIDeviceOrientation.LandscapeLeft /* 3 */ || 11 | // orientation === UIDeviceOrientation.LandscapeRight /* 4 */; 12 | } 13 | 14 | export function uniqId() { 15 | return `a${Math.random().toString(36).substr(2, 10)}`; 16 | } 17 | 18 | export module layout { 19 | const MODE_SHIFT = 30; 20 | const MODE_MASK = 0x3 << MODE_SHIFT; 21 | 22 | export function makeMeasureSpec(size: number, mode: number): number { 23 | return (Math.round(Math.max(0, size)) & ~MODE_MASK) | (mode & MODE_MASK); 24 | } 25 | 26 | export function getDisplayDensity(): number { 27 | return mainScreenScale; 28 | } 29 | 30 | export function toDevicePixels(value: number): number { 31 | return value * mainScreenScale; 32 | } 33 | 34 | export function toDeviceIndependentPixels(value: number): number { 35 | return value / mainScreenScale; 36 | } 37 | 38 | export function measureNativeView(nativeView: any /* UIView */, width: number, widthMode: number, height: number, heightMode: number): { width: number, height: number } { 39 | // const nativeSize = nativeView.sizeThatFits({ 40 | // width: widthMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : toDeviceIndependentPixels(width), 41 | // height: heightMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : toDeviceIndependentPixels(height) 42 | // }); 43 | // 44 | // nativeSize.width = layoutCommon.round(toDevicePixels(nativeSize.width)); 45 | // nativeSize.height = layoutCommon.round(toDevicePixels(nativeSize.height)); 46 | 47 | return { width: 0, height: 0 }; // nativeSize; 48 | } 49 | } 50 | 51 | // TODO(webpack-workflow): Export all methods from layoutCommon 52 | // Think of a cleaner way to do that 53 | Object.assign(layout, layoutCommon); 54 | 55 | export module desktop { 56 | // TODO: remove for NativeScript 7.0 57 | export function getter(_this: any, property: T | { (): T }): T { 58 | console.log("utils.ios.getter() is deprecated; use the respective native property instead"); 59 | if (typeof property === "function") { 60 | return (<{ (): T }>property).call(_this); 61 | } else { 62 | return property; 63 | } 64 | } 65 | 66 | export module collections { 67 | export function jsArrayToNSArray(str: string[]): any { 68 | // return NSArray.arrayWithArray(str); 69 | } 70 | 71 | export function nsArrayToJSArray(a: any): any { 72 | // const arr = []; 73 | // if (a !== undefined) { 74 | // let count = a.count; 75 | // for (let i = 0; i < count; i++) { 76 | // arr.push(a.objectAtIndex(i)); 77 | // } 78 | // } 79 | // 80 | // return arr; 81 | } 82 | } 83 | 84 | export function isLandscape(): boolean { 85 | console.log("utils.ios.isLandscape() is deprecated; use application.orientation instead"); 86 | 87 | const deviceOrientation = null; // UIDevice.currentDevice.orientation; 88 | const statusBarOrientation = null; // UIApplication.sharedApplication.statusBarOrientation; 89 | 90 | const isDeviceOrientationLandscape = isOrientationLandscape(deviceOrientation); 91 | const isStatusBarOrientationLandscape = isOrientationLandscape(statusBarOrientation); 92 | 93 | return isDeviceOrientationLandscape || isStatusBarOrientationLandscape; 94 | } 95 | 96 | export const MajorVersion = 10; // NSString.stringWithString(UIDevice.currentDevice.systemVersion).intValue; 97 | 98 | export function openFile(filePath: string): boolean { 99 | console.log("utils.ios.openFile() is deprecated; use utils.openFile() instead"); 100 | 101 | return openFileAtRootModule(filePath); 102 | } 103 | 104 | export function getCurrentAppPath(): string { 105 | const currentDir = __dirname; 106 | const tnsModulesIndex = currentDir.indexOf("/tns_modules"); 107 | 108 | // Module not hosted in ~/tns_modules when bundled. Use current dir. 109 | let appPath = currentDir; 110 | if (tnsModulesIndex !== -1) { 111 | // Strip part after tns_modules to obtain app root 112 | appPath = currentDir.substring(0, tnsModulesIndex); 113 | } 114 | 115 | return appPath; 116 | } 117 | 118 | export function joinPaths(...paths: string[]): string { 119 | if (!paths || paths.length === 0) { 120 | return ""; 121 | } 122 | 123 | return paths.join("/"); //NSString.stringWithString(NSString.pathWithComponents(paths)).stringByStandardizingPath; 124 | } 125 | 126 | // export function getVisibleViewController(rootViewController: UIViewController): UIViewController { 127 | // if (rootViewController.presentedViewController) { 128 | // return getVisibleViewController(rootViewController.presentedViewController); 129 | // } 130 | // 131 | // if (rootViewController.isKindOfClass(UINavigationController.class())) { 132 | // return getVisibleViewController((rootViewController).visibleViewController); 133 | // } 134 | // 135 | // if (rootViewController.isKindOfClass(UITabBarController.class())) { 136 | // return getVisibleViewController(rootViewController); 137 | // } 138 | // 139 | // return rootViewController; 140 | // 141 | // } 142 | 143 | } 144 | 145 | export function openFile(filePath: string): boolean { 146 | try { 147 | const appPath = desktop.getCurrentAppPath(); 148 | const path = filePath.replace("~", appPath); 149 | 150 | // const controller = UIDocumentInteractionController.interactionControllerWithURL(NSURL.fileURLWithPath(path)); 151 | // controller.delegate = new UIDocumentInteractionControllerDelegateImpl(); 152 | 153 | return false; // controller.presentPreviewAnimated(true); 154 | } 155 | catch (e) { 156 | traceWrite("Error in openFile", traceCategories.Error, traceMessageType.error); 157 | } 158 | 159 | return false; 160 | } 161 | 162 | // Need this so that we can use this function inside the ios module (avoid name clashing). 163 | const openFileAtRootModule = openFile; 164 | 165 | export function GC() { 166 | //__collect(); 167 | } 168 | 169 | export function openUrl(location: string): boolean { 170 | try { 171 | // const url = NSURL.URLWithString(location.trim()); 172 | // if (UIApplication.sharedApplication.canOpenURL(url)) { 173 | // return UIApplication.sharedApplication.openURL(url); 174 | // } 175 | } 176 | catch (e) { 177 | // We Don't do anything with an error. We just output it 178 | traceWrite("Error in OpenURL", traceCategories.Error, traceMessageType.error); 179 | } 180 | 181 | return false; 182 | } 183 | 184 | // class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate { 185 | // public static ObjCProtocols = [UIDocumentInteractionControllerDelegate]; 186 | // 187 | // public getViewController(): UIViewController { 188 | // const app = UIApplication.sharedApplication; 189 | // 190 | // return app.keyWindow.rootViewController; 191 | // } 192 | // 193 | // public documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) { 194 | // return this.getViewController(); 195 | // } 196 | // 197 | // public documentInteractionControllerViewForPreview(controller: UIDocumentInteractionController) { 198 | // return this.getViewController().view; 199 | // } 200 | // 201 | // public documentInteractionControllerRectForPreview(controller: UIDocumentInteractionController): CGRect { 202 | // return this.getViewController().view.frame; 203 | // } 204 | // } 205 | // 206 | mainScreenScale = 1; //UIScreen.mainScreen.scale; 207 | 208 | export class WeakRef { 209 | private _target: any; 210 | 211 | constructor(target) { 212 | if (target instanceof WeakRef) { 213 | this._target = target.get(); 214 | } else { 215 | this._target = target; 216 | } 217 | } 218 | 219 | get() { 220 | return this._target; 221 | } 222 | 223 | clear() { 224 | delete this._target; 225 | } 226 | } 227 | 228 | (global).WeakRef = WeakRef; 229 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | require("core/bundle-entry-points.desktop"); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-platform-desktop", 3 | "version": "0.0.1", 4 | "main": "index", 5 | "description": "", 6 | "contributors": [ 7 | "Bundyo " 8 | ], 9 | "license": "Apache-2.0", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/bundyo/nativescript-platform-desktop.git" 13 | }, 14 | "scripts": { 15 | "tsc": "tsc -w" 16 | }, 17 | "devDependencies": { 18 | "husky": "^4.2.1", 19 | "lint-staged": "^10.0.7", 20 | "@nativescript/core": "6.3.2", 21 | "@nodegui/nodegui": "0.13.0", 22 | "prettier": "~1.19.1", 23 | "@types/node": "10.17.13", 24 | "typescript": "3.7.5" 25 | }, 26 | "husky": { 27 | "hooks": { 28 | "pre-commit": "lint-staged" 29 | } 30 | }, 31 | "lint-staged": { 32 | "**/*.{js, css, ts, json, scss, html, xml, md}": [ 33 | "prettier --write", 34 | "git add" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmitOnError": false, 4 | "noEmitHelpers": true, 5 | "target": "es5", 6 | "module": "commonjs", 7 | "declaration": false, 8 | "noImplicitAny": false, 9 | "noImplicitUseStrict": true, 10 | "removeComments": true, 11 | "experimentalDecorators": true, 12 | "diagnostics": true, 13 | "sourceMap": true, 14 | "lib": ["es6", "dom"], 15 | "types": ["node", "@nativescript/core"], 16 | "baseUrl": ".", 17 | "paths": { 18 | "tns-core-modules/*": ["tns-core-modules/*"] 19 | } 20 | } 21 | } 22 | --------------------------------------------------------------------------------