Repository: penk/terrarium-app
Branch: master
Commit: 564aec92ad5b
Files: 38
Total size: 61.3 KB
Directory structure:
gitextract_mb_qe0pv/
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── patches/
│ └── expose_loader_errorstring_invokable.patch
├── platform/
│ ├── android/
│ │ └── AndroidManifest.xml
│ ├── arch/
│ │ └── terrarium.desktop
│ ├── ios/
│ │ ├── Images.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.launchimage/
│ │ │ └── Contents.json
│ │ └── Info.plist
│ ├── mac/
│ │ ├── Info.plist
│ │ └── icon.icns
│ └── ubuntu/
│ ├── debian/
│ │ ├── changelog
│ │ ├── compat
│ │ ├── control
│ │ ├── copyright
│ │ ├── install
│ │ ├── rules
│ │ └── source/
│ │ └── format
│ ├── manifest.json
│ ├── terrarium.desktop
│ └── terrarium.json
├── qml/
│ ├── BottomBar.qml
│ ├── CustomButton.qml
│ ├── HttpServer.qml
│ ├── NaviBar.qml
│ ├── QtWebKit.qml
│ ├── QtWebView.qml
│ ├── assets.qrc
│ └── main.qml
├── src/
│ ├── documenthandler.cpp
│ ├── documenthandler.h
│ ├── main.cpp
│ ├── qmlhighlighter.cpp
│ ├── qmlhighlighter.h
│ ├── quickitemgrabber.cpp
│ └── quickitemgrabber.h
└── terrarium-app.pro
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.dll
*.dylib
# Qt-es
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.moc
moc_*.cpp
qrc_*.cpp
ui_*.h
Makefile*
*-build-*
# QtCreator
*.autosave
*.swp
*.app
*.dmg
*deployment-settings.json
# Project files
Terrarium
Terrarium.build
Terrarium.xcodeproj
.tmp
terrarium_plugin_import.cpp
terrarium_qml_plugin_import.cpp
qt.conf
================================================
FILE: .gitmodules
================================================
[submodule "qhttpserver"]
path = qhttpserver
url = https://github.com/rschroll/qhttpserver.git
ignore = dirty
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2014-2015 Chen, Ping-Hsun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Terrarium - UI Prototyping Tool for Coders
=========

Terrarium is a cross platform QML Playground: the view [renders lively](http://i.imgur.com/MCA641U.gif) as you type in the editor, makes prototyping and experimenting with [QtQuick](http://qt.digia.com/qtquick/) a lot more fun!
It monitors changes in its `TextEdit`, and triggers the view to reload source from the local http server. If you're looking for a file system watcher implementation, please refer to [QML LiveReload](https://github.com/penk/qml-livereload).
### More details on http://www.terrariumapp.com
## Download
* [iOS](https://itunes.apple.com/us/app/terrarium/id891232736?ls=1&mt=8)
* [Android](https://play.google.com/store/apps/details?id=com.terrariumapp.penk.Terrarium) or [download apk](https://github.com/penk/terrarium-app/releases/download/V1.5/TerrariumApp_1.5.apk)
* [Mac OSX](https://github.com/penk/terrarium-app/releases/download/V1.5/Terrarium-1.5.dmg)
* [Ubuntu Linux](https://github.com/penk/terrarium-app/releases/download/V1.5/terrarium_1.5_amd64.deb)
* [Ubuntu Touch](https://github.com/penk/terrarium-app/releases/download/V1.5/com.ubuntu.developer.penk.terrarium_1.5_armhf.click)
## Build Instructions
git clone https://github.com/penk/terrarium-app.git
cd terrarium-app && git submodule init && git submodule update
qmake && make
## Platform Specific Instructions
### For Arch-Linux
Just go to AUR:
`yaourt -S terrarium-git`
### For Mac OSX/iOS
To add icons to iOS build, first generate and open `Terrarium.xcodeproj`, switch AppIcon to use [Assets Catalog](https://developer.apple.com/library/ios/recipes/xcode_help-image_catalog-1.0/Recipe.html), then replace `Terrarium/Images.xcassets/` directory with `platform/ios/Images.xcassets`.
As for Mac OSX, refer to `macdeployqt` command in `terrarium-app.pro` file.
### For Ubuntu Desktop/Phone
If you're using Qt packages from apt archive instead of [qt-project.org](http://download.qt-project.org/) releases, here's the dependencies:
sudo apt-get install qt5-qmake qt5-default qtbase5-dev qtdeclarative5-dev build-essential
All `debian/` package information can be found under `platform/ubuntu/` directory, copy it to current path and build the package by:
cp -r platform/ubuntu/debian .
cp platform/ubuntu/terrarium.desktop .
dpkg-buildpackage -b
If you're building click package, execute following command on device (for native compile):
cp platform/ubuntu/* .
click build .
And install it
pkcon --allow-untrusted install-local com.ubuntu.developer.penk.terrarium_1.5_armhf.click
### For Android
First generate your keystore by `keytool`
keytool -genkey -v -keystore ../TerrariumApp.keystore -alias TerrariumApp -keyalg RSA -keysize 2048 -validity 10000
then
~/Qt5.4.1/5.4/android_armv7/bin/qmake
make
make install INSTALL_ROOT=../android-terrarium
Build and sign apk by:
~/Qt5.4.1/5.4/android_armv7/bin/androiddeployqt --input \
android-libTerrarium.so-deployment-settings.json \
--output ../android-terrarium --release --sign ../TerrariumApp.keystore TerrariumApp
## Screenshots
* [Android 5.0.0](http://i.imgur.com/0X6e6wK.png)
* [iOS 8.2](http://i.imgur.com/n2EPoha.png)
* [Mac OSX 10.10.2](http://i.imgur.com/Z0KMIaf.png)
* [Ubuntu Touch](http://i.imgur.com/KShLea0.png)
* [Ubuntu 14.10](http://i.imgur.com/TI2rLIX.png)
## LICENSE
Copyright © 2014-2015 Ping-Hsun (penk) Chen <penkia@gmail.com>
The source code is, unless otherwise specified, distributed under the terms of the MIT License.
## CREDITS
* [DocumentHandler](https://github.com/khertan/ownNotes) by Benoît HERVIER
* [QMLHighligher](https://gitorious.org/aalperts-automatons/bragi) by Alan Alpert
* [QHttpServer](https://github.com/rschroll/qhttpserver) by Robert Schroll
* [Font Awesome](http://fontawesome.io) by Dave Gandy
================================================
FILE: patches/expose_loader_errorstring_invokable.patch
================================================
--- qtdeclarative-everywhere-src-5.11.1.orig/src/quick/items/qquickloader.cpp
+++ qtdeclarative-everywhere-src-5.11.1/src/quick/items/qquickloader.cpp
@@ -629,6 +629,25 @@
}
}
+QString QQuickLoader::errorString() const
+{
+ Q_D(const QQuickLoader);
+ QString ret;
+ if(d->component->errors().isEmpty())
+ return ret;
+
+ if (d->component && d->component->isError()) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &e : errorList) {
+ ret += e.url().toString() + QLatin1Char(':') +
+ QString::number(e.line()) + QLatin1Char(' ') +
+ e.description() + QLatin1Char('\n');
+ }
+ }
+
+ return ret;
+}
+
void QQuickLoaderIncubator::setInitialState(QObject *o)
{
loader->setInitialState(o);
--- qtdeclarative-everywhere-src-5.11.1.orig/src/quick/items/qquickloader_p.h
+++ qtdeclarative-everywhere-src-5.11.1/src/quick/items/qquickloader_p.h
@@ -76,6 +76,7 @@
void setActive(bool newVal);
Q_INVOKABLE void setSource(QQmlV4Function *);
+ Q_INVOKABLE QString errorString() const;
QUrl source() const;
void setSource(const QUrl &);
================================================
FILE: platform/android/AndroidManifest.xml
================================================
<?xml version='1.0' encoding='utf-8'?>
<manifest package="com.terrariumapp.penk.Terrarium" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.5" android:versionCode="124" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name="org.qtproject.qt5.android.bindings.QtActivity"
android:label="@string/app_name"
android:screenOrientation="unspecified"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="Terrarium"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:lib/libQt5QuickParticles.so"/>
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidAccessibility.jar:jar/QtAndroid-bundled.jar:jar/QtAndroidAccessibility-bundled.jar"/>
<meta-data android:name="android.app.static_init_classes" android:value=""/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!--
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
-->
<!-- Splash screen -->
</activity>
</application>
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="14"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
</manifest>
================================================
FILE: platform/arch/terrarium.desktop
================================================
[Desktop Entry]
Version=1.5
Name=Terrarium
GenericName=Terrarium
Comment=UI Prototyping Tool for Coders
Type=Application
Icon=terrarium-app.png
Exec=Terrarium
Terminal=false
MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
X-Ubuntu-Touch=true
================================================
FILE: platform/ios/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon-1.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon@2x-1.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small-1.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40@2x-1.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-72.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-72@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: platform/ios/Images.xcassets/LaunchImage.launchimage/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"scale" : "1x",
"orientation" : "portrait"
},
{
"idiom" : "iphone",
"scale" : "2x",
"orientation" : "portrait"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "Default-568h@2x.png",
"subtype" : "retina4",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "Default-568h@2x.png",
"minimum-system-version" : "7.0",
"subtype" : "retina4",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"extent" : "full-screen",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"extent" : "full-screen",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: platform/ios/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>Terrarium</string>
<key>CFBundleGetInfoString</key>
<string>Created by Qt/QMake</string>
<key>CFBundleIcons</key>
<dict/>
<key>CFBundleIcons~ipad</key>
<dict/>
<key>CFBundleIdentifier</key>
<string>com.terrariumapp.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.5.1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
<key>UIStatusBarHidden</key>
<true/>
<key>UIStatusBarHidden~ipad</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
================================================
FILE: platform/mac/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Terrarium</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.terrariumapp.terrarium</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>1.0</string>
<key>CFBundleName</key>
<string>Terrarium</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
</dict>
</plist>
================================================
FILE: platform/ubuntu/debian/changelog
================================================
terrarium (1.5) trusty; urgency=medium
* Bump version
-- Ping-Hsun Chen (penk) <penkia@gmail.com> Mon, 16 Mar 2015 11:10:32 +0800
terrarium (0.1) trusty; urgency=medium
* Initial commit
-- Ping-Hsun Chen (penk) <penkia@gmail.com> Tue, 17 Jun 2014 10:19:26 +0000
================================================
FILE: platform/ubuntu/debian/compat
================================================
8
================================================
FILE: platform/ubuntu/debian/control
================================================
Source: terrarium
Priority: extra
Maintainer: Ping-Hsun Chen (penk) <penkia@gmail.com>
Build-Depends: debhelper (>= 8.0.0),
qt5-qmake,
qt5-default,
qtbase5-dev,
qtdeclarative5-dev
Standards-Version: 3.9.4
Section: misc
Homepage: http://www.terrariumapp.com
Package: terrarium
Section: misc
Architecture: i386 amd64 armhf
Depends: ${misc:Depends},
${shlibs:Depends},
qmlscene,
libqt5sql5-sqlite,
qtdeclarative5-qtquick2-plugin,
qtdeclarative5-window-plugin,
Description: Live QML Editor and Viewer
Terrarium is an open source QML Playgrounds
================================================
FILE: platform/ubuntu/debian/copyright
================================================
Files: *
Copyright: 2014 Ping-Hsun Chen (penk) <penkia@gmail.com>
License: MIT
License: MIT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: platform/ubuntu/debian/install
================================================
terrarium.desktop /usr/share/applications
terrarium-app.png /usr/share/terrarium/
Terrarium /usr/bin/
================================================
FILE: platform/ubuntu/debian/rules
================================================
#!/usr/bin/make -f
# Uncomment this to turn on verbose mode.
# #export DH_VERBOSE=1
%:
dh $@
================================================
FILE: platform/ubuntu/debian/source/format
================================================
3.0 (native)
================================================
FILE: platform/ubuntu/manifest.json
================================================
{
"description": "UI Prototyping Tool for Coders",
"framework": "ubuntu-sdk-14.04",
"architecture": "armhf",
"hooks": {
"terrarium": {
"apparmor": "terrarium.json",
"desktop": "terrarium.desktop"
}
},
"maintainer": "Penk Chen <penkia@gmail.com>",
"name": "com.ubuntu.developer.penk.terrarium",
"title": "Terrarium",
"version": "1.5"
}
================================================
FILE: platform/ubuntu/terrarium.desktop
================================================
[Desktop Entry]
Version=1.5
Name=Terrarium
GenericName=Terrarium
Comment=UI Prototyping Tool for Coders
Type=Application
Icon=./terrarium-app.png
Exec=./Terrarium
Terminal=false
MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
X-Ubuntu-Touch=true
================================================
FILE: platform/ubuntu/terrarium.json
================================================
{
"policy_groups": [
"networking",
"sensors",
"webview"
],
"policy_version": 1.1
}
================================================
FILE: qml/BottomBar.qml
================================================
import QtQuick 2.0
Rectangle {
anchors {
bottom: parent.bottom
left: background.left
margins: 5 * scaleRatio
}
radius: 0.5 * height
height: 50 * scaleRatio
width: height
gradient: Gradient {
GradientStop { position: 0.0; color: "#70787F" }
GradientStop { position: 1.0; color: "#383C40" }
}
CustomButton {
id: viewSwitchButton
anchors.fill: parent
//anchors { top: parent.top; left: parent.left }
anchors.margins: -20 //os_type[platform] == 'ios' ? 12 : 0
// "\uf121" : fa-code, to editor
// "\uf0db" : fa-columns, to splitted view
// "\uf144" : fa-play-circle, to viewer
icon.text:
if (splitState=='viewer' && root.width * scaleRatio > 600) { "\uf0db" }
else if (splitState=='viewer') { "\uf121" }
else if (splitState=='editor') { "\uf144" }
else if (splitState=='splitted') { "\uf121" }
else "\uf144"
defaultColor: "#CAD8E5"
onClicked: {
// splitted -> editor -> viewer
if (splitState == 'editor')
splitState = 'viewer';
else if (splitState == 'viewer' && root.width * scaleRatio > 600)
splitState = 'splitted';
else
splitState = 'editor';
view.state = splitState;
}
}
}
================================================
FILE: qml/CustomButton.qml
================================================
import QtQuick 2.0
Item {
id: button
width: 30 * scaleRatio; height: 30 * scaleRatio;
property alias icon: buttonIcon
property variant defaultColor: "#CAD8E5"
signal clicked()
anchors { margins: 10 }
Text {
id: buttonIcon
anchors.centerIn: parent
font { family: fontAwesome.name; pointSize: os_type[platform] == 'ios' ? 32 : 26 }
color: defaultColor
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: button.clicked()
}
}
}
================================================
FILE: qml/HttpServer.qml
================================================
import QtQuick 2.0
import HttpServer 1.0
HttpServer {
id: server
Component.onCompleted: listen(platformIP, 5000)
onNewRequest: {
var route = /^\/\?/;
if (request.url.toString().match(/\/update\?/)) {
editor.text = decodeURI(request.url.toString().replace(/\/update\?/, "")).replace(/%23/g, '#');
console.log(editor.text)
response.writeHead(200)
response.end()
reloadView();
}
else if ( route.test(request.url) ) {
response.writeHead(200)
response.write(editor.text)
response.end()
}
else {
response.writeHead(404)
response.end()
}
}
}
================================================
FILE: qml/NaviBar.qml
================================================
import QtQuick 2.0
import QtGraphicalEffects 1.0
Rectangle {
id: navigationBar
width: parent.width
height: 44 * scaleRatio
anchors {
bottom: parent.bottom
left: parent.left
}
color: "white"
Rectangle {
id: repeater
border.color: "#007edf"
width: 240 * scaleRatio
height: 29 * scaleRatio
anchors.centerIn: parent
radius: 5
smooth: true
visible: false
Row {
Rectangle {
width: 80 *scaleRatio ; height: 29 * scaleRatio
color: splitState == 'editor' ? "#007edf" : "transparent"
Text {
text: "Editor"
color: splitState == 'editor' ? "white" : '#007edf'
anchors.centerIn: parent
font.pointSize: 15
}
}
Rectangle {
width: 80 * scaleRatio; height: 29 * scaleRatio
border.width: 1
border.color: "#007edf"
color: splitState == 'splitted' ? "#007edf" : "transparent"
Text {
text: "Split"
color: splitState == 'splitted' ? "white" : "#007edf"
anchors.centerIn: parent
font.pointSize: 15
}
}
Rectangle {
width: 80 * scaleRatio; height: 29 * scaleRatio
color: splitState == 'viewer' ? "#007edf" : "transparent"
Text {
text: "Viewer"
color: splitState == 'viewer' ? "white" : "#007edf"
anchors.centerIn: parent
font.pointSize: 15
}
}
}
}
Rectangle {
id: mask
width: repeater.width
height: repeater.height
anchors.fill: repeater
radius: 5
}
OpacityMask {
visible: (parent.state === 'view')
anchors.fill: repeater
source: repeater
maskSource: mask
}
Row {
anchors.centerIn: parent
visible: (parent.state === 'view')
MouseArea {
width: 80 * scaleRatio
height: 29 * scaleRatio
onPressed: splitState = 'editor'
}
MouseArea {
width: 80 * scaleRatio
height: 29 * scaleRatio
onPressed: splitState = 'splitted'
}
MouseArea {
width: 80 * scaleRatio
height: 29 * scaleRatio
onPressed: splitState = 'viewer'
}
}
Text {
visible: (parent.state == 'selection')
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
margins: 10 * scaleRatio
}
font { family: fontAwesome.name; pointSize: 26 }
text: "\uf057"
color: 'grey'
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: {
navigationBar.state = 'view';
editor.deselect();
}
}
}
Row {
anchors.centerIn: parent
visible: (parent.state === 'selection')
spacing: 20 * scaleRatio
Text {
visible: (editor.selectionStart !== editor.selectionEnd)
color: "#007edf"
font.pointSize: 17
text: "Cut"
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: editor.cut()
}
}
Text {
visible: (editor.selectionStart !== editor.selectionEnd)
color: "#007edf"
font.pointSize: 17
text: "Copy"
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: {
editor.copy()
editor.deselect()
}
}
}
Text {
visible: (editor.selectionStart === editor.selectionEnd)
color: "#007edf"
font.pointSize: 17
text: "Select"
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: editor.selectWord()
}
}
Text {
visible: (editor.selectionStart === editor.selectionEnd)
color: "#007edf"
font.pointSize: 17
text: "Select All"
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: editor.selectAll()
}
}
Text {
visible: (editor.canPaste === true)
color: "#007edf"
font.pointSize: 17
text: "Paste"
MouseArea {
anchors.fill: parent
anchors.margins: -5 * scaleRatio
onPressed: editor.paste()
}
}
}
states: [
State {
name: "view"
},
State {
name: "selection"
}
]
}
================================================
FILE: qml/QtWebKit.qml
================================================
import QtWebKit 3.0
import QtWebKit.experimental 1.0
WebView {}
================================================
FILE: qml/QtWebView.qml
================================================
import QtWebView 1.0
WebView {}
================================================
FILE: qml/assets.qrc
================================================
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>main.qml</file>
<file>HttpServer.qml</file>
<file>BottomBar.qml</file>
<file>NaviBar.qml</file>
<file>CustomButton.qml</file>
<file>fontawesome-webfont.ttf</file>
<file>shadow.png</file>
<file>QtWebKit.qml</file>
<file>QtWebView.qml</file>
</qresource>
</RCC>
================================================
FILE: qml/main.qml
================================================
import QtQuick 2.0
import QtQuick.Window 2.0
import HttpServer 1.0
import QtQuick.LocalStorage 2.0
import DocumentHandler 1.0
Window {
id: root
width: Screen.width
height: Screen.height
visible: true
title: "Terrarium - UI Prototyping Tool for Coders"
property variant httpServer: {}
property variant httpd: {}
property string splitState: (root.width * scaleRatio > 600) ? 'splitted' : 'editor'
property variant os_type: { '0': 'macx', '1': 'ios', '2': 'android', '3': 'linux', '4': 'default' }
property variant platformSetting: {
'ios': { 'lineNumberSpacing': -1, 'lineNumberPadding' : 20, 'defaultFont': 'Courier New' },
'macx': { 'lineNumberSpacing': -1, 'lineNumberPadding' : 20, 'defaultFont': 'Menlo' },
'android': { 'lineNumberSpacing': 0, 'lineNumberPadding' : 20, 'defaultFont': 'Droid Sans Mono' },
'linux': { 'lineNumberSpacing': 0, 'lineNumberPadding' : 20, 'defaultFont': 'Droid Sans Mono' },
'default': { 'lineNumberSpacing': 0, 'lineNumberPadding' : 20, 'defaultFont': 'Droid Sans Mono' },
}
property variant lineNumberPadding: platformSetting[os_type[platform]]['lineNumberPadding']
property variant lineNumberSpacing: platformSetting[os_type[platform]]['lineNumberSpacing']
property variant scaleRatio: Screen.pixelDensity.toFixed(0) / 5
FontLoader { id: fontAwesome; source: "fontawesome-webfont.ttf" }
Component.onCompleted: {
// FIXME: workaround for Ubuntu Phone
if ((scaleRatio < 1) && (os_type[platform]==='linux')) scaleRatio = 2;
httpServer = Qt.createComponent("HttpServer.qml");
if (httpServer.status == Component.Ready) {
httpd = httpServer.createObject(root, {'id': 'httpd'});
timer.running = true;
} else {
console.log('error loading http server')
}
var db = getDatabase();
db.transaction(
function(tx) {
var result = tx.executeSql("SELECT * FROM previous");
for (var i=0; i < result.rows.length; i++) {
editor.text = result.rows.item(i).editor
}
tx.executeSql("DROP TABLE IF EXISTS previous");
}
);
}
Component.onDestruction: {
saveContent();
}
function saveContent() {
var db = getDatabase();
db.transaction(
function(tx) { tx.executeSql('insert into previous values (?);', editor.text); }
);
}
function getDatabase() {
var db = LocalStorage.openDatabaseSync("terrarium", "1.0", "file saving db", 100000);
db.transaction(function(tx) {tx.executeSql('CREATE TABLE IF NOT EXISTS previous (editor TEXT)'); });
return db;
}
Timer {
id: timer
interval: 500; running: false; repeat: false
onTriggered: reloadView()
}
function reloadView() {
viewLoader.setSource('http://'+platformIP+':5000/?'+Math.random()) // workaround for cache
}
NaviBar {
state: "view"
id: navibar
z: 2
}
Item {
id: view
state: root.splitState
width: root.width/2
height: root.height
anchors { top: parent.top; right: parent.right; bottom: navibar.top; }
visible: opacity > 0 ? true : false
Rectangle {
color: 'grey'
visible: errorMessage.text != ""
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: Screen.top
anchors.bottomMargin: errorMessage.text == "" ? 0 : -height
width: parent.width
height: errorMessage.height
Behavior on anchors.bottomMargin {
NumberAnimation { duration: 150; easing.type: Easing.OutQuad }
}
Text {
id: errorMessage
anchors { left: parent.left; right: parent.right; top: parent.top; margins: 20; topMargin: 10 }
font.pointSize: 20
wrapMode: Text.WordWrap
text: ""
}
}
Loader {
id: viewLoader
anchors.fill: parent
property variant errorLineNumber: 0
onStatusChanged: {
if (viewLoader.status == Loader.Error) {
errorMessage.text = viewLoader.errorString().replace(/http:\/\/.*:5000\/\?.*?:/g, "Line: ");
// restart http server when connection refused
var connectionRefused = /Connection refused/;
if (connectionRefused.test(errorMessage.text)) {
httpd.destroy();
httpd = httpServer.createObject(root, {'id': 'httpd'});
saveContent();
reloadView();
}
errorLineNumber = errorMessage.text.match(/^Line: (.*?) /)[1];
lineNumberRepeater.itemAt(errorLineNumber - 1).bgcolor = 'red'
} else {
errorMessage.text = "";
if (errorLineNumber > 0)
lineNumberRepeater.itemAt(errorLineNumber - 1).bgcolor = 'transparent'
}
}
}
states: [
State {
name: "splitted"
PropertyChanges { target: view; width: root.width/2 }
PropertyChanges { target: view; opacity: 1 }
PropertyChanges { target: background; width: root.width/2 }
PropertyChanges { target: background; opacity: 1 }
},
State {
name: "editor"
PropertyChanges { target: view; width: 0 }
PropertyChanges { target: view; opacity: 0 }
PropertyChanges { target: background; width: root.width }
PropertyChanges { target: background; opacity: 1 }
},
State {
name: "viewer"
PropertyChanges { target: view; width: root.width }
PropertyChanges { target: view; opacity: 1 }
PropertyChanges { target: background; width: 0 }
PropertyChanges { target: background; opacity: 0 }
}
]
transitions: [
Transition {
to: "*"
NumberAnimation { target: view; properties: "width"; duration: 300; easing.type: Easing.InOutQuad; }
NumberAnimation { target: background; properties: "width"; duration: 300; easing.type: Easing.InOutQuad; }
}
]
}
Rectangle {
id: background
width: root.width/2
height: root.height
anchors { top: parent.top; left: parent.left; bottom: navibar.top}
color: '#1d1f21'
visible: opacity > 0 ? true : false
Flickable {
id: flickable
anchors { fill: parent; }
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.DragOverBounds
contentWidth: parent.width
contentHeight: editor.height
clip: true
Column {
id: lineNumber
anchors { margins: 20; left: parent.left; top: parent.top }
spacing: lineNumberSpacing
Repeater {
id: lineNumberRepeater
model: editor.lineCount
Text {
property alias bgcolor: rect.color
width: 20
text: index + 1
color: 'lightgray'
font.pointSize: editor.font.pointSize
horizontalAlignment: TextEdit.AlignHCenter
Rectangle {
id: rect
color: 'transparent'
anchors.fill: parent
opacity: 0.5
}
}
}
}
Rectangle {
id: editorCurrentLineHighlight
anchors {
left: lineNumber.right
margins: lineNumberPadding
}
visible: editor.focus
width: editor.width
height: editor.cursorRectangle.height
y: editor.cursorRectangle.y + lineNumberPadding
color: '#454545'
}
TextEdit {
id: editor
anchors {
margins: lineNumberPadding
left: lineNumber.right; right: parent.right; top: parent.top
}
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere;
renderType: Text.NativeRendering
onTextChanged: timer.restart();
onSelectedTextChanged: {
if (editor.selectedText === "") {
navibar.state = 'view'
}
}
// FIXME: stupid workaround for indent
Keys.onPressed: {
if (event.key == Qt.Key_BraceRight) {
editor.select(0, cursorPosition)
var previousContent = editor.selectedText.split(/\r\n|\r|\n/)
editor.deselect()
var currentLine = previousContent[previousContent.length - 1]
var leftBrace = /{/, rightBrace = /}/;
if (!leftBrace.test(currentLine)) {
editor.remove(cursorPosition, cursorPosition - currentLine.length);
currentLine = currentLine.toString().replace(/ {1,4}$/, "");
editor.insert(cursorPosition, currentLine);
}
}
}
Keys.onReturnPressed: {
editor.select(0, cursorPosition)
var previousContent = editor.selectedText.split(/\r\n|\r|\n/)
editor.deselect()
var currentLine = previousContent[previousContent.length - 1]
var leftBrace = /{/, rightBrace = /}/;
editor.insert(cursorPosition, "\n")
var whitespaceAppend = currentLine.match(new RegExp(/^[ \t]*/)) // whitespace
if (leftBrace.test(currentLine)) // indent
whitespaceAppend += " ";
editor.insert(cursorPosition, whitespaceAppend)
}
// style from Atom dark theme:
// https://github.com/atom/atom-dark-syntax/blob/master/stylesheets/syntax-variables.less
color: '#c5c8c6'
selectionColor: '#0C75BC'
selectByMouse: true
font { pointSize: 18; family: platformSetting[os_type[platform]]['defaultFont'] }
text: documentHandler.text
inputMethodHints: Qt.ImhNoPredictiveText
DocumentHandler {
id: documentHandler
target: editor
Component.onCompleted: {
documentHandler.text = "import QtQuick 2.0\n\nRectangle { \n color: '#FEEB75'" +
"\n Text { \n anchors.centerIn: parent" +
"\n text: 'Hello, World!' \n } \n}"
}
}
// FIXME: add selection / copy / paste popup
MouseArea {
id: handler
// FIXME: disable on desktop
enabled: os_type[platform] != 'macx'
anchors.fill: parent
propagateComposedEvents: true
onPressed: {
editor.cursorPosition = parent.positionAt(mouse.x, mouse.y);
editor.focus = true
navibar.state = 'view'
Qt.inputMethod.show();
}
onPressAndHold: {
navibar.state = 'selection'
Qt.inputMethod.hide();
}
onDoubleClicked: {
editor.selectWord()
navibar.state = 'selection'
}
}
} // end of editor
}
}
Image {
fillMode: Image.TileHorizontally
source: "shadow.png"
width: navibar.width
anchors.bottom: navibar.top
height: 6
}
}
================================================
FILE: src/documenthandler.cpp
================================================
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "documenthandler.h"
#include <QtGui/QTextDocument>
#include <QtGui/QTextCursor>
#include <QtGui/QFontDatabase>
#include <QtCore/QFileInfo>
DocumentHandler::DocumentHandler()
: m_target(0)
, m_doc(0)
, m_cursorPosition(-1)
, m_selectionStart(0)
, m_selectionEnd(0)
, m_highlighter(0)
{
}
void DocumentHandler::setTarget(QQuickItem *target)
{
m_doc = 0;
m_highlighter = 0;
m_target = target;
if (!m_target)
return;
QVariant doc = m_target->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>()) {
QQuickTextDocument *qqdoc = doc.value<QQuickTextDocument*>();
if (qqdoc) {
m_doc = qqdoc->textDocument();
m_highlighter = new QMLHighlighter(m_doc);
//m_highlighter = new Highlighter(m_doc);
}
}
emit targetChanged();
}
void DocumentHandler::setText(const QString &arg)
{
if (m_text != arg) {
m_text = arg;
emit textChanged();
}
}
QString DocumentHandler::text() const
{
return m_text;
}
void DocumentHandler::setCursorPosition(int position)
{
if (position == m_cursorPosition)
return;
m_cursorPosition = position;
reset();
}
void DocumentHandler::reset()
{
}
QTextCursor DocumentHandler::textCursor() const
{
QTextCursor cursor = QTextCursor(m_doc);
if (m_selectionStart != m_selectionEnd) {
cursor.setPosition(m_selectionStart);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
} else {
cursor.setPosition(m_cursorPosition);
}
return cursor;
}
void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
}
void DocumentHandler::setSelectionStart(int position)
{
m_selectionStart = position;
}
void DocumentHandler::setSelectionEnd(int position)
{
m_selectionEnd = position;
}
================================================
FILE: src/documenthandler.h
================================================
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef DOCUMENTHANDLER_H
#define DOCUMENTHANDLER_H
#include <QQuickTextDocument>
#include "qmlhighlighter.h"
#include <QtGui/QTextCharFormat>
#include <QtCore/QTextCodec>
#include <qqmlfile.h>
QT_BEGIN_NAMESPACE
class QTextDocument;
QT_END_NAMESPACE
class DocumentHandler : public QObject
{
Q_OBJECT
Q_ENUMS(HAlignment)
Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
DocumentHandler();
QQuickItem *target() { return m_target; }
void setTarget(QQuickItem *target);
void setCursorPosition(int position);
void setSelectionStart(int position);
void setSelectionEnd(int position);
int cursorPosition() const { return m_cursorPosition; }
int selectionStart() const { return m_selectionStart; }
int selectionEnd() const { return m_selectionEnd; }
QString text() const;
public Q_SLOTS:
void setText(const QString &arg);
Q_SIGNALS:
void targetChanged();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void textChanged();
private:
void reset();
QTextCursor textCursor() const;
void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
QQuickItem *m_target;
QTextDocument *m_doc;
int m_cursorPosition;
int m_selectionStart;
int m_selectionEnd;
QMLHighlighter *m_highlighter;
QString m_text;
};
#endif
================================================
FILE: src/main.cpp
================================================
#include <QtQuick/QQuickView>
#include <QtGui/QGuiApplication>
#include <QtQml>
#if QT_VERSION > QT_VERSION_CHECK(5, 1, 0)
#include <QQmlApplicationEngine>
#endif
#include "qhttpserver/src/qhttpserver.h"
#include "qhttpserver/src/qhttprequest.h"
#include "qhttpserver/src/qhttpresponse.h"
#include "qhttpserver/src/qhttpconnection.h"
#include "documenthandler.h"
#include "quickitemgrabber.h"
#if USE_WEBENGINE
#include <qtwebengineglobal.h>
#endif
int main(int argc, char *argv[])
{
QStringList imports, plugins;
QGuiApplication app(argc, argv);
app.setApplicationName("Terrarium");
app.setOrganizationName("terrariumapp");
app.setOrganizationDomain("terrariumapp.com");
#if defined(Q_OS_MACX)
int platformId = 0;
#elif defined(Q_OS_IOS)
int platformId = 1;
#elif defined(Q_OS_ANDROID)
int platformId = 2;
#elif defined(Q_OS_LINUX)
int platformId = 3;
#else
int platformId = 4;
#endif
qmlRegisterType<QHttpServer>("HttpServer", 1, 0, "HttpServer");
qmlRegisterType<DocumentHandler>("DocumentHandler", 1, 0, "DocumentHandler");
qmlRegisterUncreatableType<QHttpRequest>("HttpServer", 1, 0, "HttpRequest", "Do not create HttpRequest directly");
qmlRegisterUncreatableType<QHttpResponse>("HttpServer", 1, 0, "HttpResponse", "Do not create HttpResponse directly");
QString platformIP;
foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) {
if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost))
platformIP = address.toString();
}
// Handle command line arguments
const QStringList arguments = QCoreApplication::arguments();
for (int i = 1, size = arguments.size(); i < size; ++i) {
const QString lowerArgument = arguments.at(i).toLower();
if (lowerArgument == QLatin1String("-i") && i + 1 < size) {
imports.append(arguments.at(++i));
} else if (lowerArgument == QLatin1String("-p") && i + 1 < size) {
plugins.append(arguments.at(++i));
}
}
#if USE_WEBENGINE
QtWebEngine::initialize();
#endif
#if QT_VERSION > QT_VERSION_CHECK(5, 1, 0)
QQmlApplicationEngine engine;
for(int i = 0; i < imports.size(); ++i) {
engine.addImportPath(imports[i]);
}
for(int i = 0; i < plugins.size(); ++i) {
engine.addPluginPath(plugins[i]);
}
engine.rootContext()->setContextProperty("platform", QVariant::fromValue(platformId));
engine.rootContext()->setContextProperty("platformIP", QVariant::fromValue(platformIP));
engine.rootContext()->setContextProperty("Grabber",new QuickItemGrabber(&app));
engine.load(QUrl("qrc:///main.qml"));
#else
QQuickView view;
view.engine()->rootContext()->setContextProperty("platform", QVariant::fromValue(platformId));
view.engine()->rootContext()->setContextProperty("platformIP", QVariant::fromValue(platformIP));
view.setSource(QUrl("qrc:///main.qml"));
view.show();
#endif
return app.exec();
}
================================================
FILE: src/qmlhighlighter.cpp
================================================
#include "qmlhighlighter.h"
#include <QColor>
void QMLHighlighter::highlightBlock(const QString &text)
{
QTextCharFormat keywordFormat;
keywordFormat.setForeground(QColor("#d7ffaf")); // Identifier
QTextCharFormat typeFormat;
typeFormat.setForeground(QColor("#afffff")); // Type
QTextCharFormat commentFormat;
commentFormat.setForeground(QColor("#8a8a8a")); // Comment
QTextCharFormat numericConstantFormat;
numericConstantFormat.setForeground(QColor("#ffffd7")); // Constant
QTextCharFormat stringConstantFormat;
stringConstantFormat.setForeground(QColor("#ffffd7"));
QRegExp type("\\b[A-Z][A-Za-z]+\\b");
QRegExp numericConstant("[0-9]+\\.?[0-9]*");
QRegExp stringConstant("['\"].*['\"]");//Not multiline strings, but they're rare
QRegExp lineComment("//[^\n]*");
QRegExp startComment("/\\*");
QRegExp endComment("\\*/");
applyBasicHighlight(text, type, typeFormat);
applyBasicHighlight(text, numericConstant, numericConstantFormat);
applyBasicHighlight(text, stringConstant, stringConstantFormat);
applyBasicHighlight(text, lineComment, commentFormat);
setCurrentBlockState(0);
int startIndex = 0;
if (previousBlockState() != 1)
startIndex = text.indexOf(startComment);
while (startIndex >= 0) {
int endIndex = text.indexOf(endComment, startIndex);
int commentLength;
if (endIndex == -1) {
setCurrentBlockState(1);
commentLength = text.length() - startIndex;
} else {
commentLength = endIndex - startIndex
+ endComment.matchedLength();
}
setFormat(startIndex, commentLength, commentFormat);
startIndex = text.indexOf(startComment,
startIndex + commentLength);
}
}
void QMLHighlighter::applyBasicHighlight(const QString &text, QRegExp &re, QTextCharFormat &format)
{
int index = text.indexOf(re);
while (index >= 0) {
int length = re.matchedLength();
setFormat(index, length, format);
index = text.indexOf(re, index + length);
}
}
================================================
FILE: src/qmlhighlighter.h
================================================
#include <QSyntaxHighlighter>
class QMLHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
//BUG: QSyntaxHighlighter(0) crashes!
QMLHighlighter(QObject* parent=new QObject()) : QSyntaxHighlighter(parent)
{
}
QMLHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent)
{
}
protected:
virtual void highlightBlock(const QString &text);
private:
void applyBasicHighlight(const QString &text, QRegExp &re, QTextCharFormat &format);
};
================================================
FILE: src/quickitemgrabber.cpp
================================================
/** Author: Ben Lau (https://github.com/benlau)
*/
#include <QtGlobal>
#include <QtCore>
#include <QQuickWindow>
#include <QOpenGLFunctions>
#include "quickitemgrabber.h"
#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
#include <private/qquickitem_p.h>
#include <private/qquickshadereffectsource_p.h>
#else
// Added since Qt 5.4
#include <QQuickItemGrabResult>
#endif
QuickItemGrabber::QuickItemGrabber(QObject *parent) :
QObject(parent)
{
m_busy = false;
m_ready = false;
}
bool QuickItemGrabber::busy() const
{
return m_busy;
}
bool QuickItemGrabber::grab(QQuickItem *target,QSize targetSize)
{
if (m_busy ||
target == 0 ||
!target->window() ||
!target->window()->isVisible() ) {
return false;
}
m_ready = false;
m_targetSize = targetSize;
m_target = target;
m_window = target->window();
if (m_targetSize.isEmpty()) {
m_targetSize = QSize(m_target->width(),m_target->height());
}
#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
QQuickItemPrivate::get(m_target)->refFromEffectItem(false);
m_target->window()->update();
connect(m_window.data(),SIGNAL(beforeSynchronizing()),
this,SLOT(ready()),Qt::DirectConnection);
connect(m_window.data(),SIGNAL(afterRendering()),
this,SLOT(capture()),Qt::DirectConnection);
#else
result = target->grabToImage(m_targetSize);
if (result.isNull()) {
qDebug() << "Can't grab target item";
return false;
}
connect(result.data(),SIGNAL(ready()),
this,SLOT(onGrabResultReady()));
#endif
setBusy(true);
return true;
}
bool QuickItemGrabber::save(QString filename)
{
if (m_image.isNull()) {
qWarning() << "QuickItemGrabber::save() - The image is null";
return false;
}
return m_image.save(filename);
}
void QuickItemGrabber::ready()
{
m_ready = true;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
void QuickItemGrabber::onGrabResultReady()
{
QImage image = result->image();
setImage(image);
result.clear();
setBusy(false);
emit grabbed();
}
#endif
QImage QuickItemGrabber::image() const
{
return m_image;
}
#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
void QuickItemGrabber::capture()
{
if (!m_ready) { // It is not ready yet
return;
}
if (m_target) { // Just in case the item is destroyed before rendering completed
QOpenGLContext* context = QOpenGLContext::currentContext();
QQuickShaderEffectTexture *m_texture = new QQuickShaderEffectTexture(m_target);
m_texture->setItem(QQuickItemPrivate::get(m_target)->itemNode());
// Set the source rectangle
QSize sourceSize;
sourceSize = QSize(m_target->width(),m_target->height());
m_texture->setRect(QRectF(0, sourceSize.height(), sourceSize.width(), -sourceSize.height()));
QSize maxSize = maxTextureSize();
/*
if (!maxSize.isValid()) {
GLint param;
QOpenGLFunctions glFuncs(context);
glFuncs.glGetIntegerv(GL_MAX_TEXTURE_SIZE,¶m);
maxSize = QSize(param,param);
setMaxTextureSize(maxSize);
}
QSize textureSize = m_targetSize;
if (maxSize.isValid() &&
(textureSize.width() > maxSize.width() ||
textureSize.height() > maxSize.height())) {
FVRectToRect scaler;
scaler.scaleToFit(textureSize,maxSize);
// The required size is larger than max texture size.
qDebug() << "Downgrade target image from" << textureSize << scaler.transformedRect().toRect().size();
textureSize = scaler.transformedRect().toRect().size();
}
*/
QSize expectedSize = textureSize();
QSGContext *sg = QSGRenderContext::from(context)->sceneGraphContext();
const QSize minSize = sg->minimumFBOSize();
m_texture->setSize(QSize(qMax<int>(minSize.width(), expectedSize.width()),
qMax<int>(minSize.height(), expectedSize.height())));
m_texture->scheduleUpdate();
m_texture->updateTexture();
QImage image = m_texture->toImage();
setImage(image);
delete m_texture;
m_texture = 0;
}
disconnect(m_window.data(), SIGNAL(afterRendering()), this, SLOT(capture()));
disconnect(m_window.data(), SIGNAL(beforeSynchronizing()), this, SLOT(ready()));
setBusy(false);
emit grabbed();
}
#endif
void QuickItemGrabber::setBusy(bool value)
{
if (m_busy != value) {
m_busy = value;
emit busyChanged();
}
}
void QuickItemGrabber::setImage(QImage value)
{
m_image = value;
emit imageChanged();
}
void QuickItemGrabber::clear()
{
setImage(QImage());
}
================================================
FILE: src/quickitemgrabber.h
================================================
/** Author: Ben Lau (https://github.com/benlau)
*/
#ifndef QUICKITEMGRABBER_H
#define QUICKITEMGRABBER_H
#include <QObject>
#include <QQuickItem>
#include <QImage>
#include <QPointer>
/// QuickItemGrabber grabs QQuickItem into QImage
class QuickItemGrabber : public QObject
{
Q_OBJECT
/// "Busy" flag. It is TRUE if the grabber is running. It won't accept another request when busy.
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
/// The grabbed image
Q_PROPERTY(QImage image READ image NOTIFY imageChanged)
public:
explicit QuickItemGrabber(QObject *parent = 0);
bool busy() const;
QImage image() const;
/// Grab the target item and save to "image" property
Q_INVOKABLE bool grab(QQuickItem* target,QSize targetSize = QSize());
/// Save the grabbed image into file. It is a blocked call.
Q_INVOKABLE bool save(QString filename);
/// Clear the captured image.
Q_INVOKABLE void clear();
signals:
void busyChanged();
void imageChanged();
void grabbed();
void maxTextureSizeChanged();
private:
// Ready for capture
Q_INVOKABLE void ready();
#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
Q_INVOKABLE void capture();
#else
Q_INVOKABLE void onGrabResultReady();
QSharedPointer<QQuickItemGrabResult> result;
#endif
void setBusy(bool value);
void setImage(QImage value);
bool m_busy;
bool m_ready;
QSize m_targetSize;
QPointer<QQuickItem> m_target;
QPointer<QQuickWindow> m_window;
QImage m_image;
};
#endif // QUICKITEMGRABBER_H
================================================
FILE: terrarium-app.pro
================================================
TEMPLATE = app
TARGET = Terrarium
QT += qml quick network sql
webengine {
QT += webengine
DEFINES += USE_WEBENGINE
}
SOURCES += src/main.cpp \
src/qmlhighlighter.cpp \
src/documenthandler.cpp \
qhttpserver/src/qhttpconnection.cpp \
qhttpserver/src/qhttprequest.cpp \
qhttpserver/src/qhttpresponse.cpp \
qhttpserver/src/qhttpserver.cpp \
qhttpserver/http-parser/http_parser.c \
src/quickitemgrabber.cpp
HEADERS += qhttpserver/src/qhttpserver.h \
qhttpserver/src//qhttpresponse.h \
qhttpserver/src//qhttprequest.h \
src/qmlhighlighter.h \
src/documenthandler.h \
qhttpserver/src//qhttpconnection.h \
src/quickitemgrabber.h
INCLUDEPATH += ./qhttpserver/http-parser/
RESOURCES += qml/assets.qrc
android {
ANDROID_PACKAGE_SOURCE_DIR = ./platform/android
}
macx {
QMAKE_MAC_SDK = macosx10.10
QMAKE_INFO_PLIST = platform/mac/Info.plist
ICON = platform/mac/icon.icns
#QMAKE_POST_LINK += macdeployqt Terrarium.app/ -qmldir=qml/ -verbose=1 -dmg
}
ios {
QMAKE_INFO_PLIST = platform/ios/Info.plist
}
gitextract_mb_qe0pv/ ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── patches/ │ └── expose_loader_errorstring_invokable.patch ├── platform/ │ ├── android/ │ │ └── AndroidManifest.xml │ ├── arch/ │ │ └── terrarium.desktop │ ├── ios/ │ │ ├── Images.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage/ │ │ │ └── Contents.json │ │ └── Info.plist │ ├── mac/ │ │ ├── Info.plist │ │ └── icon.icns │ └── ubuntu/ │ ├── debian/ │ │ ├── changelog │ │ ├── compat │ │ ├── control │ │ ├── copyright │ │ ├── install │ │ ├── rules │ │ └── source/ │ │ └── format │ ├── manifest.json │ ├── terrarium.desktop │ └── terrarium.json ├── qml/ │ ├── BottomBar.qml │ ├── CustomButton.qml │ ├── HttpServer.qml │ ├── NaviBar.qml │ ├── QtWebKit.qml │ ├── QtWebView.qml │ ├── assets.qrc │ └── main.qml ├── src/ │ ├── documenthandler.cpp │ ├── documenthandler.h │ ├── main.cpp │ ├── qmlhighlighter.cpp │ ├── qmlhighlighter.h │ ├── quickitemgrabber.cpp │ └── quickitemgrabber.h └── terrarium-app.pro
SYMBOL INDEX (7 symbols across 6 files)
FILE: src/documenthandler.cpp
function QString (line 86) | QString DocumentHandler::text() const
function QTextCursor (line 107) | QTextCursor DocumentHandler::textCursor() const
FILE: src/documenthandler.h
function QQuickItem (line 71) | QQuickItem *target() { return m_target; }
FILE: src/main.cpp
function main (line 17) | int main(int argc, char *argv[])
FILE: src/qmlhighlighter.h
function class (line 3) | class QMLHighlighter : public QSyntaxHighlighter
FILE: src/quickitemgrabber.cpp
function QImage (line 102) | QImage QuickItemGrabber::image() const
FILE: src/quickitemgrabber.h
function class (line 13) | class QuickItemGrabber : public QObject
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
{
"path": ".gitignore",
"chars": 393,
"preview": "# C++ objects and libs\n\n*.slo\n*.lo\n*.o\n*.a\n*.la\n*.lai\n*.so\n*.dll\n*.dylib\n\n# Qt-es\n\n/.qmake.cache\n/.qmake.stash\n*.pro.use"
},
{
"path": ".gitmodules",
"chars": 120,
"preview": "[submodule \"qhttpserver\"]\n\tpath = qhttpserver\n\turl = https://github.com/rschroll/qhttpserver.git\n ignore = dirty\n"
},
{
"path": "LICENSE",
"chars": 1087,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014-2015 Chen, Ping-Hsun\n\nPermission is hereby granted, free of charge, to any per"
},
{
"path": "README.md",
"chars": 3892,
"preview": "Terrarium - UI Prototyping Tool for Coders\n=========\n\n\n\nTerrarium is a cross plat"
},
{
"path": "patches/expose_loader_errorstring_invokable.patch",
"chars": 1178,
"preview": "--- qtdeclarative-everywhere-src-5.11.1.orig/src/quick/items/qquickloader.cpp\n+++ qtdeclarative-everywhere-src-5.11.1/sr"
},
{
"path": "platform/android/AndroidManifest.xml",
"chars": 4412,
"preview": "<?xml version='1.0' encoding='utf-8'?>\n<manifest package=\"com.terrariumapp.penk.Terrarium\" xmlns:android=\"http://schemas"
},
{
"path": "platform/arch/terrarium.desktop",
"chars": 290,
"preview": "[Desktop Entry]\nVersion=1.5\nName=Terrarium\nGenericName=Terrarium\nComment=UI Prototyping Tool for Coders\nType=Application"
},
{
"path": "platform/ios/Images.xcassets/AppIcon.appiconset/Contents.json",
"chars": 2113,
"preview": "{\n \"images\" : [\n {\n \"size\" : \"29x29\",\n \"idiom\" : \"iphone\",\n \"filename\" : \"Icon.png\",\n \"scale\" : "
},
{
"path": "platform/ios/Images.xcassets/LaunchImage.launchimage/Contents.json",
"chars": 1937,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"scale\" : \"1x\",\n \"orientation\" : \"portrait\"\n },\n {\n "
},
{
"path": "platform/ios/Info.plist",
"chars": 1566,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "platform/mac/Info.plist",
"chars": 761,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "platform/ubuntu/debian/changelog",
"chars": 275,
"preview": "terrarium (1.5) trusty; urgency=medium\n\n * Bump version\n\n -- Ping-Hsun Chen (penk) <penkia@gmail.com> Mon, 16 Mar 2015"
},
{
"path": "platform/ubuntu/debian/compat",
"chars": 2,
"preview": "8\n"
},
{
"path": "platform/ubuntu/debian/control",
"chars": 608,
"preview": "Source: terrarium\nPriority: extra\nMaintainer: Ping-Hsun Chen (penk) <penkia@gmail.com>\nBuild-Depends: debhelper (>= 8.0."
},
{
"path": "platform/ubuntu/debian/copyright",
"chars": 1135,
"preview": "Files: *\nCopyright: 2014 Ping-Hsun Chen (penk) <penkia@gmail.com>\nLicense: MIT\n\nLicense: MIT\n Permission is hereby grant"
},
{
"path": "platform/ubuntu/debian/install",
"chars": 102,
"preview": "terrarium.desktop /usr/share/applications\nterrarium-app.png /usr/share/terrarium/\nTerrarium /usr/bin/\n"
},
{
"path": "platform/ubuntu/debian/rules",
"chars": 96,
"preview": "#!/usr/bin/make -f\n\n# Uncomment this to turn on verbose mode.\n# #export DH_VERBOSE=1\n\n%:\n\tdh $@\n"
},
{
"path": "platform/ubuntu/debian/source/format",
"chars": 13,
"preview": "3.0 (native)\n"
},
{
"path": "platform/ubuntu/manifest.json",
"chars": 335,
"preview": "{\n\"description\": \"UI Prototyping Tool for Coders\",\n\"framework\": \"ubuntu-sdk-14.04\",\n\"architecture\": \"armhf\",\n\"hooks\": {\n"
},
{
"path": "platform/ubuntu/terrarium.desktop",
"chars": 294,
"preview": "[Desktop Entry]\nVersion=1.5\nName=Terrarium\nGenericName=Terrarium\nComment=UI Prototyping Tool for Coders\nType=Application"
},
{
"path": "platform/ubuntu/terrarium.json",
"chars": 83,
"preview": "{\n\"policy_groups\": [\n\"networking\",\n\"sensors\",\n\"webview\"\n],\n\"policy_version\": 1.1\n}\n"
},
{
"path": "qml/BottomBar.qml",
"chars": 1416,
"preview": "import QtQuick 2.0\n\nRectangle {\n anchors {\n bottom: parent.bottom\n left: background.left\n margin"
},
{
"path": "qml/CustomButton.qml",
"chars": 568,
"preview": "import QtQuick 2.0\n\nItem {\n id: button\n width: 30 * scaleRatio; height: 30 * scaleRatio; \n property alias icon:"
},
{
"path": "qml/HttpServer.qml",
"chars": 728,
"preview": "import QtQuick 2.0\nimport HttpServer 1.0\n\nHttpServer {\n id: server\n Component.onCompleted: listen(platformIP, 5000"
},
{
"path": "qml/NaviBar.qml",
"chars": 5227,
"preview": "import QtQuick 2.0\nimport QtGraphicalEffects 1.0\n\nRectangle {\n id: navigationBar\n width: parent.width\n height: "
},
{
"path": "qml/QtWebKit.qml",
"chars": 65,
"preview": "import QtWebKit 3.0\nimport QtWebKit.experimental 1.0\n\nWebView {}\n"
},
{
"path": "qml/QtWebView.qml",
"chars": 33,
"preview": "import QtWebView 1.0\n\nWebView {}\n"
},
{
"path": "qml/assets.qrc",
"chars": 360,
"preview": "<!DOCTYPE RCC><RCC version=\"1.0\">\n<qresource prefix=\"/\"> \n <file>main.qml</file>\n <file>HttpServer.qml</file>\n "
},
{
"path": "qml/main.qml",
"chars": 12787,
"preview": "import QtQuick 2.0\nimport QtQuick.Window 2.0\n\nimport HttpServer 1.0\nimport QtQuick.LocalStorage 2.0\nimport DocumentHandl"
},
{
"path": "src/documenthandler.cpp",
"chars": 4043,
"preview": "/****************************************************************************\n**\n** Copyright (C) 2013 Digia Plc and/or "
},
{
"path": "src/documenthandler.h",
"chars": 3820,
"preview": "/****************************************************************************\n**\n** Copyright (C) 2013 Digia Plc and/or "
},
{
"path": "src/main.cpp",
"chars": 3015,
"preview": "#include <QtQuick/QQuickView>\n#include <QtGui/QGuiApplication>\n#include <QtQml>\n#if QT_VERSION > QT_VERSION_CHECK(5, 1, "
},
{
"path": "src/qmlhighlighter.cpp",
"chars": 2112,
"preview": "#include \"qmlhighlighter.h\"\n#include <QColor>\n\nvoid QMLHighlighter::highlightBlock(const QString &text)\n{\n QTextCharF"
},
{
"path": "src/qmlhighlighter.h",
"chars": 490,
"preview": "#include <QSyntaxHighlighter>\n\nclass QMLHighlighter : public QSyntaxHighlighter\n{\n Q_OBJECT\npublic:\n //BUG: QS"
},
{
"path": "src/quickitemgrabber.cpp",
"chars": 4809,
"preview": "/** Author: Ben Lau (https://github.com/benlau)\n */\n#include <QtGlobal>\n#include <QtCore>\n#include <QQuickWindow>\n#incl"
},
{
"path": "src/quickitemgrabber.h",
"chars": 1566,
"preview": "/** Author: Ben Lau (https://github.com/benlau)\n */\n#ifndef QUICKITEMGRABBER_H\n#define QUICKITEMGRABBER_H\n\n#include <QO"
},
{
"path": "terrarium-app.pro",
"chars": 1083,
"preview": "TEMPLATE = app\nTARGET = Terrarium\nQT += qml quick network sql\nwebengine {\n QT += webengine\n DEFINES += USE_WEBENGI"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the penk/terrarium-app GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (61.3 KB), approximately 16.4k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.