diff --git a/README.md b/README.md index 4e1a9108..ce14550d 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ See the official [plugin documentation](https://www.appdevforall.org/codeonthego | [`rainbow-on-the-go/`](rainbow-on-the-go/) | Colors matching parentheses, brackets, and braces by nesting depth, with light/dark palettes. | | [`compose-preview/`](compose-preview/) | Renders Jetpack Compose `@Preview` functions on-device — no full app build or run. | | [`ai-literacy-course/`](ai-literacy-course/) | Bundles Learn AI Anywhere's offline "Introduction to AI" course (26 videos + interactive activities) and plays it full-screen, fully offline. | +| [`layout-editor/`](layout-editor/) | Visual drag-and-drop editor for Android XML layouts. | ## Building a plugin diff --git a/layout-editor/README.md b/layout-editor/README.md new file mode 100644 index 00000000..84c6f3dd --- /dev/null +++ b/layout-editor/README.md @@ -0,0 +1,108 @@ +# Layout Editor Plugin for Code on the Go + +A visual, drag-and-drop editor for Android XML layouts, running on-device. Open a +layout XML file, tap the Layout Editor action in the editor toolbar, and build the +view hierarchy on a canvas, set attributes through typed dialogs, and preview the +result at different device sizes. + +The editor was extracted from the host IDE into this standalone plugin so the host +APK stays smaller and its build stays shorter. The plugin carries its own resources +and libraries and links only against the stable `plugin-api` contract. + +## Features + +- **Design and Blueprint views** - render the layout as it looks at runtime, or as a wireframe of each view's bounds. +- **Palette** - drag widgets onto the canvas, grouped as Common, Text, Buttons, Widgets, Layouts, and Containers. +- **Component tree** - the view hierarchy as a tree; tap to select, long-press for information. +- **Attribute editing** - add and remove attributes on the selected view and edit values through typed dialogs (color, dimension, size, number, string, boolean, enum, flag, id, reference). +- **Resource Manager** - browse and edit the project's colors, strings, drawables, and fonts. +- **Device size preview** - check the layout at different device dimensions and orientations. +- **XML view** - read the generated XML for the current layout. +- **Long-press documentation** - a tooltip on every control, palette item, and attribute. + +## Plugin interfaces implemented + +- `IPlugin` - core plugin lifecycle. +- `UIExtension` - the toolbar action and its enable rule. +- `DocumentationExtension` - tooltips and documentation. +- `BuildStatusListener` - disables the action while a project has a failed sync. + +## Architecture + +``` +layout-editor/ +├── build.gradle.kts, settings.gradle.kts, proguard-rules.pro +├── layout-editor-documentation.html # full reference for this plugin +├── src/main/ +│ ├── AndroidManifest.xml # plugin id, main class, icons, permissions +│ ├── assets/ # icons, tooltips.json, widgetclasses.json, palette/, attributes/, editor/ +│ ├── kotlin|java/.../layouteditor/ +│ │ ├── LayoutEditorPlugin.kt # entry point: toolbar action, enable rule, tooltips, build-status +│ │ ├── LayoutEditorFragment.kt # full-screen editor: canvas, palette, component tree, drawer +│ │ ├── ResourceManagerFragment.kt # colors, strings, drawables, fonts +│ │ ├── editor/DesignEditor.kt # drag-and-drop canvas over the live view hierarchy +│ │ ├── editor/dialogs/ # one typed dialog per attribute format +│ │ ├── editor/callers|initializer # apply values to real View instances; track attributes +│ │ ├── LayoutEditorDocs.kt # long-press tooltip helper +│ │ └── PluginDialogContext.kt # dialog context with a valid window token +│ └── res/ # layout/, drawable/, values/, menu/ +└── README.md +``` + +The editor opens full-screen through `IdeUIService.openPluginScreen()`, which carries +no `Bundle`, so screens hand data through process-level state holders +(`LayoutEditorState`, `EditorSubScreenState`). Because a plugin has its own resource +namespace, plugin XML is inflated with the plugin's own inflater +(`PluginFragmentHelper.getPluginInflater`) so `?attr/` and `app:` attributes resolve +against the plugin theme, and dialogs are shown through `PluginDialogContext`, which +pairs the host activity (for the window token) with the plugin context (for resources +and theme). + +## Host services used + +- `IdeEditorService` - read the current file for the enable rule and the target layout. +- `IdeProjectService` - resolve the project and its resource directories. +- `IdeBuildService` - subscribe to build status so a failed sync disables the action. +- `IdeUIService` - `openPluginScreen()` to present the editor and its sub-screens. +- `IdeTooltipService` - show the long-press documentation tooltips. + +## Building + +```bash +cd layout-editor +./gradlew clean assemblePluginDebug # or assemblePlugin for release +``` + +Output: `build/plugin/layout-editor-debug.cgp`. Run `clean` first, since the plugin +builder copies the built APK into the `.cgp` and then deletes the source APK. + +## Installation + +1. Open Preferences, then Plugin Manager, then the add button. +2. Select the `layout-editor-debug.cgp` file. +3. The IDE discovers `LayoutEditorPlugin` from the manifest metadata and activates it. + +## Usage + +1. Open a layout XML file (under a `layout` resource directory). +2. Tap the Layout Editor action in the editor toolbar. +3. Drag widgets from the palette, select views on the canvas or in the component tree, and edit their attributes. +4. Open the Resource Manager to edit colors, strings, drawables, and fonts. +5. Save to write the layout back. Long-press any control for its tooltip. + +## Requirements + +- Android API 26 or newer. +- Minimum IDE version 1.0.0. +- Permissions: `filesystem.read`, `filesystem.write`, `project.structure`. +- No network access. + +## Documentation + +`layout-editor-documentation.html` is the full reference for this plugin. In the IDE, +long-press any control, palette item, or attribute for its tooltip. + +## License + +Layout Editor is an open-source example plugin for Code on the Go, licensed per the +surrounding `plugin-examples` repository. See `LICENSE` at the repo root. diff --git a/layout-editor/build.gradle.kts b/layout-editor/build.gradle.kts new file mode 100644 index 00000000..98afaba1 --- /dev/null +++ b/layout-editor/build.gradle.kts @@ -0,0 +1,101 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("com.itsaky.androidide.plugins.build") +} + +pluginBuilder { + pluginName = "layout-editor" +} + +android { + namespace = "org.appdevforall.codeonthego.layouteditor" + compileSdk = 34 + + defaultConfig { + applicationId = "org.appdevforall.codeonthego.layouteditor" + minSdk = 26 + targetSdk = 34 + versionCode = 1 + versionName = "1.0.0" + } + + buildTypes { + release { + isMinifyEnabled = false + isShrinkResources = false + signingConfig = signingConfigs.getByName("debug") + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + buildFeatures { + viewBinding = true + buildConfig = true + } + + packaging { + resources { + excludes += setOf( + "META-INF/DEPENDENCIES", + "META-INF/LICENSE", + "META-INF/LICENSE.txt", + "META-INF/NOTICE", + "META-INF/NOTICE.txt", + "META-INF/INDEX.LIST", + "META-INF/*.kotlin_module" + ) + } + } +} + +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } +} + +dependencies { + // Host-provided at runtime (parent classloader) — link only, never bundle. + compileOnly(files("../libs/plugin-api.jar")) + compileOnly(files("../libs/eventbus-events.jar")) + compileOnly(files("../libs/common.jar")) + compileOnly(files("../libs/idetooltips.jar")) + compileOnly("org.greenrobot:eventbus:3.3.1") + compileOnly("org.slf4j:slf4j-api:2.0.9") + + // Bundled into the .cgp (vectormaster is sourced directly; the rest are libraries). + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") + implementation("androidx.activity:activity-ktx:1.8.2") + implementation("androidx.appcompat:appcompat:1.7.1") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.preference:preference-ktx:1.2.1") + implementation("androidx.recyclerview:recyclerview:1.3.2") + implementation("androidx.viewpager2:viewpager2:1.1.0-beta02") + implementation("androidx.palette:palette-ktx:1.0.0") + implementation("androidx.fragment:fragment-ktx:1.8.8") + implementation("com.google.android.material:material:1.12.0") + implementation("com.google.code.gson:gson:2.10.1") + implementation("com.github.bumptech.glide:glide:4.16.0") + implementation("com.jsibbold:zoomage:1.3.1") + implementation("com.blankj:utilcodex:1.31.1") + implementation("com.github.skydoves:colorpickerview:2.3.0") + implementation(platform("io.github.Rosemoe.sora-editor:bom:0.23.6")) + implementation("io.github.Rosemoe.sora-editor:editor") + implementation("io.github.Rosemoe.sora-editor:language-textmate") + implementation("org.apache.commons:commons-text:1.11.0") + implementation("commons-io:commons-io:2.15.1") + implementation("org.jetbrains.kotlin:kotlin-stdlib:2.3.0") +} + +// application-as-library packaging trips AAR metadata checks; disabling is intentional +// (documented convention in plugin-examples/CLAUDE.md). +tasks.matching { + it.name.contains("checkDebugAarMetadata") || it.name.contains("checkReleaseAarMetadata") +}.configureEach { enabled = false } diff --git a/layout-editor/gradle.properties b/layout-editor/gradle.properties new file mode 100644 index 00000000..04894fe8 --- /dev/null +++ b/layout-editor/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +org.gradle.parallel=true +org.gradle.caching=true +android.useAndroidX=true +android.nonTransitiveRClass=true +kotlin.code.style=official diff --git a/layout-editor/gradle/wrapper/gradle-wrapper.jar b/layout-editor/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 00000000..8bdaf60c Binary files /dev/null and b/layout-editor/gradle/wrapper/gradle-wrapper.jar differ diff --git a/layout-editor/gradle/wrapper/gradle-wrapper.properties b/layout-editor/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..df97d72b --- /dev/null +++ b/layout-editor/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/layout-editor/gradlew b/layout-editor/gradlew new file mode 100755 index 00000000..ef07e016 --- /dev/null +++ b/layout-editor/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/layout-editor/gradlew.bat b/layout-editor/gradlew.bat new file mode 100644 index 00000000..db3a6ac2 --- /dev/null +++ b/layout-editor/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/layout-editor/layout-editor-documentation.html b/layout-editor/layout-editor-documentation.html new file mode 100644 index 00000000..345d1e40 --- /dev/null +++ b/layout-editor/layout-editor-documentation.html @@ -0,0 +1,418 @@ + + + + + +Layout Editor Plugin Documentation + + + + +
+

Layout Editor Plugin

+

A visual, drag-and-drop editor for Android XML layouts, running on-device

+
Version 1.0.0 · Author: App Dev for All · Package: org.appdevforall.codeonthego.layouteditor
+
+ + + + +
+

1. Executive Overview

+

+ Layout Editor is a Code on the Go plugin that builds Android XML layouts + visually. Open a layout XML file, tap the Layout Editor action + in the editor toolbar, and the plugin opens a full-screen editor where you drag + widgets onto a canvas, arrange the view hierarchy, set attributes through typed + dialogs, and preview the result at different device sizes. +

+

+ The editor was extracted from the host IDE into a standalone plugin so the host + APK stays smaller and its build stays shorter. The plugin carries its own copy + of every resource and library it needs, and it links only against the stable + plugin-api contract, never host-internal modules. +

+

+ The plugin implements four extension interfaces (IPlugin, + UIExtension, DocumentationExtension, + BuildStatusListener) and consumes five host services for the editor, + project model, build system, UI, and tooltips. +

+
+ + +
+

2. Core Functionality

+ +

The Layout Editor action

+

+ The plugin contributes one editor-toolbar action via + UIExtension.getToolbarActions(). It is content-aware: it is enabled + only when the open file is a layout XML file (an .xml file inside a + directory whose name starts with layout) and the last Gradle sync + did not fail. The action sits in the preview slot, the position the host's former + built-in Preview Layout action used. Tapping it opens the editor full-screen + through IdeUIService.openPluginScreen(). +

+ +

Editor surfaces

+ + + + + + + + + + + + + + +
SurfaceWhat it does
DesignRenders the layout the way it looks at runtime.
BlueprintShows a wireframe outline of each view's bounds, which makes spacing and nesting easier to read.
PaletteGroups the available widgets (Common, Text, Buttons, Widgets, Layouts, Containers). Drag an item onto the canvas to add it.
Component treeShows the view hierarchy as a tree. Tap a node to select it, long-press for information about it.
AttributesAdd or remove attributes on the selected view and edit their values through typed dialogs.
Resource ManagerBrowse and edit the project's colors, strings, drawables, and fonts.
Device sizePreview the layout at different device dimensions and orientations.
XML viewRead the generated XML for the current layout.
+ +

Editing attributes

+

+ Each attribute opens the dialog that fits its format: color, dimension, size, + number, string, boolean, enum, flag, id, and view reference. Required attributes + such as layout_width and layout_height cannot be + removed. Saving writes the edited hierarchy back to the layout XML file. +

+ +

Documentation on demand

+

+ Every control, palette item, and attribute carries a tooltip. Long-press it to + read a short description, with a button through to the IDE's fuller + documentation. The entries come from the plugin's + DocumentationExtension. +

+
+ + +
+

3. Technical Architecture

+ +

File layout

+ + +

Class overview

+ + + + + + + + + + + + +
ClassRoleKey interfaces
LayoutEditorPluginEntry point. Contributes the toolbar action and its enable rule, the long-press tooltips, and the build-status listener.IPlugin, UIExtension, DocumentationExtension, BuildStatusListener
LayoutEditorFragmentThe full-screen editor screen: canvas, palette, component tree, toolbar, and navigation drawer.extends Fragment
DesignEditorThe drag-and-drop canvas. Inflates the layout, adds and removes views, and routes selection to the attribute editors.None
ResourceManagerFragmentBrowses and edits the project's colors, strings, drawables, and fonts.extends Fragment
Attribute dialogs (editor/dialogs)Typed editors, one per attribute format (color, dimension, enum, flag, and so on).None
LayoutEditorDocs / PluginDialogContextServe long-press tooltips, and wrap the plugin context so dialogs have a valid window token.None
+ +

Edit pipeline

+
+ open layout/*.xml + tap the Layout Editor action + | + v + LayoutEditorState.set(target layout) (openPluginScreen carries no Bundle) + | + v + IdeUIService.openPluginScreen ──> LayoutEditorFragment + | + v + DesignEditor inflates the layout with the plugin inflater + | + v + drag from palette / select on canvas / edit in the component tree + | + v + typed attribute dialog ──> caller applies the value to the live View + | + v + save ──> layout XML written back to the file
+
+ Why the plugin inflater and dialog wrapper. A plugin has its own + resource namespace, so plugin-inflated XML is inflated with the plugin's own + inflater (PluginFragmentHelper.getPluginInflater) to make + ?attr/ and app: attributes resolve against the plugin + theme. Dialogs are shown through PluginDialogContext, which pairs the + host activity (for the window token) with the plugin context (for resources and + theme). +
+ +

Build configuration

+ + + + + + + + + +
SettingValue
Gradle plugincom.itsaky.androidide.plugins.build
Compile / Target SDK34
Min SDK26
Java / Kotlin target17
View bindingEnabled
Plugin APIcompileOnly via ../libs/plugin-api.jar
Output format.cgp package
+
+ + +
+

4. Integration Points

+

+ Layout Editor implements four extension interfaces and consumes five host + services, all through plugin-api. +

+ +

4.1 Plugin lifecycle (IPlugin)

+

+ initialize() stores the PluginContext; + activate() registers a build-status listener; + deactivate() and dispose() unregister it. The editor and + its sub-screens open full-screen through + IdeUIService.openPluginScreen(). +

+ +

4.2 Toolbar action and enablement (UIExtension)

+

+ getToolbarActions() contributes the Layout Editor action with an + isEnabledProvider that enables it only when a layout XML file is open + and the last sync succeeded. The action uses the preview-slot order so it takes + the same position the built-in Preview Layout action used. No + getHiddenToolbarActionIds() is needed, because the host's built-in + action was removed when the editor became a plugin. +

+ +

4.3 Host services consumed

+ + + + + + + + + + + +
ServiceUsed for
IdeEditorServiceRead the current file to decide whether the action is enabled and which layout to open.
IdeProjectServiceResolve the current project and its resource directories for the Resource Manager.
IdeBuildServiceSubscribe to build status so a failed sync disables the action.
IdeUIServiceopenPluginScreen(): present the editor and its sub-screens full-screen.
IdeTooltipServiceShow the long-press documentation tooltips.
+ +

4.4 Build-status tracking (BuildStatusListener)

+

+ onBuildStarted() and onBuildFinished() clear the + failed-sync flag; onBuildFailed() sets it. The action's enable rule + reads the flag, so the editor does not open against a project that failed to + sync. +

+ +

4.5 Documentation (DocumentationExtension)

+

+ getTooltipCategory() returns the plugin's tooltip category, and + getTooltipEntries() provides the toolbar-action tooltip, the + editor-control tooltips, and one entry per widget and attribute (loaded from + assets/tooltips.json). Tooltip buttons link into the IDE's + documentation through direct paths, so the plugin does not bundle its own + documentation pages. +

+ +

4.6 Permissions

+
<meta-data android:name="plugin.permissions"
+           android:value="filesystem.read,filesystem.write,project.structure" />
+

+ Read access backs layout and resource parsing; write access backs saving edited + layouts and resources; project-structure access backs resource-directory + resolution. +

+
+ + +
+

5. Deployment & Usage

+ +

Building

+
cd layout-editor
+./gradlew clean assemblePluginDebug   # or assemblePlugin for release
+

+ Produces layout-editor/build/plugin/layout-editor-debug.cgp, the + bundle you sideload into Code on the Go. +

+
+ Always clean first. The plugin builder copies the + built APK to the .cgp and then deletes the source APK, so an + incremental build can pick up an empty artifact. +
+ +

Installation

+
    +
  1. Open Preferences → Plugin Manager → +.
  2. +
  3. Select the layout-editor-debug.cgp file.
  4. +
  5. The IDE discovers LayoutEditorPlugin via manifest metadata and activates it.
  6. +
+ +

Using the plugin

+
    +
  1. Open a layout XML file (under a layout resource directory).
  2. +
  3. Tap the Layout Editor action in the editor toolbar.
  4. +
  5. Drag widgets from the palette, select views on the canvas or in the component tree, and edit their attributes.
  6. +
  7. Open the Resource Manager to edit colors, strings, drawables, and fonts.
  8. +
  9. Save to write the layout back. Long-press any control for its tooltip; this page is the full reference.
  10. +
+ +

Runtime requirements

+ + + + + + +
RequirementValue
Min Android versionAPI 26 (Android 8)
Min IDE version1.0.0
Permissionsfilesystem.read, filesystem.write, project.structure
Network accessNone
+
+ + +
+

6. Key Benefits

+ +
+ + +
+

7. Attribution & License

+

+ Layout Editor is an open-source example plugin for Code on the Go. Its source is + licensed per the surrounding plugin-examples repository (see + LICENSE at the repo root). +

+ +
+ + + + + diff --git a/layout-editor/proguard-rules.pro b/layout-editor/proguard-rules.pro new file mode 100644 index 00000000..83e37e37 --- /dev/null +++ b/layout-editor/proguard-rules.pro @@ -0,0 +1 @@ +# Layout Editor plugin — no minification (release uses isMinifyEnabled = false). diff --git a/layout-editor/settings.gradle.kts b/layout-editor/settings.gradle.kts new file mode 100644 index 00000000..8b717726 --- /dev/null +++ b/layout-editor/settings.gradle.kts @@ -0,0 +1,32 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +buildscript { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath(files("../libs/plugin-api.jar")) + classpath(files("../libs/gradle-plugin.jar")) + classpath("com.android.tools.build:gradle:8.8.2") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.0") + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { url = uri("https://jitpack.io") } + } +} + +rootProject.name = "layout-editor" diff --git a/layout-editor/src/main/AndroidManifest.xml b/layout-editor/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b036b882 --- /dev/null +++ b/layout-editor/src/main/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout-editor/src/main/assets/attributes/attributes.json b/layout-editor/src/main/assets/attributes/attributes.json new file mode 100644 index 00000000..f72c8db2 --- /dev/null +++ b/layout-editor/src/main/assets/attributes/attributes.json @@ -0,0 +1,738 @@ +{ + "android.view.View": [ + { + "name": "android:id", + "methodName": "setId", + "className": "ViewCaller", + "attributeName": "android:id", + "argumentType": "id" + }, + { + "name": "android:layout_width", + "methodName": "setLayoutWidth", + "className": "ViewCaller", + "attributeName": "android:layout_width", + "argumentType": "size", + "canDelete": "false" + }, + { + "name": "android:layout_height", + "methodName": "setLayoutHeight", + "className": "ViewCaller", + "attributeName": "android:layout_height", + "argumentType": "size", + "canDelete": "false" + }, + { + "name": "android:alpha", + "methodName": "setAlpha", + "className": "ViewCaller", + "attributeName": "android:alpha", + "argumentType": "float" + }, + { + "name": "android:background", + "methodName": "setBackground", + "className": "ViewCaller", + "attributeName": "android:background", + "argumentType": "color|drawable" + }, + { + "name": "android:foreground", + "methodName": "setForeground", + "className": "ViewCaller", + "attributeName": "android:foreground", + "argumentType": "color|drawable" + }, + { + "name": "android:elevation", + "methodName": "setElevation", + "className": "ViewCaller", + "attributeName": "android:elevation", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:padding", + "methodName": "setPadding", + "className": "ViewCaller", + "attributeName": "android:padding", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:paddingLeft", + "methodName": "setPaddingLeft", + "className": "ViewCaller", + "attributeName": "android:paddingLeft", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:paddingRight", + "methodName": "setPaddingRight", + "className": "ViewCaller", + "attributeName": "android:paddingRight", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:paddingTop", + "methodName": "setPaddingTop", + "className": "ViewCaller", + "attributeName": "android:paddingTop", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:paddingBottom", + "methodName": "setPaddingBottom", + "className": "ViewCaller", + "attributeName": "android:paddingBottom", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:rotation", + "methodName": "setRotation", + "className": "ViewCaller", + "attributeName": "android:rotation", + "argumentType": "float" + }, + { + "name": "android:rotationX", + "methodName": "setRotationX", + "className": "ViewCaller", + "attributeName": "android:rotationX", + "argumentType": "float" + }, + { + "name": "android:rotationY", + "methodName": "setRotationY", + "className": "ViewCaller", + "attributeName": "android:rotationY", + "argumentType": "float" + }, + { + "name": "android:translationX", + "methodName": "setTranslationX", + "className": "ViewCaller", + "attributeName": "android:translationX", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:translationY", + "methodName": "setTranslationY", + "className": "ViewCaller", + "attributeName": "android:translationY", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:translationZ", + "methodName": "setTranslationZ", + "className": "ViewCaller", + "attributeName": "android:translationZ", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:scaleX", + "methodName": "setScaleX", + "className": "ViewCaller", + "attributeName": "android:scaleX", + "argumentType": "float" + }, + { + "name": "android:scaleY", + "methodName": "setScaleY", + "className": "ViewCaller", + "attributeName": "android:scaleY", + "argumentType": "float" + }, + { + "name": "android:enabled", + "methodName": "setEnabled", + "className": "ViewCaller", + "attributeName": "android:enabled", + "argumentType": "boolean" + }, + { + "name": "android:visibility", + "methodName": "setVisibility", + "className": "ViewCaller", + "attributeName": "android:visibility", + "argumentType": "enum", + "arguments": [ + "visible", + "invisible", + "gone" + ], + "defaultValue": "-1" + } + ], + "android.widget.LinearLayout": [ + { + "name": "android:gravity", + "methodName": "setGravity", + "className": "LinearLayoutCaller", + "attributeName": "android:gravity", + "argumentType": "flag", + "arguments": [ + "left", + "right", + "top", + "bottom", + "center", + "center_horizontal", + "center_vertical", + "fill", + "fill_horizontal", + "fill_vertical", + "clip_horizontal", + "clip_vertical", + "start", + "end" + ], + "defaultValue": "-1" + }, + { + "name": "android:orientation", + "methodName": "setOrientation", + "className": "LinearLayoutCaller", + "attributeName": "android:orientation", + "argumentType": "enum", + "arguments": [ + "horizontal", + "vertical" + ], + "defaultValue": "-1" + }, + { + "name": "android:weightSum", + "methodName": "setWeightSum", + "className": "layouts.LinearLayoutCaller", + "attributeName": "android:weightSum", + "argumentType": "float" + } + ], + "androidx.cardview.widget.CardView": [ + { + "name": "app:cardElevation", + "methodName": "setCardElevation", + "className": "CardViewCaller", + "attributeName": "app:cardElevation", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:cardCornerRadius", + "methodName": "setCardCornerRadius", + "className": "CardViewCaller", + "attributeName": "app:cardCornerRadius", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:cardBackgroundColor", + "methodName": "setCardBackgroundColor", + "className": "CardViewCaller", + "attributeName": "app:cardBackgroundColor", + "argumentType": "color" + }, + { + "name": "app:cardUseCompatPadding", + "methodName": "setCardUseCompatPadding", + "className": "CardViewCaller", + "attributeName": "app:cardUseCompatPadding", + "argumentType": "boolean", + "defaultValue": "false" + } + ], + "android.widget.TextView": [ + { + "name": "android:text", + "methodName": "setText", + "className": "text.TextViewCaller", + "attributeName": "android:text", + "argumentType": "text|string" + }, + { + "name": "android:textSize", + "methodName": "setTextSize", + "className": "text.TextViewCaller", + "attributeName": "android:textSize", + "argumentType": "dimension", + "dimensionUnit": "sp" + }, + { + "name": "android:textColor", + "methodName": "setTextColor", + "className": "text.TextViewCaller", + "attributeName": "android:textColor", + "argumentType": "color" + }, + { + "name": "android:textStyle", + "methodName": "setTextStyle", + "className": "text.TextViewCaller", + "attributeName": "android:textStyle", + "argumentType": "enum", + "arguments": [ + "normal", + "bold", + "italic", + "bold|italic" + ], + "defaultValue": "-1" + }, + { + "name": "android:gravity", + "methodName": "setGravity", + "className": "text.TextViewCaller", + "attributeName": "android:gravity", + "argumentType": "flag", + "arguments": [ + "left", + "right", + "top", + "bottom", + "center", + "center_horizontal", + "center_vertical", + "fill", + "fill_horizontal", + "fill_vertical", + "clip_horizontal", + "clip_vertical", + "start", + "end" + ], + "defaultValue": "-1" + } + ], + "android.widget.CheckedTextView": [ + { + "name": "android:checkMark", + "methodName": "setCheckMark", + "className": "text.TextViewCaller", + "attributeName": "android:checkMark", + "argumentType": "drawable" + }, + { + "name": "android:checked", + "methodName": "setChecked", + "className": "text.TextViewCaller", + "attributeName": "android:checked", + "argumentType": "boolean" + } + ], + "android.widget.AutoCompleteTextView": [ + { + "name": "android:completionHint", + "methodName": "setCompletionHint", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:completionHint", + "argumentType": "string|text" + }, + { + "name": "android:dropDownHeight", + "methodName": "setDropDownHeight", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:dropDownHeight", + "argumentType": "size" + }, + { + "name": "android:dropDownHorizontalOffset", + "methodName": "setDropDownHorizontalOffset", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:dropDownHorizontalOffset", + "argumentType": "float" + }, + { + "name": "android:dropDownVerticalOffset", + "methodName": "setDropDownVerticalOffset", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:dropDownVerticalOffset", + "argumentType": "float" + }, + { + "name": "android:dropDownWidth", + "methodName": "setDropDownWidth", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:dropDownWidth", + "argumentType": "size" + }, + { + "name": "android:popupBackground", + "methodName": "setDropDownBackgroundResource", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:popupBackground", + "argumentType": "color" + }, + { + "name": "android:completionThreshold", + "methodName": "setThreshold", + "className": "text.AutoCompleteTextViewCaller", + "attributeName": "android:completionThreshold", + "argumentType": "int" + } + ], + "android.widget.EditText": [ + { + "name": "android:hint", + "methodName": "setHint", + "className": "text.EditTextCaller", + "attributeName": "android:hint", + "argumentType": "text|string" + }, + { + "name": "android:inputType", + "methodName": "setInputType", + "className": "text.EditTextCaller", + "attributeName": "android:inputType", + "argumentType": "flag", + "arguments": [ + "date", + "datetime", + "none", + "number", + "numberDecimal", + "numberSigned", + "numberPassword", + "phone", + "text", + "textAutoComplete", + "textAutoCorrect", + "textCapCharacters", + "textCapSentences", + "textCapWords", + "textEmailAddress", + "textEmailSubject", + "textEnableTextConversionSuggestions", + "textFilter", + "textImeMultiLine", + "textLongMessage", + "textMultiLine", + "textNoSuggestions", + "textPassword", + "textPersonName", + "textPhonetic", + "textPostalAddress", + "textShortMessage", + "textUri", + "textVisiblePassword", + "textWebEditText", + "textWebEmailAddress", + "textWebPassword", + "time" + ], + "defaultValue": "-1" + }, + { + "name": "android:textColorHint", + "methodName": "setHintTextColor", + "className": "text.EditTextCaller", + "attributeName": "android:textColorHint", + "argumentType": "color" + }, + { + "name": "android:singleLine", + "methodName": "setSingleLine", + "className": "text.EditTextCaller", + "attributeName": "android:singleLine", + "argumentType": "boolean" + } + ], + "android.widget.ImageView": [ + { + "name": "android:src", + "methodName": "setImage", + "className": "ImageViewCaller", + "attributeName": "android:src", + "argumentType": "drawable" + }, + { + "name": "android:scaleType", + "methodName": "setScaleType", + "className": "ImageViewCaller", + "attributeName": "android:scaleType", + "argumentType": "enum", + "arguments": [ + "fitXY", + "fitStart", + "fitCenter", + "fitEnd", + "center", + "centerCrop", + "centerInside" + ], + "defaultValue": "-1" + }, + { + "name": "android:tint", + "methodName": "setTint", + "className": "ImageViewCaller", + "attributeName": "android:tint", + "argumentType": "color" + } + ], + "androidx.appcompat.widget.AppCompatImageView": [ + { + "name": "android:src", + "methodName": "setImage", + "className": "ImageViewCaller", + "attributeName": "android:src", + "argumentType": "drawable" + }, + { + "name": "app:srcCompat", + "methodName": "setImage", + "className": "ImageViewCaller", + "attributeName": "app:srcCompat", + "argumentType": "drawable" + }, + { + "name": "android:scaleType", + "methodName": "setScaleType", + "className": "ImageViewCaller", + "attributeName": "android:scaleType", + "argumentType": "enum", + "arguments": [ + "fitXY", + "fitStart", + "fitCenter", + "fitEnd", + "center", + "centerCrop", + "centerInside" + ], + "defaultValue": "-1" + }, + { + "name": "android:tint", + "methodName": "setTint", + "className": "ImageViewCaller", + "attributeName": "android:tint", + "argumentType": "color" + } + ], + "com.google.android.material.floatingactionbutton.FloatingActionButton": [ + { + "name": "app:background", + "methodName": "setBackgroundColor", + "className": "FABCaller", + "attributeName": "app:background", + "argumentType": "color" + }, + { + "name": "app:fabSize", + "methodName": "setSize", + "className": "FABCaller", + "attributeName": "app:fabSize", + "argumentType": "enum", + "arguments": [ + "auto", + "mini", + "normal" + ], + "defaultValue": "-1" + }, + { + "name": "app:fabCustomSize", + "methodName": "setCustomSize", + "className": "FABCaller", + "attributeName": "app:fabCustomSize", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:elevation", + "methodName": "setCompatElevation", + "className": "FABCaller", + "attributeName": "app:elevation", + "argumentType": "dimension", + "dimensionUnit": "dp" + } + ], + "android.widget.Switch": [ + { + "name": "android:checked", + "methodName": "setChecked", + "className": "SwitchCaller", + "attributeName": "android:checked", + "argumentType": "boolean" + } + ], + "androidx.appcompat.widget.SwitchCompat": [ + { + "name": "android:checked", + "methodName": "setChecked", + "className": "SwitchCaller", + "attributeName": "android:checked", + "argumentType": "boolean" + } + ], + "android.widget.ProgressBar": [ + { + "name": "android:progress", + "methodName": "setProgress", + "className": "ProgressBarCaller", + "attributeName": "android:progress", + "argumentType": "int" + }, + { + "name": "android:max", + "methodName": "setMax", + "className": "ProgressBarCaller", + "attributeName": "android:max", + "argumentType": "int" + }, + { + "name": "android:indeterminate", + "methodName": "setIndeterminate", + "className": "ProgressBarCaller", + "attributeName": "android:indeterminate", + "argumentType": "boolean" + } + ], + "android.widget.ScrollView": [ + { + "name": "android:fillViewport", + "methodName": "setFillViewport", + "className": "containers.ScrollViewCaller", + "attributeName": "android:fillViewport", + "argumentType": "boolean" + } + ], + "android.widget.FrameLayout": [ + { + "name": "android:foregroundGravity", + "methodName": "setForegroundGravity", + "className": "layouts.FrameLayoutCaller", + "attributeName": "android:foregroundGravity", + "argumentType": "flag", + "arguments": [ + "left", + "right", + "top", + "bottom", + "center", + "center_horizontal", + "center_vertical", + "fill", + "fill_horizontal", + "fill_vertical", + "clip_horizontal", + "clip_vertical", + "start", + "end" + ], + "defaultValue": "-1" + }, + { + "name": "android:measureAllChildren", + "methodName": "setMeasureAllChildren", + "className": "layouts.FrameLayoutCaller", + "attributeName": "android:measureAllChildren", + "argumentType": "boolean" + } + ], + "com.google.android.material.textfield.TextInputEditText": [ + { + "name": "android:hint", + "methodName": "setHint", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:hint", + "argumentType": "text|string" + }, + { + "name": "android:inputType", + "methodName": "setInputType", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:inputType", + "argumentType": "flag", + "arguments": [ + "date", + "datetime", + "none", + "number", + "numberDecimal", + "numberSigned", + "numberPassword", + "phone", + "text", + "textAutoComplete", + "textAutoCorrect", + "textCapCharacters", + "textCapSentences", + "textCapWords", + "textEmailAddress", + "textEmailSubject", + "textEnableTextConversionSuggestions", + "textFilter", + "textImeMultiLine", + "textLongMessage", + "textMultiLine", + "textNoSuggestions", + "textPassword", + "textPersonName", + "textPhonetic", + "textPostalAddress", + "textShortMessage", + "textUri", + "textVisiblePassword", + "textWebEditText", + "textWebEmailAddress", + "textWebPassword", + "time" + ], + "defaultValue": "-1" + }, + { + "name": "android:textColorHint", + "methodName": "setHintTextColor", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:textColorHint", + "argumentType": "color" + }, + { + "name": "android:singleLine", + "methodName": "setSingleLine", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:singleLine", + "argumentType": "boolean" + } + ], + "com.google.android.material.textfield.TextInputLayout": [ + { + "name": "android:hint", + "methodName": "setHint", + "className": "text.TextInputLayoutCaller", + "attributeName": "android:hint", + "argumentType": "text|string" + }, + { + "name": "app:hintEnabled", + "methodName": "setHintEnabled", + "className": "text.TextInputLayoutCaller", + "attributeName": "app:hintEnabled", + "argumentType": "boolean" + }, + { + "name": "app:errorEnabled", + "methodName": "setErrorEnabled", + "className": "text.TextInputLayoutCaller", + "attributeName": "app:errorEnabled", + "argumentType": "boolean" + }, + { + "name": "app:counterEnabled", + "methodName": "setCounterEnabled", + "className": "text.TextInputLayoutCaller", + "attributeName": "app:counterEnabled", + "argumentType": "boolean" + } + ] +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/attributes/parent_attributes.json b/layout-editor/src/main/assets/attributes/parent_attributes.json new file mode 100644 index 00000000..5a7deb67 --- /dev/null +++ b/layout-editor/src/main/assets/attributes/parent_attributes.json @@ -0,0 +1,532 @@ +{ + "android.widget.LinearLayout": [ + { + "name": "android:layout_weight", + "methodName": "setLayoutWeight", + "className": "parentcallers.LinearLayoutCaller", + "attributeName": "android:layout_weight", + "argumentType": "float" + }, + { + "name": "android:layout_margin", + "methodName": "setLayoutMargin", + "className": "parentcallers.LinearLayoutCaller", + "attributeName": "android:layout_margin", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginLeft", + "methodName": "setLayoutMarginLeft", + "className": "parentcallers.LinearLayoutCaller", + "attributeName": "android:layout_marginLeft", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginRight", + "methodName": "setLayoutMarginRight", + "className": "parentcallers.LinearLayoutCaller", + "attributeName": "android:layout_marginRight", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginTop", + "methodName": "setLayoutMarginTop", + "className": "parentcallers.LinearLayoutCaller", + "attributeName": "android:layout_marginTop", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginBottom", + "methodName": "setLayoutMarginBottom", + "className": "parentcallers.LinearLayoutCaller", + "attributeName": "android:layout_marginBottom", + "argumentType": "dimension", + "dimensionUnit": "dp" + } + ], + "android.widget.FrameLayout": [ + { + "name": "android:layout_gravity", + "methodName": "setLayoutGravity", + "className": "parentcallers.FrameLayoutCaller", + "attributeName": "android:layout_gravity", + "argumentType": "flag", + "arguments": [ + "left", + "right", + "top", + "bottom", + "center", + "center_horizontal", + "center_vertical", + "fill", + "fill_horizontal", + "fill_vertical", + "clip_horizontal", + "clip_vertical", + "start", + "end" + ], + "defaultValue": "-1" + }, + { + "name": "android:layout_margin", + "methodName": "setLayoutMargin", + "className": "parentcallers.FrameLayoutCaller", + "attributeName": "android:layout_margin", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginLeft", + "methodName": "setLayoutMarginLeft", + "className": "parentcallers.FrameLayoutCaller", + "attributeName": "android:layout_marginLeft", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginRight", + "methodName": "setLayoutMarginRight", + "className": "parentcallers.FrameLayoutCaller", + "attributeName": "android:layout_marginRight", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginTop", + "methodName": "setLayoutMarginTop", + "className": "parentcallers.FrameLayoutCaller", + "attributeName": "android:layout_marginTop", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginBottom", + "methodName": "setLayoutMarginBottom", + "className": "parentcallers.FrameLayoutCaller", + "attributeName": "android:layout_marginBottom", + "argumentType": "dimension", + "dimensionUnit": "dp" + } + ], + "android.widget.RelativeLayout": [ + { + "name": "android:layout_centerHorizontal", + "methodName": "setLayoutCenterHorizontal", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_centerHorizontal", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_centerVertical", + "methodName": "setLayoutCenterVertical", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_centerVertical", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_centerInParent", + "methodName": "setLayoutCenterInParent", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_centerInParent", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_below", + "methodName": "setLayoutBelow", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_below", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_above", + "methodName": "setLayoutAbove", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_above", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_toLeftOf", + "methodName": "setLayoutToLeftOf", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_toLeftOf", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_toRightOf", + "methodName": "setLayoutToRightOf", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_toRightOf", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_alignLeft", + "methodName": "setLayoutAlignLeft", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignLeft", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_alignRight", + "methodName": "setLayoutAlignRight", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignRight", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_alignTop", + "methodName": "setLayoutAlignTop", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignTop", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_alignBottom", + "methodName": "setLayoutAlignBottom", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignBottom", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_alignParentLeft", + "methodName": "setLayoutAlignParentLeft", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignParentLeft", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_alignParentRight", + "methodName": "setLayoutAlignParentRight", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignParentRight", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_alignParentTop", + "methodName": "setLayoutAlignParentTop", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignParentTop", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_alignParentBottom", + "methodName": "setLayoutAlignParentBottom", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignParentBottom", + "argumentType": "boolean", + "defaultValue": "false" + }, + { + "name": "android:layout_alignBaseline", + "methodName": "setLayoutAlignBaseline", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_alignBaseline", + "argumentType": "view", + "defaultValue": "-1" + }, + { + "name": "android:layout_margin", + "methodName": "setLayoutMargin", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_margin", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginLeft", + "methodName": "setLayoutMarginLeft", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_marginLeft", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginRight", + "methodName": "setLayoutMarginRight", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_marginRight", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginTop", + "methodName": "setLayoutMarginTop", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_marginTop", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginBottom", + "methodName": "setLayoutMarginBottom", + "className": "parentcallers.RelativeLayoutCaller", + "attributeName": "android:layout_marginBottom", + "argumentType": "dimension", + "dimensionUnit": "dp" + } + ], + "androidx.constraintlayout.widget.ConstraintLayout": [ + { + "name": "app:layout_constraintLeft_toLeftOf", + "methodName": "setLeftToLeft", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintLeft_toLeftOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintLeft_toRightOf", + "methodName": "setLeftToRight", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintLeft_toRightOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintRight_toLeftOf", + "methodName": "setRightToLeft", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintRight_toLeftOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintRight_toRightOf", + "methodName": "setRightToRight", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintRight_toRightOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintTop_toTopOf", + "methodName": "setTopToTop", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintTop_toTopOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintTop_toBottomOf", + "methodName": "setTopToBottom", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintTop_toBottomOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintBottom_toTopOf", + "methodName": "setBottomToTop", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintBottom_toTopOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintBottom_toBottomOf", + "methodName": "setBottomToBottom", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintBottom_toBottomOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintBaseline_toBaselineOf", + "methodName": "setBaselineToBaseline", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintBaseline_toBaselineOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintStart_toStartOf", + "methodName": "setStartToStart", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintStart_toStartOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintStart_toEndOf", + "methodName": "setStartToEnd", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintStart_toEndOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintEnd_toStartOf", + "methodName": "setEndToStart", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintEnd_toStartOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "app:layout_constraintEnd_toEndOf", + "methodName": "setEndToEnd", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintEnd_toEndOf", + "argumentType": "view", + "constant": "parent", + "defaultValue": "-1" + }, + { + "name": "android:layout_marginLeft", + "methodName": "setLayoutMarginLeft", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "android:layout_marginLeft", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginRight", + "methodName": "setLayoutMarginRight", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "android:layout_marginRight", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginTop", + "methodName": "setLayoutMarginTop", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "android:layout_marginTop", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginBottom", + "methodName": "setLayoutMarginBottom", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "android:layout_marginBottom", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginStart", + "methodName": "setLayoutMarginStart", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "android:layout_marginStart", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "android:layout_marginEnd", + "methodName": "setLayoutMarginEnd", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "android:layout_marginEnd", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_marginBaseline", + "methodName": "setLayoutMarginBaseline", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_marginBaseline", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginLeft", + "methodName": "setLayoutGoneMarginLeft", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginLeft", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginRight", + "methodName": "setLayoutGoneMarginRight", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginRight", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginTop", + "methodName": "setLayoutGoneMarginTop", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginTop", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginBottom", + "methodName": "setLayoutGoneMarginBottom", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginBottom", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginStart", + "methodName": "setLayoutGoneMarginStart", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginStart", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginEnd", + "methodName": "setLayoutGoneMarginEnd", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginEnd", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_goneMarginBaseline", + "methodName": "setLayoutGoneMarginBaseline", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_goneMarginBaseline", + "argumentType": "dimension", + "dimensionUnit": "dp" + }, + { + "name": "app:layout_constraintHorizontal_bias", + "methodName": "setHorizontalBias", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintHorizontal_bias", + "argumentType": "float" + }, + { + "name": "app:layout_constraintVertical_bias", + "methodName": "setVerticalBias", + "className": "layouts.ConstraintLayoutCaller", + "attributeName": "app:layout_constraintVertical_bias", + "argumentType": "float" + } + ] +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/colors.xml b/layout-editor/src/main/assets/colors.xml new file mode 100644 index 00000000..fc802bd7 --- /dev/null +++ b/layout-editor/src/main/assets/colors.xml @@ -0,0 +1,3 @@ + + #4CAF50 + \ No newline at end of file diff --git a/layout-editor/src/main/assets/default_font.ttf b/layout-editor/src/main/assets/default_font.ttf new file mode 100644 index 00000000..973e658e Binary files /dev/null and b/layout-editor/src/main/assets/default_font.ttf differ diff --git a/layout-editor/src/main/assets/default_image.png b/layout-editor/src/main/assets/default_image.png new file mode 100644 index 00000000..aa0f4681 Binary files /dev/null and b/layout-editor/src/main/assets/default_image.png differ diff --git a/layout-editor/src/main/assets/editor/textmate/abyss.json b/layout-editor/src/main/assets/editor/textmate/abyss.json new file mode 100644 index 00000000..24ae2408 --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/abyss.json @@ -0,0 +1,223 @@ +{ + "name": "Abyss", + "settings": [{ + "settings": { + "background": "#000c18", + "caret": "#ddbb88", + "foreground": "#6688cc", + "invisibles": "#002040", + "lineHighlight": "#082050", + "selection": "#770811", + "guide": "#002952" + } + }, { + "scope": ["meta.embedded", "source.groovy.embedded"], + "settings": { + "foreground": "#6688cc" + } + }, { + "name": "Comment", + "scope": "comment", + "settings": { + "foreground": "#384887" + } + }, { + "name": "String", + "scope": "string", + "settings": { + "foreground": "#22aa44" + } + }, { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#f280d0" + } + }, { + "name": "Built-in constant", + "scope": "constant.language", + "settings": { + "foreground": "#f280d0" + } + }, { + "name": "User-defined constant", + "scope": ["constant.character", "constant.other"], + "settings": { + "foreground": "#f280d0" + } + }, { + "name": "Variable", + "scope": "variable", + "settings": { + "fontStyle": "" + } + }, { + "name": "Keyword", + "scope": "keyword", + "settings": { + "foreground": "#225588" + } + }, { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "", + "foreground": "#225588" + } + }, { + "name": "Storage type", + "scope": "storage.type", + "settings": { + "fontStyle": "italic", + "foreground": "#9966b8" + } + }, { + "name": "Class name", + "scope": ["entity.name.class", "entity.name.type", "entity.name.namespace", "entity.name.scope-resolution"], + "settings": { + "fontStyle": "underline", + "foreground": "#ffeebb" + } + }, { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": { + "fontStyle": "italic underline", + "foreground": "#ddbb88" + } + }, { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "fontStyle": "", + "foreground": "#ddbb88" + } + }, { + "name": "Function argument", + "scope": "variable.parameter", + "settings": { + "fontStyle": "italic", + "foreground": "#2277ff" + } + }, { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "fontStyle": "", + "foreground": "#225588" + } + }, { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "fontStyle": "", + "foreground": "#ddbb88" + } + }, { + "name": "Library function", + "scope": "support.function", + "settings": { + "fontStyle": "", + "foreground": "#9966b8" + } + }, { + "name": "Library constant", + "scope": "support.constant", + "settings": { + "fontStyle": "", + "foreground": "#9966b8" + } + }, { + "name": "Library class/type", + "scope": ["support.type", "support.class"], + "settings": { + "fontStyle": "italic", + "foreground": "#9966b8" + } + }, { + "name": "Library variable", + "scope": "support.other.variable", + "settings": { + "fontStyle": "" + } + }, { + "name": "Invalid", + "scope": "invalid", + "settings": { + "fontStyle": "", + "foreground": "#A22D44" + } + }, { + "name": "Invalid deprecated", + "scope": "invalid.deprecated", + "settings": { + "foreground": "#A22D44" + } + }, { + "name": "diff: header", + "scope": ["meta.diff", "meta.diff.header"], + "settings": { + "fontStyle": "italic", + "foreground": "#E0EDDD" + } + }, { + "name": "diff: deleted", + "scope": "markup.deleted", + "settings": { + "fontStyle": "", + "foreground": "#dc322f" + } + }, { + "name": "diff: changed", + "scope": "markup.changed", + "settings": { + "fontStyle": "", + "foreground": "#cb4b16" + } + }, { + "name": "diff: inserted", + "scope": "markup.inserted", + "settings": { + "foreground": "#219186" + } + }, { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#22aa44" + } + }, { + "name": "Markup Styling", + "scope": ["markup.bold", "markup.italic"], + "settings": { + "foreground": "#22aa44" + } + }, { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#9966b8" + } + }, { + "name": "Markup Headings", + "scope": ["markup.heading", "markup.heading.setext"], + "settings": { + "fontStyle": "bold", + "foreground": "#6688cc" + } + }] + +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/editor/textmate/darcula.json b/layout-editor/src/main/assets/editor/textmate/darcula.json new file mode 100644 index 00000000..7c07f8d1 --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/darcula.json @@ -0,0 +1,465 @@ +{ + "name": "darcula", + "settings": [{ + "settings": { + "background": "#242424", + "foreground": "#cccccc", + "lineHighlight": "#2B2B2B", + "selection": "#214283", + "highlightedDelimetersForeground": "#57f6c0" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "foreground": "#707070" + } + }, + { + "name": "Operator Keywords", + "scope": "keyword.operator,keyword.operator.logical,keyword.operator.relational,keyword.operator.assignment,keyword.operator.comparison,keyword.operator.ternary,keyword.operator.arithmetic,keyword.operator.spread", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Strings", + "scope": "string,string.character.escape,string.template.quoted,string.template.quoted.punctuation,string.template.quoted.punctuation.single,string.template.quoted.punctuation.double,string.type.declaration.annotation,string.template.quoted.punctuation.tag", + "settings": { + "foreground": "#6A8759" + } + }, + { + "name": "String Interpolation Begin and End", + "scope": "punctuation.definition.template-expression.begin,punctuation.definition.template-expression.end", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "String Interpolation Body", + "scope": "expression.string,meta.template.expression", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "Built-in constant", + "scope": "constant.language,variable.language", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "User-defined constant", + "scope": "constant.character, constant.other", + "settings": { + "foreground": "#9E7BB0" + } + }, + { + "name": "Keyword", + "scope": "keyword,keyword.operator.new,keyword.operator.delete,keyword.operator.static,keyword.operator.this,keyword.operator.expression", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Types, Class Types", + "scope": "entity.name.type,meta.return.type,meta.type.annotation,meta.type.parameters,support.type.primitive", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "Storage type", + "scope": "storage,storage.type,storage.modifier,storage.arrow", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Class constructor", + "scope": "class.instance.constructor,new.expr entity.name.type", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "Function", + "scope": "support.function, entity.name.function", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "Function Types", + "scope": "annotation.meta.ts, annotation.meta.tsx", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Function Argument", + "scope": "variable.parameter, operator.rest.parameters", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Variable, Property", + "scope": "variable.property,variable.other.property,variable.other.object.property,variable.object.property,support.variable.property", + "settings": { + "foreground": "#9E7BB0" + } + }, + { + "name": "Module Name", + "scope": "quote.module", + "settings": { + "foreground": "#6A8759" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Tag name", + "scope": "punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end, entity.name.tag", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Object Keys", + "scope": "meta.object-literal.key", + "settings": { + "foreground": "#9E7BB0" + } + }, + { + "name": "TypeScript Class Modifiers", + "scope": "storage.modifier.ts", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "TypeScript Type Casting", + "scope": "ts.cast.expr,ts.meta.entity.class.method.new.expr.cast,ts.meta.entity.type.name.new.expr.cast,ts.meta.entity.type.name.var-single-variable.annotation,tsx.cast.expr,tsx.meta.entity.class.method.new.expr.cast,tsx.meta.entity.type.name.new.expr.cast,tsx.meta.entity.type.name.var-single-variable.annotation", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "TypeScript Type Declaration", + "scope": "ts.meta.type.support,ts.meta.type.entity.name,ts.meta.class.inherited-class,tsx.meta.type.support,tsx.meta.type.entity.name,tsx.meta.class.inherited-class,type-declaration,enum-declaration", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "TypeScript Method Declaration", + "scope": "function-declaration,method-declaration,method-overload-declaration,type-fn-type-parameters", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "Documentation Block", + "scope": "comment.block.documentation", + "settings": { + "foreground": "#6A8759" + } + }, + { + "name": "Documentation Highlight (JSDoc)", + "scope": "storage.type.class.jsdoc", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Import-Export-All (*) Keyword", + "scope": "constant.language.import-export-all", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Object Key Seperator", + "scope": "objectliteral.key.separator, punctuation.separator.key-value", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Regex", + "scope": "regex", + "settings": { + "fontStyle": " italic" + } + }, + { + "name": "Typescript Namespace", + "scope": "ts.meta.entity.name.namespace,tsx.meta.entity.name.namespace", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Regex Character-class", + "scope": "regex.character-class", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Class Name", + "scope": "entity.name.type.class", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Class Inheritances", + "scope": "entity.other.inherited-class", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "Documentation Entity", + "scope": "entity.name.type.instance.jsdoc", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "YAML entity", + "scope": "yaml.entity.name,yaml.string.entity.name", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "YAML string value", + "scope": "yaml.string.out", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Ignored (Exceptions Rules)", + "scope": "meta.brace.square.ts,block.support.module,block.support.type.module,block.support.function.variable,punctuation.definition.typeparameters.begin,punctuation.definition.typeparameters.end", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Regex", + "scope": "string.regexp", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Regex Group/Set", + "scope": "punctuation.definition.group.regexp,punctuation.definition.character-class.regexp", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "Regex Character Class", + "scope": "constant.other.character-class.regexp, constant.character.escape.ts", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Regex Or Operator", + "scope": "expr.regex.or.operator", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Tag string", + "scope": "string.template.tag,string.template.punctuation.tag,string.quoted.punctuation.tag,string.quoted.embedded.tag, string.quoted.double.tag", + "settings": { + "foreground": "#6A8759" + } + }, + { + "name": "Tag function parenthesis", + "scope": "tag.punctuation.begin.arrow.parameters.embedded,tag.punctuation.end.arrow.parameters.embedded", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "Object-literal key class", + "scope": "object-literal.object.member.key.field.other,object-literal.object.member.key.accessor,object-literal.object.member.key.array.brace.square", + "settings": { + "foreground": "#CCCCCC" + } + }, + { + "name": "CSS Property-value", + "scope": "property-list.property-value,property-list.constant", + "settings": { + "foreground": "#A5C261" + } + }, + { + "name": "CSS Property variable", + "scope": "support.type.property-name.variable.css,support.type.property-name.variable.scss,variable.scss", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "CSS Property entity", + "scope": "entity.other.attribute-name.class.css,entity.other.attribute-name.class.scss,entity.other.attribute-name.parent-selector-suffix.css,entity.other.attribute-name.parent-selector-suffix.scss", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "CSS Property-value", + "scope": "property-list.property-value.rgb-value, keyword.other.unit.css,keyword.other.unit.scss", + "settings": { + "foreground": "#7A9EC2" + } + }, + { + "name": "CSS Property-value function", + "scope": "property-list.property-value.function", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "CSS constant variables", + "scope": "support.constant.property-value.css,support.constant.property-value.scss", + "settings": { + "foreground": "#A5C261" + } + }, + { + "name": "CSS Tag", + "scope": "css.entity.name.tag,scss.entity.name.tag", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "CSS ID, Selector", + "scope": "meta.selector.css, entity.attribute-name.id, entity.other.attribute-name.pseudo-class.css,entity.other.attribute-name.pseudo-element.css", + "settings": { + "foreground": "#FFC66D" + } + }, + { + "name": "CSS Keyword", + "scope": "keyword.scss,keyword.css", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Triple-slash Directive Tag", + "scope": "triple-slash.tag", + "settings": { + "foreground": "#CCCCCC", + "fontStyle": "italic" + } + }, + { + "scope": "token.info-token", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "token.warn-token", + "settings": { + "foreground": "#cd9731" + } + }, + { + "scope": "token.error-token", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "token.debug-token", + "settings": { + "foreground": "#b267e6" + } + }, + { + "name": "Python operators", + "scope": "keyword.operator.logical.python", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "Dart class type", + "scope": "support.class.dart", + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "PHP variables", + "scope": ["variable.language.php", "variable.other.php"], + "settings": { + "foreground": "#9E7BB0" + } + }, + { + "name": "Perl specific", + "scope": ["variable.other.readwrite.perl"], + "settings": { + "foreground": "#9E7BB0" + } + }, + { + "name": "PHP variables", + "scope": ["variable.other.property.php"], + "settings": { + "foreground": "#CC8242" + } + }, + { + "name": "PHP variables", + "scope": ["support.variable.property.php"], + "settings": { + "foreground": "#FFC66D" + } + } + ] +} diff --git a/layout-editor/src/main/assets/editor/textmate/default.json b/layout-editor/src/main/assets/editor/textmate/default.json new file mode 100644 index 00000000..73eb60e2 --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/default.json @@ -0,0 +1,60 @@ +{ + "name": "Tomorrow-Night-XML", + "author": "Vivek", + "version": "1.0", + "settings": [ + { + "name": "Tag", + "scope": "entity.name.tag", + "settings": { + "fontStyle": "bold", + "foreground": "#F92672" + } + }, + { + "name": "Attribute names", + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#A6E22E" + } + }, + { + "name": "Attribute values", + "scope": "string.quoted", + "settings": { + "fontStyle": "italic", + "foreground": "#E6DB74" + } + }, + { + "name": "Comments", + "scope": "comment.block", + "settings": { + "fontStyle": "italic", + "foreground": "#75715E" + } + }, + { + "name": "CDATA", + "scope": "string.unquoted.cdata.xml", + "settings": { + "foreground": "#66D9EF" + } + }, + { + "name": "Namespace Tag", + "scope": "entity.name.namespace.xml", + "settings": { + "fontStyle": "bold", + "foreground": "#AE81FF" + } + }, + { + "name": "Default Text", + "scope": "text.xml", + "settings": { + "foreground": "#F8F8F2" + } + } + ] +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/editor/textmate/languages.json b/layout-editor/src/main/assets/editor/textmate/languages.json new file mode 100644 index 00000000..50770b0f --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/languages.json @@ -0,0 +1,10 @@ +{ + "languages": [ + { + "grammar": "editor/textmate/xml/syntaxes/xml.tmLanguage.json", + "name": "xml", + "scopeName": "text.xml", + "languageConfiguration": "editor/textmate/xml/language-configuration.json" + } + ] +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/editor/textmate/quietlight.json b/layout-editor/src/main/assets/editor/textmate/quietlight.json new file mode 100644 index 00000000..9b8bec18 --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/quietlight.json @@ -0,0 +1,542 @@ +{ + "name": "Quiet Light", + "tokenColors": [ + { + "settings": { + "foreground": "#333333" + } + }, + { + "scope": [ + "meta.embedded", + "source.groovy.embedded" + ], + "settings": { + "foreground": "#333333" + } + }, + { + "name": "Comments", + "scope": [ + "comment", + "punctuation.definition.comment" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#AAAAAA" + } + }, + { + "name": "Comments: Preprocessor", + "scope": "comment.block.preprocessor", + "settings": { + "fontStyle": "", + "foreground": "#AAAAAA" + } + }, + { + "name": "Comments: Documentation", + "scope": [ + "comment.documentation", + "comment.block.documentation", + "comment.block.documentation punctuation.definition.comment " + ], + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "foreground": "#cd3131" + } + }, + { + "name": "Invalid - Illegal", + "scope": "invalid.illegal", + "settings": { + "foreground": "#660000" + } + }, + { + "name": "Operators", + "scope": "keyword.operator", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Keywords", + "scope": [ + "keyword", + "storage" + ], + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "Types", + "scope": [ + "storage.type", + "support.type" + ], + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "Language Constants", + "scope": [ + "constant.language", + "support.constant", + "variable.language" + ], + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "Variables", + "scope": [ + "variable", + "support.variable" + ], + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "Functions", + "scope": [ + "entity.name.function", + "support.function" + ], + "settings": { + "fontStyle": "bold", + "foreground": "#AA3731" + } + }, + { + "name": "Classes", + "scope": [ + "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution", + "entity.other.inherited-class", + "support.class" + ], + "settings": { + "fontStyle": "bold", + "foreground": "#7A3E9D" + } + }, + { + "name": "Exceptions", + "scope": "entity.name.exception", + "settings": { + "foreground": "#660000" + } + }, + { + "name": "Sections", + "scope": "entity.name.section", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Numbers, Characters", + "scope": [ + "constant.numeric", + "constant.character", + "constant" + ], + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "Strings", + "scope": "string", + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "Strings: Escape Sequences", + "scope": "constant.character.escape", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Strings: Regular Expressions", + "scope": "string.regexp", + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "Strings: Symbols", + "scope": "constant.other.symbol", + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "Punctuation", + "scope": "punctuation", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "HTML: Doctype Declaration", + "scope": [ + "meta.tag.sgml.doctype", + "meta.tag.sgml.doctype string", + "meta.tag.sgml.doctype entity.name.tag", + "meta.tag.sgml punctuation.definition.tag.html" + ], + "settings": { + "foreground": "#AAAAAA" + } + }, + { + "name": "HTML: Tags", + "scope": [ + "meta.tag", + "punctuation.definition.tag.html", + "punctuation.definition.tag.begin.html", + "punctuation.definition.tag.end.html" + ], + "settings": { + "foreground": "#91B3E0" + } + }, + { + "name": "HTML: Tag Names", + "scope": "entity.name.tag", + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "HTML: Attribute Names", + "scope": [ + "meta.tag entity.other.attribute-name", + "entity.other.attribute-name.html" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#8190A0" + } + }, + { + "name": "HTML: Entities", + "scope": [ + "constant.character.entity", + "punctuation.definition.entity" + ], + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "CSS: Selectors", + "scope": [ + "meta.selector", + "meta.selector entity", + "meta.selector entity punctuation", + "entity.name.tag.css" + ], + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "CSS: Property Names", + "scope": [ + "meta.property-name", + "support.type.property-name" + ], + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "CSS: Property Values", + "scope": [ + "meta.property-value", + "meta.property-value constant.other", + "support.constant.property-value" + ], + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "CSS: Important Keyword", + "scope": "keyword.other.important", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Changed", + "scope": "markup.changed", + "settings": { + "foreground": "#000000" + } + }, + { + "name": "Markup: Deletion", + "scope": "markup.deleted", + "settings": { + "foreground": "#000000" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.strikethrough", + "settings": { + "fontStyle": "strikethrough" + } + }, + { + "name": "Markup: Error", + "scope": "markup.error", + "settings": { + "foreground": "#660000" + } + }, + { + "name": "Markup: Insertion", + "scope": "markup.inserted", + "settings": { + "foreground": "#000000" + } + }, + { + "name": "Markup: Link", + "scope": "meta.link", + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "Markup: Output", + "scope": [ + "markup.output", + "markup.raw" + ], + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Markup: Prompt", + "scope": "markup.prompt", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Markup: Heading", + "scope": "markup.heading", + "settings": { + "foreground": "#AA3731" + } + }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Traceback", + "scope": "markup.traceback", + "settings": { + "foreground": "#660000" + } + }, + { + "name": "Markup: Underline", + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "Markup Styling", + "scope": [ + "markup.bold", + "markup.italic" + ], + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#9C5D27" + } + }, + { + "name": "Extra: Diff Range", + "scope": [ + "meta.diff.range", + "meta.diff.index", + "meta.separator" + ], + "settings": { + "foreground": "#434343" + } + }, + { + "name": "Extra: Diff From", + "scope": [ + "meta.diff.header.from-file", + "punctuation.definition.from-file.diff" + ], + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "Extra: Diff To", + "scope": [ + "meta.diff.header.to-file", + "punctuation.definition.to-file.diff" + ], + "settings": { + "foreground": "#4B69C6" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted.diff", + "settings": { + "foreground": "#C73D20" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed.diff", + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted.diff", + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "JSX: Tags", + "scope": [ + "punctuation.definition.tag.js", + "punctuation.definition.tag.begin.js", + "punctuation.definition.tag.end.js" + ], + "settings": { + "foreground": "#91B3E0" + } + }, + { + "name": "JSX: InnerText", + "scope": "meta.jsx.children.js", + "settings": { + "foreground": "#333333ff" + } + } + ], + "colors": { + "focusBorder": "#A6B39B", + "pickerGroup.foreground": "#A6B39B", + "pickerGroup.border": "#749351", + "list.activeSelectionForeground": "#6c6c6c", + "quickInputList.focusBackground": "#CADEB9", + "list.hoverBackground": "#e0e0e0", + "list.activeSelectionBackground": "#c4d9b1", + "list.inactiveSelectionBackground": "#d3dbcd", + "list.highlightForeground": "#9769dc", + "selection.background": "#C9D0D9", + "editor.background": "#F5F5F5", + "editorWhitespace.foreground": "#AAAAAA", + "editor.lineHighlightBackground": "#E4F6D4", + "editorLineNumber.activeForeground": "#9769dc", + "editor.selectionBackground": "#C9D0D9", + "minimap.selectionHighlight": "#C9D0D9", + "panel.background": "#F5F5F5", + "sideBar.background": "#F2F2F2", + "sideBarSectionHeader.background": "#ede8ef", + "editorLineNumber.foreground": "#6D705B", + "editorCursor.foreground": "#54494B", + "inputOption.activeBorder": "#adafb7", + "dropdown.background": "#F5F5F5", + "editor.findMatchBackground": "#BF9CAC", + "editor.findMatchHighlightBackground": "#edc9d8", + "peekViewEditor.matchHighlightBackground": "#C2DFE3", + "peekViewTitle.background": "#F2F8FC", + "peekViewEditor.background": "#F2F8FC", + "peekViewResult.background": "#F2F8FC", + "peekView.border": "#705697", + "peekViewResult.matchHighlightBackground": "#93C6D6", + "tab.lastPinnedBorder": "#c9d0d9", + "statusBar.background": "#705697", + "welcomePage.tileBackground": "#f0f0f7", + "statusBar.noFolderBackground": "#705697", + "statusBar.debuggingBackground": "#705697", + "statusBarItem.remoteBackground": "#4e3c69", + "ports.iconRunningProcessForeground": "#749351", + "activityBar.background": "#EDEDF5", + "activityBar.foreground": "#705697", + "activityBarBadge.background": "#705697", + "titleBar.activeBackground": "#c4b7d7", + "button.background": "#705697", + "editorGroup.dropBackground": "#C9D0D988", + "inputValidation.infoBorder": "#4ec1e5", + "inputValidation.infoBackground": "#f2fcff", + "inputValidation.warningBackground": "#fffee2", + "inputValidation.warningBorder": "#ffe055", + "inputValidation.errorBackground": "#ffeaea", + "inputValidation.errorBorder": "#f1897f", + "errorForeground": "#f1897f", + "badge.background": "#705697AA", + "progressBar.background": "#705697", + "walkThrough.embeddedEditorBackground": "#00000014", + "editorIndentGuide.background": "#aaaaaa60", + "editorIndentGuide.activeBackground": "#777777b0" + }, + "semanticHighlighting": true +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/editor/textmate/solarized_drak.json b/layout-editor/src/main/assets/editor/textmate/solarized_drak.json new file mode 100644 index 00000000..b0de1a5d --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/solarized_drak.json @@ -0,0 +1,421 @@ +{ + "name": "Solarized (dark)", + "tokenColors": [ + { + "settings": { + "foreground": "#839496" + } + }, + { + "scope": [ + "meta.embedded", + "source.groovy.embedded" + ], + "settings": { + "foreground": "#839496" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "fontStyle": "italic", + "foreground": "#586E75" + } + }, + { + "name": "String", + "scope": "string", + "settings": { + "foreground": "#2AA198" + } + }, + { + "name": "Regexp", + "scope": "string.regexp", + "settings": { + "foreground": "#DC322F" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#D33682" + } + }, + { + "name": "Variable", + "scope": [ + "variable.language", + "variable.other" + ], + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "bold", + "foreground": "#93A1A1" + } + }, + { + "name": "Class name", + "scope": [ + "entity.name.class", + "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution" + ], + "settings": { + "fontStyle": "", + "foreground": "#CB4B16" + } + }, + { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Variable start", + "scope": "punctuation.definition.variable", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Embedded code markers", + "scope": [ + "punctuation.section.embedded.begin", + "punctuation.section.embedded.end" + ], + "settings": { + "foreground": "#DC322F" + } + }, + { + "name": "Built-in constant", + "scope": [ + "constant.language", + "meta.preprocessor" + ], + "settings": { + "foreground": "#B58900" + } + }, + { + "name": "Support.construct", + "scope": [ + "support.function.construct", + "keyword.other.new" + ], + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "User-defined constant", + "scope": [ + "constant.character", + "constant.other" + ], + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": { + "foreground": "#6C71C4" + } + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "settings": {} + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Tag start/end", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#586E75" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#93A1A1" + } + }, + { + "name": "Library function", + "scope": "support.function", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Continuation", + "scope": "punctuation.separator.continuation", + "settings": { + "foreground": "#DC322F" + } + }, + { + "name": "Library constant", + "scope": [ + "support.constant", + "support.variable" + ], + "settings": {} + }, + { + "name": "Library class/type", + "scope": [ + "support.type", + "support.class" + ], + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Library Exception", + "scope": "support.type.exception", + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "Library variable", + "scope": "support.other.variable", + "settings": {} + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "foreground": "#DC322F" + } + }, + { + "name": "diff: header", + "scope": [ + "meta.diff", + "meta.diff.header" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#268BD2" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted", + "settings": { + "fontStyle": "", + "foreground": "#DC322F" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed", + "settings": { + "fontStyle": "", + "foreground": "#CB4B16" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#B58900" + } + }, + { + "name": "Markup Styling", + "scope": [ + "markup.bold", + "markup.italic" + ], + "settings": { + "foreground": "#D33682" + } + }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.strikethrough", + "settings": { + "fontStyle": "strikethrough" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#2AA198" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#268BD2" + } + }, + { + "name": "Markup Setext Header", + "scope": "markup.heading.setext", + "settings": { + "fontStyle": "", + "foreground": "#268BD2" + } + } + ], + "colors": { + "focusBorder": "#2AA19899", + "selection.background": "#2AA19899", + "input.background": "#003847", + "input.foreground": "#93A1A1", + "input.placeholderForeground": "#93A1A1AA", + "inputOption.activeBorder": "#2AA19899", + "inputValidation.infoBorder": "#363b5f", + "inputValidation.infoBackground": "#052730", + "inputValidation.warningBackground": "#5d5938", + "inputValidation.warningBorder": "#9d8a5e", + "inputValidation.errorBackground": "#571b26", + "inputValidation.errorBorder": "#a92049", + "errorForeground": "#ffeaea", + "badge.background": "#047aa6", + "progressBar.background": "#047aa6", + "dropdown.background": "#00212B", + "dropdown.border": "#2AA19899", + "button.background": "#2AA19899", + "list.activeSelectionBackground": "#005A6F", + "quickInputList.focusBackground": "#005A6F", + "list.hoverBackground": "#004454AA", + "list.inactiveSelectionBackground": "#00445488", + "list.dropBackground": "#00445488", + "list.highlightForeground": "#1ebcc5", + "editor.background": "#002B36", + "editor.foreground": "#839496", + "editorWidget.background": "#00212B", + "editorCursor.foreground": "#D30102", + "editorWhitespace.foreground": "#93A1A180", + "editor.lineHighlightBackground": "#073642", + "editorLineNumber.activeForeground": "#949494", + "editor.selectionBackground": "#274642", + "minimap.selectionHighlight": "#274642", + "editorIndentGuide.background": "#93A1A180", + "editorIndentGuide.activeBackground": "#C3E1E180", + "editorHoverWidget.background": "#004052", + "editorMarkerNavigationError.background": "#AB395B", + "editorMarkerNavigationWarning.background": "#5B7E7A", + "editor.selectionHighlightBackground": "#005A6FAA", + "editor.wordHighlightBackground": "#004454AA", + "editor.wordHighlightStrongBackground": "#005A6FAA", + "editorBracketHighlight.foreground1": "#cdcdcdff", + "editorBracketHighlight.foreground2": "#b58900ff", + "editorBracketHighlight.foreground3": "#d33682ff", + "peekViewResult.background": "#00212B", + "peekViewEditor.background": "#10192c", + "peekViewTitle.background": "#00212B", + "peekView.border": "#2b2b4a", + "peekViewEditor.matchHighlightBackground": "#7744AA40", + "titleBar.activeBackground": "#002C39", + "editorGroup.border": "#00212B", + "editorGroup.dropBackground": "#2AA19844", + "editorGroupHeader.tabsBackground": "#004052", + "tab.activeForeground": "#d6dbdb", + "tab.activeBackground": "#002B37", + "tab.inactiveForeground": "#93A1A1", + "tab.inactiveBackground": "#004052", + "tab.border": "#003847", + "tab.lastPinnedBorder": "#2AA19844", + "activityBar.background": "#003847", + "panel.border": "#2b2b4a", + "sideBar.background": "#00212B", + "sideBarTitle.foreground": "#93A1A1", + "statusBar.foreground": "#93A1A1", + "statusBar.background": "#00212B", + "statusBar.debuggingBackground": "#00212B", + "statusBar.noFolderBackground": "#00212B", + "statusBarItem.remoteBackground": "#2AA19899", + "ports.iconRunningProcessForeground": "#369432", + "statusBarItem.prominentBackground": "#003847", + "statusBarItem.prominentHoverBackground": "#003847", + "debugToolBar.background": "#00212B", + "debugExceptionWidget.background": "#00212B", + "debugExceptionWidget.border": "#AB395B", + "pickerGroup.foreground": "#2AA19899", + "pickerGroup.border": "#2AA19899", + "terminal.ansiBlack": "#073642", + "terminal.ansiRed": "#dc322f", + "terminal.ansiGreen": "#859900", + "terminal.ansiYellow": "#b58900", + "terminal.ansiBlue": "#268bd2", + "terminal.ansiMagenta": "#d33682", + "terminal.ansiCyan": "#2aa198", + "terminal.ansiWhite": "#eee8d5", + "terminal.ansiBrightBlack": "#002b36", + "terminal.ansiBrightRed": "#cb4b16", + "terminal.ansiBrightGreen": "#586e75", + "terminal.ansiBrightYellow": "#657b83", + "terminal.ansiBrightBlue": "#839496", + "terminal.ansiBrightMagenta": "#6c71c4", + "terminal.ansiBrightCyan": "#93a1a1", + "terminal.ansiBrightWhite": "#fdf6e3" + }, + "semanticHighlighting": true +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/editor/textmate/xml/language-configuration.json b/layout-editor/src/main/assets/editor/textmate/xml/language-configuration.json new file mode 100644 index 00000000..934eb6b5 --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/xml/language-configuration.json @@ -0,0 +1,104 @@ +{ + "comments": { + "blockComment": [ + "" + ] + }, + "brackets": [ + [ + "" + ], + [ + "<", + ">" + ], + [ + "{", + "}" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "", + "notIn": [ + "comment", + "string" + ] + }, + { + "open": "", + "notIn": [ + "comment", + "string" + ] + } + ], + "surroundingPairs": [ + { + "open": "'", + "close": "'" + }, + { + "open": "\"", + "close": "\"" + }, + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "<", + "close": ">" + } + ], + "colorizedBracketPairs": [], + "folding": { + "markers": { + "start": "^\\s*", + "end": "^\\s*" + } + }, + "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)" +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/editor/textmate/xml/syntaxes/xml.tmLanguage.json b/layout-editor/src/main/assets/editor/textmate/xml/syntaxes/xml.tmLanguage.json new file mode 100644 index 00000000..181a9822 --- /dev/null +++ b/layout-editor/src/main/assets/editor/textmate/xml/syntaxes/xml.tmLanguage.json @@ -0,0 +1,389 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/atom/language-xml/blob/master/grammars/xml.cson", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/atom/language-xml/commit/7bc75dfe779ad5b35d9bf4013d9181864358cb49", + "name": "XML", + "scopeName": "text.xml", + "patterns": [ + { + "begin": "(<\\?)\\s*([-_a-zA-Z0-9]+)", + "captures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "entity.name.tag.xml" + } + }, + "end": "(\\?>)", + "name": "meta.tag.preprocessor.xml", + "patterns": [ + { + "match": " ([a-zA-Z-]+)", + "name": "entity.other.attribute-name.xml" + }, + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + { + "begin": "()", + "name": "meta.tag.sgml.doctype.xml", + "patterns": [ + { + "include": "#internalSubset" + } + ] + }, + { + "include": "#comments" + }, + { + "begin": "(<)((?:([-_a-zA-Z0-9]+)(:))?([-_a-zA-Z0-9:]+))(?=(\\s[^>]*)?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "entity.name.tag.xml" + }, + "3": { + "name": "entity.name.tag.namespace.xml" + }, + "4": { + "name": "punctuation.separator.namespace.xml" + }, + "5": { + "name": "entity.name.tag.localname.xml" + } + }, + "end": "(>)()", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "punctuation.definition.tag.xml" + }, + "3": { + "name": "entity.name.tag.xml" + }, + "4": { + "name": "entity.name.tag.namespace.xml" + }, + "5": { + "name": "punctuation.separator.namespace.xml" + }, + "6": { + "name": "entity.name.tag.localname.xml" + }, + "7": { + "name": "punctuation.definition.tag.xml" + } + }, + "name": "meta.tag.no-content.xml", + "patterns": [ + { + "include": "#tagStuff" + } + ] + }, + { + "begin": "()", + "name": "meta.tag.xml", + "patterns": [ + { + "include": "#tagStuff" + } + ] + }, + { + "include": "#entity" + }, + { + "include": "#bare-ampersand" + }, + { + "begin": "<%@", + "beginCaptures": { + "0": { + "name": "punctuation.section.embedded.begin.xml" + } + }, + "end": "%>", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.end.xml" + } + }, + "name": "source.java-props.embedded.xml", + "patterns": [ + { + "match": "page|include|taglib", + "name": "keyword.other.page-props.xml" + } + ] + }, + { + "begin": "<%[!=]?(?!--)", + "beginCaptures": { + "0": { + "name": "punctuation.section.embedded.begin.xml" + } + }, + "end": "(?!--)%>", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.end.xml" + } + }, + "name": "source.java.embedded.xml", + "patterns": [ + { + "include": "source.java" + } + ] + }, + { + "begin": "", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.unquoted.cdata.xml" + } + ], + "repository": { + "EntityDecl": { + "begin": "()", + "patterns": [ + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + "bare-ampersand": { + "match": "&", + "name": "invalid.illegal.bad-ampersand.xml" + }, + "doublequotedString": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.xml" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.quoted.double.xml", + "patterns": [ + { + "include": "#entity" + }, + { + "include": "#bare-ampersand" + } + ] + }, + "entity": { + "captures": { + "1": { + "name": "punctuation.definition.constant.xml" + }, + "3": { + "name": "punctuation.definition.constant.xml" + } + }, + "match": "(&)([:a-zA-Z_][:a-zA-Z0-9_.-]*|#[0-9]+|#x[0-9a-fA-F]+)(;)", + "name": "constant.character.entity.xml" + }, + "internalSubset": { + "begin": "(\\[)", + "captures": { + "1": { + "name": "punctuation.definition.constant.xml" + } + }, + "end": "(\\])", + "name": "meta.internalsubset.xml", + "patterns": [ + { + "include": "#EntityDecl" + }, + { + "include": "#parameterEntity" + }, + { + "include": "#comments" + } + ] + }, + "parameterEntity": { + "captures": { + "1": { + "name": "punctuation.definition.constant.xml" + }, + "3": { + "name": "punctuation.definition.constant.xml" + } + }, + "match": "(%)([:a-zA-Z_][:a-zA-Z0-9_.-]*)(;)", + "name": "constant.character.parameter-entity.xml" + }, + "singlequotedString": { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.xml" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.quoted.single.xml", + "patterns": [ + { + "include": "#entity" + }, + { + "include": "#bare-ampersand" + } + ] + }, + "tagStuff": { + "patterns": [ + { + "captures": { + "1": { + "name": "entity.other.attribute-name.namespace.xml" + }, + "2": { + "name": "entity.other.attribute-name.xml" + }, + "3": { + "name": "punctuation.separator.namespace.xml" + }, + "4": { + "name": "entity.other.attribute-name.localname.xml" + } + }, + "match": "(?:^|\\s+)(?:([-\\w.]+)((:)))?([-\\w.:]+)\\s*=" + }, + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + "comments": { + "patterns": [ + { + "begin": "<%--", + "captures": { + "0": { + "name": "punctuation.definition.comment.xml" + } + }, + "end": "--%>", + "name": "comment.block.xml" + }, + { + "begin": "", + "name": "comment.block.xml", + "patterns": [ + { + "begin": "--(?!>)", + "captures": { + "0": { + "name": "invalid.illegal.bad-comments-or-CDATA.xml" + } + }, + "end": "", + "name": "comment.block.xml" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/layout-editor/src/main/assets/icon_day.png b/layout-editor/src/main/assets/icon_day.png new file mode 100644 index 00000000..bfaa9441 Binary files /dev/null and b/layout-editor/src/main/assets/icon_day.png differ diff --git a/layout-editor/src/main/assets/icon_night.png b/layout-editor/src/main/assets/icon_night.png new file mode 100644 index 00000000..bfaa9441 Binary files /dev/null and b/layout-editor/src/main/assets/icon_night.png differ diff --git a/layout-editor/src/main/assets/palette/buttons.json b/layout-editor/src/main/assets/palette/buttons.json new file mode 100644 index 00000000..5ad9420f --- /dev/null +++ b/layout-editor/src/main/assets/palette/buttons.json @@ -0,0 +1,81 @@ +[ + { + "name": "Button", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.ButtonDesign", + "iconName": "ic_palette_button", + "defaultAttributes": { + "android:text": "Button" + } + }, + { + "name": "ImageButton", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.ImageButtonDesign", + "iconName": "ic_palette_image_button", + "defaultAttributes": { + "android:src": "@drawable/ic_launcher_round" + } + }, + { + "name": "ChipGroup", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.ChipGroupDesign", + "iconName": "ic_palette_chip_group", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "Chip", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.ChipDesign", + "iconName": "ic_palette_chip", + "defaultAttributes": { + "android:text": "Chip" + } + }, + { + "name": "CheckBox", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.CheckBoxDesign", + "iconName": "ic_palette_check_box", + "defaultAttributes": { + "android:text": "CheckBox" + } + }, + { + "name": "RadioGroup", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.RadioGroupDesign", + "iconName": "ic_palette_radio_group", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "RadioButton", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.RadioButtonDesign", + "iconName": "ic_palette_radio_button", + "defaultAttributes": { + "android:text": "RadioButton" + } + }, + { + "name": "ToggleButton", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.ToggleButtonDesign", + "iconName": "ic_palette_toggle_button", + "defaultAttributes": { + "android:text": "ToggleButton" + } + }, + { + "name": "Switch", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.SwitchDesign", + "iconName": "ic_palette_switch", + "defaultAttributes": { + "android:text": "Switch" + } + }, + { + "name": "FloatingActionButton", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.FloatingActionButtonDesign", + "iconName": "ic_palette_floating_action_button" + } +] \ No newline at end of file diff --git a/layout-editor/src/main/assets/palette/common.json b/layout-editor/src/main/assets/palette/common.json new file mode 100644 index 00000000..46f546aa --- /dev/null +++ b/layout-editor/src/main/assets/palette/common.json @@ -0,0 +1,85 @@ +[ + { + "name": "LinearLayout (H)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.LinearLayoutDesign", + "iconName": "ic_palette_linear_layout_horz", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:orientation": "horizontal", + "android:padding": "8dp" + } + }, + { + "name": "LinearLayout (V)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.LinearLayoutDesign", + "iconName": "ic_palette_linear_layout_vert", + "defaultAttributes": { + "android:layout_height": "match_parent", + "android:orientation": "vertical", + "android:padding": "8dp" + } + }, + { + "name": "ScrollView (unable to scroll in the editor)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.ScrollViewDesign", + "iconName": "ic_palette_scroll_view", + "defaultAttributes": { + "android:layout_height": "match_parent", + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "HorizontalScrollView (unable to scroll in the editor)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.HorizontalScrollViewDesign", + "iconName": "ic_palette_scroll_view", + "defaultAttributes": { + "android:layout_height": "match_parent", + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "TextView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextViewDesign", + "iconName": "ic_palette_text_view", + "defaultAttributes": { + "android:text": "TextView" + } + }, + { + "name": "Button", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.ButtonDesign", + "iconName": "ic_palette_button", + "defaultAttributes": { + "android:text": "Button" + } + }, + { + "name": "ImageView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.ImageViewDesign", + "iconName": "ic_palette_image_view", + "defaultAttributes": { + "android:layout_width": "48dp", + "android:layout_height": "48dp", + "android:src": "@drawable/ic_launcher_round" + } + }, + { + "name": "Switch", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.buttons.SwitchDesign", + "iconName": "ic_palette_switch", + "defaultAttributes": { + "android:text": "Switch" + } + }, + { + "name": "RecyclerView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.RecyclerViewDesign", + "iconName": "ic_palette_recycler_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"100dp" + } + } +] \ No newline at end of file diff --git a/layout-editor/src/main/assets/palette/containers.json b/layout-editor/src/main/assets/palette/containers.json new file mode 100644 index 00000000..6b8e8630 --- /dev/null +++ b/layout-editor/src/main/assets/palette/containers.json @@ -0,0 +1,155 @@ +[ + { + "name": "Spinner", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.SpinnerDesign", + "iconName": "ic_palette_spinner" + }, + { + "name": "RecyclerView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.RecyclerViewDesign", + "iconName": "ic_palette_recycler_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"100dp" + } + }, + { + "name": "ScrollView (unable to scroll in the editor)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.ScrollViewDesign", + "iconName": "ic_palette_scroll_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"match_parent", + "android:padding": "8dp" + } + }, + { + "name": "HorizontalScrollView (unable to scroll in the editor)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.HorizontalScrollViewDesign", + "iconName": "ic_palette_horizontal_scroll_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"match_parent", + "android:padding": "8dp" + } + }, + { + "name": "NestedScrollView (unable to scroll in the editor)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.NestedScrollViewDesign", + "iconName": "ic_palette_nested_scroll_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"match_parent", + "android:padding": "8dp" + } + }, + { + "name": "ViewPager", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.ViewPagerDesign", + "iconName": "ic_palette_view_pager", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"match_parent" + } + }, + { + "name": "CardView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.CardViewDesign", + "iconName": "ic_palette_card_view", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "AppBarLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.AppBarLayoutDesign", + "iconName": "ic_palette_app_bar_layout", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "BottomAppBar", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.BottomAppBarDesign", + "iconName": "ic_palette_bottom_app_bar", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:gravity":"bottom" + } + }, + { + "name": "NavigationView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.NavigationViewDesign", + "iconName": "ic_palette_navigation_view", + "defaultAttributes": { + "android:layout_width":"100dp" + } + }, + { + "name": "BottomNavigationView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.BottomNavigationViewDesign", + "iconName": "ic_palette_bottom_navigation_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:gravity":"bottom" + } + }, + { + "name": "Toolbar", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.ToolbarDesign", + "iconName": "ic_palette_toolbar", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "MaterialToolbar", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.MaterialToolbarDesign", + "iconName": "ic_palette_toolbar", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "TabLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.TabLayoutDesign", + "iconName": "ic_palette_tab_layout", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "TabItem", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.containers.TabItemDesign", + "iconName": "ic_palette_tab_item", + "defaultAttributes": { + "android:layout_height":"match_parent" + } + }, + { + "name": "ListView (old, use RecyclerView instead)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.legacy.ListViewDesign", + "iconName": "ic_palette_list_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"100dp" + } + }, + { + "name": "TabHost (old, use TabLayout + ViewPager2 instead)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.legacy.TabHostDesign", + "iconName": "ic_palette_tab_host", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "GridView (old, use RecyclerView + GridLayoutManager instead)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.legacy.GridViewDesign", + "iconName": "ic_palette_grid_view", + "defaultAttributes": { + "android:layout_width":"match_parent", + "android:layout_height":"100dp" + } + } +] \ No newline at end of file diff --git a/layout-editor/src/main/assets/palette/layouts.json b/layout-editor/src/main/assets/palette/layouts.json new file mode 100644 index 00000000..85763176 --- /dev/null +++ b/layout-editor/src/main/assets/palette/layouts.json @@ -0,0 +1,103 @@ +[ + { + "name": "RelativeLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.RelativeLayoutDesign", + "iconName": "ic_palette_relative_layout", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "ConstraintLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.ConstraintLayoutDesign", + "iconName": "ic_palette_constraint_layout", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "LinearLayout (H)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.LinearLayoutDesign", + "iconName": "ic_palette_linear_layout_horz", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:orientation": "horizontal", + "android:padding": "8dp" + } + }, + { + "name": "LinearLayout (V)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.LinearLayoutDesign", + "iconName": "ic_palette_linear_layout_vert", + "defaultAttributes": { + "android:layout_height": "match_parent", + "android:orientation": "vertical", + "android:padding": "8dp" + } + }, + { + "name": "FrameLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.FrameLayoutDesign", + "iconName": "ic_palette_frame_layout", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "TableLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.TableLayoutDesign", + "iconName": "ic_palette_table_layout", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "TableRow", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.TableRowDesign", + "iconName": "ic_palette_table_row", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "Space", + "className": "android.widget.Space", + "iconName": "ic_palette_space", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "GridLayout (old, use ConstraintLayout instead)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.legacy.GridLayoutDesign", + "iconName": "ic_palette_grid_layout", + "defaultAttributes": { + "android:layout_width":"match_parent" + } + }, + { + "name": "CoordinatorLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.CoordinatorLayoutDesign", + "iconName": "ic_palette_coordinator_layout", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:padding": "8dp" + } + }, + { + "name": "DrawerLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.layouts.DrawerLayoutDesign", + "iconName": "ic_palette_drawer_layout", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "match_parent", + "android:padding": "8dp" + } + } +] \ No newline at end of file diff --git a/layout-editor/src/main/assets/palette/text.json b/layout-editor/src/main/assets/palette/text.json new file mode 100644 index 00000000..370e177b --- /dev/null +++ b/layout-editor/src/main/assets/palette/text.json @@ -0,0 +1,161 @@ +[ + { + "name": "TextView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextViewDesign", + "iconName": "ic_palette_text_view", + "defaultAttributes": { + "android:text": "@string/default_string" + } + }, + { + "name": "Plain Text", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_edit_text", + "defaultAttributes": { + "android:hint": "Plain Text", + "android:inputType":"text" + } + }, + { + "name": "Password", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_password_textfield", + "defaultAttributes": { + "android:hint": "Password", + "android:inputType":"textPassword" + } + }, + { + "name": "Password (Numeric)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_password_numeric_textfield", + "defaultAttributes": { + "android:hint": "Password (Numeric)", + "android:inputType":"numberPassword" + } + }, + { + "name": "E-mail", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_email_textfield", + "defaultAttributes": { + "android:hint": "E-mail", + "android:inputType":"textEmailAddress" + } + }, + { + "name": "Phone", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_phone_textfield", + "defaultAttributes": { + "android:hint": "Phone", + "android:inputType":"phone" + } + }, + { + "name": "Postal Address", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_postal_address_textfield", + "defaultAttributes": { + "android:hint": "Postal Address", + "android:inputType":"textPostalAddress" + } + }, + { + "name": "Multiline Text", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_textfield_multiline", + "defaultAttributes": { + "android:hint": "Multiline Text", + "android:inputType":"textMultiLine" + } + }, + { + "name": "Time", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_time_textfield", + "defaultAttributes": { + "android:hint": "Time", + "android:inputType":"time" + } + }, + { + "name": "Date", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_date_textfield", + "defaultAttributes": { + "android:hint": "Date", + "android:inputType":"date" + } + }, + { + "name": "Number", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_number_textfield", + "defaultAttributes": { + "android:hint": "Number", + "android:inputType":"number" + } + }, + { + "name": "Number (Signed)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_number_signed_textfield", + "defaultAttributes": { + "android:hint": "Number (Signed)", + "android:inputType":"numberSigned" + } + }, + { + "name": "Number (Decimal)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.EditTextDesign", + "iconName": "ic_palette_number_decimal_textfield", + "defaultAttributes": { + "android:hint": "Number (Decimal)", + "android:inputType":"numberDecimal" + } + }, + { + "name": "AutoCompleteTextView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.AutoCompleteTextViewDesign", + "iconName": "ic_palette_auto_complete_text_view", + "defaultAttributes": { + "android:hint": "AutoCompleteTextView" + } + }, + { + "name": "MultiAutoCompleteTextView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.MultiAutoCompleteTextViewDesign", + "iconName": "ic_palette_multi_auto_complete_text_view", + "defaultAttributes": { + "android:hint": "MultiAutoCompleteTextView" + } + }, + { + "name": "CheckedTextView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.CheckedTextViewDesign", + "iconName": "ic_palette_checked_text_view", + "defaultAttributes": { + "android:text": "CheckedTextView" + } + }, + { + "name": "TextInputLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputLayoutDesign", + "iconName": "ic_palette_linear_layout_vert", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "wrap_content" + } + }, + { + "name": "TextInputEditText", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputEditTextDesign", + "iconName": "ic_palette_edit_text", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "wrap_content", + "android:inputType": "text" + } + } +] \ No newline at end of file diff --git a/layout-editor/src/main/assets/palette/widgets.json b/layout-editor/src/main/assets/palette/widgets.json new file mode 100644 index 00000000..a15b4070 --- /dev/null +++ b/layout-editor/src/main/assets/palette/widgets.json @@ -0,0 +1,118 @@ +[ + { + "name": "View", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.ViewDesign", + "iconName": "ic_palette_view", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "30dp", + "android:padding": "8dp" + } + }, + { + "name": "ImageView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.ImageViewDesign", + "iconName": "ic_palette_image_view", + "defaultAttributes": { + "android:layout_width": "48dp", + "android:layout_height": "48dp", + "android:src": "@drawable/ic_launcher_round" + } + }, + { + "name": "WebView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.WebViewDesign", + "iconName": "ic_palette_web_view", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "200dp" + } + }, + { + "name": "VideoView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.VideoViewDesign", + "iconName": "ic_palette_video_view", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "200dp" + } + }, + { + "name": "CalendarView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.CalendarViewDesign", + "iconName": "ic_palette_calendar_view", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "Text Clock", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.TextClockDesign", + "iconName": "ic_palette_text_clock", + "defaultAttributes": { + "android:textSize": "20sp" + } + }, + { + "name": "ProgressBar", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.ProgressBarDesign", + "iconName": "ic_palette_progress_bar", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "ProgressBar (Horizontal)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.ProgressBarDesign", + "iconName": "ic_palette_progress_bar_horizontal", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "SeekBar", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SeekBarDesign", + "iconName": "ic_palette_seek_bar", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "SeekBar (Discrete)", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SeekBarDesign", + "iconName": "ic_palette_seek_bar_discrete", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "RatingBar", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.RatingBarDesign", + "iconName": "ic_palette_rating_bar" + }, + { + "name": "SearchView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SearchViewDesign", + "iconName": "ic_palette_search_view", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "TextureView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.TextureViewDesign", + "iconName": "ic_palette_texture_view", + "defaultAttributes": { + "android:layout_width": "match_parent" + } + }, + { + "name": "SurfaceView", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SurfaceViewDesign", + "iconName": "ic_palette_surface_view", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "200dp" + } + } +] \ No newline at end of file diff --git a/layout-editor/src/main/assets/strings.xml b/layout-editor/src/main/assets/strings.xml new file mode 100644 index 00000000..3dc9e613 --- /dev/null +++ b/layout-editor/src/main/assets/strings.xml @@ -0,0 +1,3 @@ + + Hello World! + \ No newline at end of file diff --git a/layout-editor/src/main/assets/tooltips.json b/layout-editor/src/main/assets/tooltips.json new file mode 100644 index 00000000..6ec133c1 --- /dev/null +++ b/layout-editor/src/main/assets/tooltips.json @@ -0,0 +1,800 @@ +[ + { + "tag": "android.content.Context", + "summary": "Interface to global information about an application environment.", + "detail": "Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.", + "uri": "a/android/content/Context.html", + "label": "View full documentation" + }, + { + "tag": "android.view.SurfaceView", + "summary": "Provides a dedicated drawing surface embedded inside of a view hierarchy.", + "detail": "Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the Surfac...", + "uri": "a/android/view/SurfaceView.html", + "label": "View full documentation" + }, + { + "tag": "android.view.TextureView", + "summary": "A TextureView can be used to display a content stream, such as that coming from a camera preview, a video, or an OpenGL scene.", + "detail": "A TextureView can be used to display a content stream, such as that coming from a camera preview, a video, or an OpenGL scene. The content stream can come from the application's process as well as a remote process. TextureView can only be used in a hardware accelerated window. When rendered in software, TextureView will draw nothing. TextureView vs. SurfaceView Capabilities TextureView SurfaceView Supports View alpha X U+ Supports rotations X Supports clipping X HDR support Limited (on Android T...", + "uri": "a/android/view/TextureView.html", + "label": "View full documentation" + }, + { + "tag": "android.view.View", + "summary": "This class represents the basic building block for user interface components.", + "detail": "This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties. Developer Guides For information about u...", + "uri": "a/android/view/View.html", + "label": "View full documentation" + }, + { + "tag": "android.webkit.WebView", + "summary": "A View that displays web pages.", + "detail": "A View that displays web pages. Basic usage In most cases, we recommend using a standard web browser, like Chrome, to deliver content to the user. To learn more about web browsers, read the guide on invoking a browser with an intent. WebView objects allow you to display web content as part of your activity layout, but lack some of the features of fully-developed browsers. A WebView is useful when you need increased control over the UI and advanced configuration options that will allow you to emb...", + "uri": "a/android/webkit/WebView.html", + "label": "View full documentation" + }, + { + "tag": "android.widget.AutoCompleteTextView", + "summary": "An editable text view that shows completion suggestions automatically while the user is typing.", + "detail": "An editable text view that shows completion suggestions automatically while the user is typing. The list of suggestions is displayed in a drop down menu from which the user can choose an item to replace the content of the edit box with. The drop down can be dismissed at any time by pressing the back key or, if no item is selected in the drop down, by pressing the enter/dpad center key. The list of suggestions is obtained from a data adapter and appears only after a given number of characters def...", + "uri": "a/android/widget/AutoCompleteTextView.html", + "label": "View full documentation" + }, + { + "tag": "android.widget.Button", + "summary": "A user interface element the user can tap or click to perform an action.", + "detail": "A user interface element the user can tap or click to perform an action. To display a button in an activity, add a button to the activity's layout XML file: