├── .gitignore
├── LICENSE.md
├── Lit.pro
├── README.md
├── main.qrc
├── platform
└── mac
│ ├── Info.plist
│ ├── icon.icns
│ └── lit.iconset
│ ├── icon_128x128.png
│ ├── icon_128x128@2x.png
│ ├── icon_16x16.png
│ ├── icon_16x16@2x.png
│ ├── icon_256x256.png
│ ├── icon_256x256@2x.png
│ ├── icon_32x32.png
│ ├── icon_32x32@2x.png
│ ├── icon_512x512.png
│ └── icon_512x512@2x.png
├── qml
├── DisplayWindow.qml
├── EditorWindow.qml
└── main.qml
└── src
├── LitNativeHandler.cpp
├── LitNativeHandler.h
├── LitNativeHandler_mac.mm
└── main.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pro.user
2 | *.pro.user.*
3 | *.autosave
4 | build/
5 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Poren Chiang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Lit.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 | TARGET = Lit
3 | QT += qml quick
4 |
5 | # The .cpp file which was generated for your project. Feel free to hack it.
6 | SOURCES += \
7 | src/main.cpp \
8 | src/LitNativeHandler.cpp
9 |
10 | HEADERS += \
11 | src/LitNativeHandler.h
12 |
13 | RESOURCES += \
14 | main.qrc
15 |
16 | OTHER_FILES += qml/*.qml
17 |
18 | mac {
19 | SOURCES += \
20 | src/LitNativeHandler_mac.mm
21 |
22 | QMAKE_INFO_PLIST = platform/mac/Info.plist
23 | ICON = platform/mac/icon.icns
24 |
25 | CONFIG(build_release) {
26 | # qmldir must match project folder name
27 | QMAKE_POST_LINK += macdeployqt Lit.app -qmldir=../lit-app/qml/ -verbose=1 -dmg;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Lit
2 | ===
3 |
4 | Lit is a Qt-based application aimed to make live text projecting like a breeze.
5 |
6 | Targets Qt 5.4 or higher.
7 |
8 | Supported syntaxes
9 | ------------------
10 | ```
11 | *italics*
12 | **bold text**
13 | `code segments`
14 | ~~strikethrough~~
15 | ```
16 |
17 | Functionalities
18 | ---------------
19 |
20 | `Cmd+L` toggles **Code Mode**, which will render text in monospace.
21 |
22 |
23 | Contributing
24 | ------------
25 |
26 | Visit [Lit project page on GitHub](https://github.com/rschiang/lit-app).
27 |
28 |
29 | License
30 | -------
31 |
32 | See [LICENSE](LICENSE.md).
33 |
--------------------------------------------------------------------------------
/main.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qml/main.qml
4 | qml/EditorWindow.qml
5 | qml/DisplayWindow.qml
6 |
7 |
8 |
--------------------------------------------------------------------------------
/platform/mac/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleExecutable
6 | Lit
7 | CFBundleIconFile
8 | icon.icns
9 | CFBundleIdentifier
10 | tw.poren.lit
11 | CFBundleInfoDictionaryVersion
12 | 1.0
13 | CFBundleName
14 | Lit
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 |
20 |
21 |
--------------------------------------------------------------------------------
/platform/mac/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/icon.icns
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_512x512.png
--------------------------------------------------------------------------------
/platform/mac/lit.iconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rschiang/lit-app/a1e0413ecd1e6c91db67265b7d0eb1d0bdf45a0a/platform/mac/lit.iconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/qml/DisplayWindow.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 6.0
2 |
3 | Window {
4 | id: window
5 | title: "Lit"
6 | color: app.theme === "dark" ? "#1e1e1e" : "#fff"
7 |
8 | Text {
9 | id: label
10 |
11 | anchors {
12 | left: parent.left
13 | right: parent.right
14 | verticalCenter: parent.verticalCenter
15 | margins: 8
16 | }
17 | horizontalAlignment: app.mode === "code" ? TextEdit.AlignLeft : TextEdit.AlignHCenter
18 |
19 | lineHeight: 0.85
20 | wrapMode: Text.NoWrap
21 | textFormat: app.mode === "code" ? Text.PlainText : Text.RichText
22 |
23 | color: app.theme === "dark" ? "#f8f8f2" : "#222"
24 |
25 |
26 | font.family: app.mode === "code" ? "Source Code Pro, Menlo, Source Han Sans TC" :
27 | "Source Sans Pro, Proxima Nova, Source Han Sans TC"
28 | font.weight: Font.DemiBold
29 | renderType: TextEdit.NativeRendering
30 | smooth: true
31 |
32 | text: app.mode === "code" ? app.text : format(app.text)
33 |
34 | Behavior on font.pixelSize {
35 | SmoothedAnimation {
36 | duration: 50
37 | easing.type: Easing.InExpo
38 | }
39 | }
40 |
41 | function format(text) {
42 | let pageBreakIndex = text.lastIndexOf("\n\n")
43 | let pageText = pageBreakIndex >= 0 ? text.substr(pageBreakIndex+2) : text
44 | return pageText
45 | .replace(/\n+$/g, '\n')
46 | .replace(/&/g, "&")
47 | .replace(//g, ">")
49 | .replace(/`(.+)`/g, "$1
")
50 | .replace(/\*\*([^\n\*]+)\*\*/g, "$1")
51 | .replace(/\*([^\s\n\*]+)\*/g, "*$1*")
52 | .replace(/~~(.+)~~/g, "$1")
53 | .replace(/\n/g, "
")
54 | }
55 | }
56 |
57 | Text {
58 | id: measurer
59 | opacity: 0
60 | renderType: Text.NativeRendering
61 | antialiasing: false
62 |
63 | width: label.width
64 | horizontalAlignment: label.horizontalAlignment
65 | lineHeight: label.lineHeight
66 | wrapMode: label.wrapMode
67 | textFormat: label.textFormat
68 | font.family: label.font.family
69 | font.weight: label.font.weight
70 | text: label.text
71 | }
72 |
73 | Timer {
74 | id: timer
75 | interval: 50
76 | repeat: false
77 | running: false
78 | triggeredOnStart: false
79 |
80 | onTriggered: {
81 | var baseSize = Math.min(window.width, window.height)
82 | var ratio = 1.125
83 |
84 | do {
85 | measurer.font.pixelSize = baseSize
86 | baseSize = Math.floor(baseSize / ratio)
87 | } while ((measurer.paintedWidth >= window.width ||
88 | measurer.paintedHeight >= window.height) && baseSize >= 12)
89 |
90 | let size = measurer.font.pixelSize
91 | label.font.pixelSize = size
92 | label.font.letterSpacing = size * -0.025
93 | }
94 | }
95 |
96 | Connections {
97 | target: app
98 |
99 | function onTextChanged() { timer.start() }
100 | function onModeChanged() { timer.start() }
101 | }
102 |
103 | onWidthChanged: timer.start()
104 | onHeightChanged: timer.start()
105 | onScreenChanged: Native.fillScreen(window, window.screen)
106 | onWindowStateChanged: Native.makeWindowTitleBarTransparent(window)
107 | onVisibilityChanged: Native.makeWindowTitleBarTransparent(window)
108 | Component.onCompleted: {
109 | window.flags |= Qt.WindowDoesNotAcceptFocus
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/qml/EditorWindow.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 6.0
2 | import QtQuick.Controls 6.0
3 |
4 | Window {
5 | id: window
6 | title: "Lit"
7 |
8 | width: 540
9 | height: 320
10 | color: app.theme === "dark" ? "#1e1e1e" : "#fff"
11 |
12 | Component.onCompleted: {
13 | window.flags |= Qt.WindowStaysOnTopHint
14 | }
15 |
16 | onClosing: Qt.quit()
17 |
18 | ScrollView {
19 | id: scrollView
20 | anchors.fill: parent
21 |
22 | TextArea {
23 | id: editor
24 |
25 | wrapMode: Text.Wrap
26 | textFormat: TextEdit.PlainText
27 |
28 | font.pixelSize: 24
29 | font.family: app.mode === "code" ? "Source Code Pro, Menlo, Source Han Sans TC" :
30 | "Source Sans Pro, Proxima Nova, Source Han Sans TC"
31 | font.weight: Font.Medium
32 | renderType: TextEdit.NativeRendering
33 | smooth: true
34 |
35 | focus: true
36 | selectByMouse: true
37 |
38 | onTextChanged: app.text = text
39 |
40 | Keys.onPressed: (event) => {
41 | if (event.modifiers & Qt.ControlModifier)
42 | switch (event.key) {
43 | case Qt.Key_D:
44 | app.theme = (app.theme === "dark") ? "light" : "dark"
45 | break
46 | case Qt.Key_L:
47 | app.mode = (app.mode === "code") ? "" : "code"
48 | break
49 | }
50 | }
51 | }
52 | }
53 |
54 | Label {
55 | id: hint
56 | anchors {
57 | left: parent.left
58 | bottom: parent.bottom
59 | margins: 8
60 | }
61 | visible: (editor.text.length === 0)
62 |
63 | opacity: 0.35
64 | renderType: Text.NativeRendering
65 | textFormat: TextEdit.PlainText
66 |
67 | text: "Type to project text. Move to next screen with two line breaks.\n" +
68 | "⌘-D: Dark Mode ⌘-L: Plain Text Mode (turn off markdown formatting)"
69 |
70 | onVisibleChanged: {
71 | if (editor.text.length > 0)
72 | hint.visible = false // Disconnecting the property, Make hint display only once.
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/qml/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 6.0
2 |
3 | QtObject {
4 | id: app
5 |
6 | // Properties
7 | property string text
8 | property string theme
9 | property string mode
10 |
11 | // Function
12 | function spawnDisplayWindow(screen) {
13 | let window = windowPrototype.createObject(app)
14 | window.screen = screen
15 | Native.fillScreen(window, screen)
16 |
17 | window.show()
18 | window.raise()
19 | return window
20 | }
21 |
22 | // Events
23 | Component.onCompleted: {
24 | const screens = Native.getScreens()
25 | const primaryScreen = Native.getPrimaryScreen()
26 |
27 | if (screens.length < 2)
28 | spawnDisplayWindow(screens[0])
29 | else
30 | for (let screen of screens)
31 | if (screen !== primaryScreen)
32 | spawnDisplayWindow(screen)
33 |
34 | let editor = editorPrototype.createObject(app)
35 | editor.screen = primaryScreen
36 | Native.alignAtScreen(editor, primaryScreen)
37 | editor.show()
38 | }
39 |
40 | // Resources
41 | property list resources: [
42 | Component {
43 | id: windowPrototype
44 | DisplayWindow {}
45 | },
46 | Component {
47 | id: editorPrototype
48 | EditorWindow {}
49 | }
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/src/LitNativeHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "LitNativeHandler.h"
2 |
3 | LitNativeHandler::LitNativeHandler(QGuiApplication* parent)
4 | : QObject(parent)
5 | {
6 | this->app = parent;
7 | }
8 |
9 | QVariantList LitNativeHandler::getScreens() {
10 | QVariantList screens;
11 | foreach (QScreen* screen, QGuiApplication::screens()) {
12 | screens << QVariant::fromValue(screen);
13 | QQmlEngine::setObjectOwnership(screen, QQmlEngine::CppOwnership);
14 | }
15 | return screens;
16 | }
17 |
18 | QScreen* LitNativeHandler::getPrimaryScreen() {
19 | return QGuiApplication::primaryScreen();
20 | }
21 |
22 | void LitNativeHandler::fillScreen(QWindow *window, QScreen *screen) {
23 | window->setGeometry(screen->availableVirtualGeometry());
24 | }
25 |
26 | void LitNativeHandler::alignAtScreen(QWindow *window, QScreen *screen) {
27 | const QPoint screenCorner = screen->availableVirtualGeometry().bottomRight();
28 | const QSize windowSize = window->size();
29 | window->setPosition(screenCorner.x() - windowSize.width(),
30 | screenCorner.y() - windowSize.height());
31 | }
32 |
--------------------------------------------------------------------------------
/src/LitNativeHandler.h:
--------------------------------------------------------------------------------
1 | #ifndef LITNATIVEHANDLER_H
2 | #define LITNATIVEHANDLER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | class LitNativeHandler : public QObject
11 | {
12 | Q_OBJECT
13 | public:
14 | explicit LitNativeHandler(QGuiApplication* parent);
15 | Q_INVOKABLE QVariantList getScreens();
16 | Q_INVOKABLE QScreen* getPrimaryScreen();
17 | Q_INVOKABLE void fillScreen(QWindow* window, QScreen* screen);
18 | Q_INVOKABLE void alignAtScreen(QWindow* window, QScreen* screen);
19 | Q_INVOKABLE void makeWindowTitleBarTransparent(QWindow* window);
20 |
21 | private:
22 | QGuiApplication* app;
23 | };
24 |
25 | #endif // LITNATIVEHANDLER_H
26 |
--------------------------------------------------------------------------------
/src/LitNativeHandler_mac.mm:
--------------------------------------------------------------------------------
1 | #include
2 | #include "LitNativeHandler.h"
3 |
4 | void LitNativeHandler::makeWindowTitleBarTransparent(QWindow* qWindow) {
5 | NSView *view = (NSView *) reinterpret_cast(qWindow->winId());
6 | NSWindow *window = view.window;
7 | window.styleMask |= NSWindowStyleMaskFullSizeContentView;
8 | window.titlebarAppearsTransparent = YES;
9 | }
10 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "LitNativeHandler.h"
5 |
6 | int main(int argc, char *argv[])
7 | {
8 | QGuiApplication app(argc, argv);
9 | app.setApplicationName("Lit");
10 | app.setOrganizationName("Poren Chiang");
11 | app.setOrganizationDomain("lit.poren.tw");
12 |
13 | LitNativeHandler native(&app);
14 |
15 | QQmlApplicationEngine engine;
16 | engine.rootContext()->setContextProperty("Native", &native);
17 | engine.load(QUrl("qrc:/qml/main.qml"));
18 |
19 | return app.exec();
20 | }
21 |
--------------------------------------------------------------------------------