├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── com.github.brain-child.moobo.yml ├── data ├── com.github.brain_child.moobo.appdata.xml.in ├── com.github.brain_child.moobo.desktop.in ├── com.github.brain_child.moobo.gresource.xml ├── com.github.brain_child.moobo.gschema.xml ├── icons │ ├── 16 │ │ └── com.github.brain_child.moobo.svg │ ├── 24 │ │ └── com.github.brain_child.moobo.svg │ ├── 32 │ │ └── com.github.brain_child.moobo.svg │ ├── 48 │ │ └── com.github.brain_child.moobo.svg │ ├── 64 │ │ └── com.github.brain_child.moobo.svg │ ├── 128 │ │ └── com.github.brain_child.moobo.svg │ └── symbolic.svg ├── meson.build └── styles │ ├── ColorButton.css │ ├── SourceRow.css │ └── Style.css ├── meson.build ├── meson └── post_install.py ├── po ├── LINGUAS ├── POTFILES ├── com.github.brain_child.moobo.pot ├── de.po ├── extra │ ├── LINGUAS │ ├── POTFILES │ ├── de.po │ ├── extra.pot │ ├── fr.po │ ├── meson.build │ └── pt.po ├── fr.po ├── meson.build └── pt.po ├── preview ├── moobo_dark.png └── moobo_light.png └── src ├── Application.vala ├── Controllers ├── BoardController.vala ├── FloatingButtonController.vala ├── ImageController.vala ├── LabelController.vala ├── RowController.vala └── TextController.vala ├── Helper ├── Colors.vala ├── Constants.vala ├── DemoBoard.vala ├── Deserializer.vala └── Serializer.vala ├── Models ├── BaseModel.vala ├── BoardModel.vala ├── ImageModel.vala ├── LabelModel.vala └── TextModel.vala ├── Movable.vala ├── Widgets ├── Board.vala ├── ColorButton.vala ├── FloatingButton.vala ├── ImageWidget.vala ├── LabelWidget.vala ├── MenuItemColor.vala ├── Row.vala └── TextWidget.vala ├── Window.vala └── meson.build /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig 2 | root = true 3 | 4 | # elementary defaults 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = tab 9 | indent_style = space 10 | insert_final_newline = true 11 | max_line_length = 80 12 | tab_width = 4 13 | 14 | # Markup files 15 | [{*.html,*.xml,*.xml.in,*.yml}] 16 | tab_width = 2 17 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # This workflow will run for any pull request or pushed commit 4 | on: [push, pull_request] 5 | 6 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 7 | jobs: 8 | # This workflow contains a single job called "flatpak" 9 | flatpak: 10 | # The type of runner that the job will run on 11 | runs-on: ubuntu-latest 12 | 13 | # This job runs in a special container designed for building Flatpaks for AppCenter 14 | container: 15 | image: ghcr.io/elementary/flatpak-platform/runtime:6 16 | options: --privileged 17 | 18 | # Steps represent a sequence of tasks that will be executed as part of the job 19 | steps: 20 | # Checks-out your repository under $GITHUB_WORKSPACE, so the job can access it 21 | - uses: actions/checkout@v2 22 | 23 | # Builds your flatpak manifest using the Flatpak Builder action 24 | - uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v3 25 | with: 26 | # This is the name of the Bundle file we're building and can be anything you like 27 | bundle: Moobo.flatpak 28 | # This uses your app's RDNN ID 29 | manifest-path: com.github.brain-child.moobo.yml 30 | 31 | # You can automatically run any of the tests you've created as part of this workflow 32 | run-tests: true 33 | 34 | # These lines specify the location of the elementary Runtime and Sdk 35 | repository-name: appcenter 36 | repository-url: https://flatpak.elementary.io/repo.flatpakrepo 37 | cache-key: "flatpak-builder-${{ github.sha }}" 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | _build/ 3 | builddir/ 4 | .flatpak-builder/ 5 | *~ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moobo 2 | 3 | ## Visualize your ideas on moodboards 4 | 5 | Use different widgets to visualize your thoughts. 6 | 7 | ⚠ NOTE: Moobo was developed as part of my bachelors degree. At this point, consider Moobo as proof of concept. 8 | 9 | [![Get it on AppCenter](https://appcenter.elementary.io/badge.svg)](https://appcenter.elementary.io/com.github.brain_child.moobo) 10 | 11 | ![moobo light](/preview/moobo_light.png) 12 | 13 | ![moobo dark](/preview/moobo_dark.png) 14 | 15 | ### Credits 16 | 17 | Moobo is developed after a concept by [Heru Setiawan](https://hrstwn.github.io/). Heru Setiawan is an illustrator who came up with the idea of the app. The original mockup can be found on [reddit](https://www.reddit.com/r/elementaryos/comments/kg5uiw/moobo_is_a_moodboarding_and_notetaking_app/). 18 | -------------------------------------------------------------------------------- /com.github.brain-child.moobo.yml: -------------------------------------------------------------------------------- 1 | app-id: com.github.brain_child.moobo 2 | 3 | runtime: io.elementary.Platform 4 | runtime-version: '6.1' 5 | sdk: io.elementary.Sdk 6 | 7 | command: com.github.brain_child.moobo 8 | 9 | finish-args: 10 | - '--share=ipc' 11 | - '--socket=fallback-x11' 12 | - '--socket=wayland' 13 | 14 | modules: 15 | - name: Moobo 16 | buildsystem: meson 17 | sources: 18 | - type: dir 19 | path: . 20 | -------------------------------------------------------------------------------- /data/com.github.brain_child.moobo.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.github.brain_child.moobo 5 | CC0 6 | Moobo 7 | Visualize your ideas on moodboards 8 | 9 | 10 |

11 | Use different widgets to visualize your thoughts. 12 |

13 |

14 | ⚠ NOTE: The app is not finished. At this point, consider Moobo as proof of concept. 15 |

16 |
17 | 18 | 19 | 20 | 21 | 22 | brain·​child 23 | http://github.com/brain-child/moobo 24 | http://github.com/brain-child/moobo/issues 25 | http://github.com/brain-child/moobo/issues 26 | 27 | 28 | 29 | https://raw.githubusercontent.com/brain-child/moobo/main/preview/moobo_light.png 30 | 31 | 32 | https://raw.githubusercontent.com/brain-child/moobo/main/preview/moobo_dark.png 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
    42 |
  • Add shortcuts for QWERTY and AZERTY keyboard layout
  • 43 |
44 |
45 |
46 | 47 | 48 |
    49 |
  • Window size is calculated dynamically
  • 50 |
  • Show shortcuts by pressing ESC
  • 51 |
  • Rename board by pressing F2
  • 52 |
  • Show placeholder when adding an image
  • 53 |
  • Add French translation
  • 54 |
  • Add Portuguese translation
  • 55 |
  • Visual improvements
  • 56 |
57 |
58 |
59 | 60 | 61 |
    62 |
  • Fix productpage in AppCenter
  • 63 |
64 |
65 |
66 | 67 | 68 |
    69 |
  • First release!
  • 70 |
71 |
72 |
73 |
74 | 75 | 76 | #3689E6 77 | #FFFFFF 78 | 1 79 | pk_live_51Jn7RuIIljArhmepVPJdZfHeDKFox4HDQN60QFkowrkHtfNuxuM74dHrXeYU75IoYsNUGXmkZVvVz9YywwFEtQ8700QubEbk62 80 | 81 | 82 |
83 | 84 | -------------------------------------------------------------------------------- /data/com.github.brain_child.moobo.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Moobo 3 | GenericName=Moodboard Application 4 | Comment=Visualize your ideas 5 | Categories=GTK;Office;Utility; 6 | Exec=com.github.brain_child.moobo 7 | Icon=com.github.brain_child.moobo 8 | Terminal=false 9 | Type=Application 10 | Keywords=notes;moobo;moodboard;idea; 11 | -------------------------------------------------------------------------------- /data/com.github.brain_child.moobo.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | styles/Style.css 5 | styles/SourceRow.css 6 | styles/ColorButton.css 7 | 8 | 9 | -------------------------------------------------------------------------------- /data/com.github.brain_child.moobo.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | first time running flag 7 | flag to determine if app is running for the first time 8 | 9 | 10 | 100 11 | x-position 12 | window position on x axis 13 | 14 | 15 | 100 16 | y-position 17 | window position on y axis 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /data/icons/128/com.github.brain_child.moobo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 43 | 50 | 61 | 62 | 64 | 65 | 67 | image/svg+xml 68 | 70 | 71 | 72 | 73 | 75 | 77 | 81 | 85 | 86 | 88 | 92 | 96 | 100 | 104 | 105 | 114 | 116 | 120 | 124 | 125 | 127 | 131 | 135 | 139 | 140 | 148 | 150 | 154 | 158 | 159 | 169 | 171 | 175 | 179 | 180 | 190 | 200 | 210 | 212 | 216 | 220 | 221 | 230 | 232 | 236 | 240 | 244 | 248 | 249 | 250 | 252 | 257 | 261 | 268 | 276 | 283 | 284 | 285 | 294 | 303 | 312 | 318 | 325 | 332 | 339 | 346 | 353 | 360 | 367 | 374 | 381 | 388 | 395 | 402 | 409 | 416 | 423 | 430 | 437 | 444 | 451 | 458 | 465 | 472 | 479 | 486 | 493 | 500 | 507 | 514 | 521 | 528 | 535 | 542 | 549 | 556 | 563 | 570 | 577 | 584 | 591 | 598 | 605 | 612 | 619 | 626 | 633 | 640 | 647 | 654 | 661 | 662 | 663 | -------------------------------------------------------------------------------- /data/icons/16/com.github.brain_child.moobo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 39 | 43 | 50 | 51 | 53 | 55 | 59 | 63 | 64 | 73 | 75 | 79 | 83 | 87 | 91 | 92 | 94 | 98 | 102 | 103 | 113 | 122 | 124 | 128 | 132 | 136 | 140 | 141 | 142 | 144 | 145 | 147 | image/svg+xml 148 | 150 | 151 | 152 | 153 | 162 | 169 | 176 | 183 | 192 | 201 | 208 | 215 | 222 | 223 | -------------------------------------------------------------------------------- /data/icons/24/com.github.brain_child.moobo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 39 | 43 | 50 | 51 | 53 | 55 | 59 | 63 | 64 | 66 | 70 | 74 | 75 | 83 | 91 | 101 | 103 | 107 | 111 | 115 | 119 | 120 | 129 | 139 | 141 | 145 | 149 | 150 | 159 | 161 | 165 | 169 | 173 | 177 | 178 | 179 | 181 | 182 | 184 | image/svg+xml 185 | 187 | 188 | 189 | 190 | 193 | 200 | 208 | 215 | 216 | 225 | 232 | 239 | 246 | 253 | 260 | 267 | 274 | 281 | 288 | 295 | 302 | 309 | 316 | 323 | 330 | 339 | 348 | 349 | -------------------------------------------------------------------------------- /data/icons/32/com.github.brain_child.moobo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 40 | 44 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 65 | 67 | 71 | 75 | 76 | 78 | 82 | 86 | 87 | 89 | 93 | 97 | 98 | 100 | 104 | 108 | 112 | 113 | 122 | 132 | 142 | 144 | 148 | 152 | 156 | 160 | 161 | 170 | 180 | 182 | 186 | 190 | 194 | 198 | 199 | 208 | 210 | 214 | 218 | 222 | 226 | 227 | 236 | 237 | 240 | 247 | 252 | 257 | 258 | 267 | 276 | 285 | 292 | 299 | 306 | 313 | 320 | 327 | 334 | 341 | 348 | 355 | 362 | 369 | 376 | 383 | 390 | 397 | 398 | -------------------------------------------------------------------------------- /data/icons/48/com.github.brain_child.moobo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 39 | 43 | 50 | 51 | 53 | 55 | 59 | 63 | 64 | 66 | 70 | 74 | 78 | 82 | 83 | 85 | 89 | 93 | 97 | 101 | 102 | 111 | 113 | 117 | 121 | 125 | 126 | 136 | 138 | 142 | 146 | 147 | 157 | 166 | 176 | 185 | 187 | 191 | 195 | 199 | 203 | 204 | 205 | 207 | 208 | 210 | image/svg+xml 211 | 213 | 214 | 215 | 216 | 219 | 224 | 229 | 236 | 237 | 246 | 255 | 264 | 271 | 278 | 285 | 292 | 299 | 306 | 313 | 320 | 327 | 334 | 341 | 348 | 355 | 362 | 369 | 376 | 383 | 390 | 397 | 404 | 411 | 418 | 425 | 432 | 439 | 440 | -------------------------------------------------------------------------------- /data/icons/64/com.github.brain_child.moobo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 43 | 47 | 54 | 55 | 57 | 58 | 60 | image/svg+xml 61 | 63 | 64 | 65 | 66 | 68 | 70 | 74 | 78 | 79 | 81 | 85 | 89 | 93 | 97 | 98 | 100 | 104 | 108 | 109 | 111 | 115 | 119 | 123 | 124 | 134 | 144 | 153 | 162 | 172 | 174 | 178 | 182 | 186 | 190 | 191 | 200 | 202 | 206 | 210 | 214 | 218 | 219 | 220 | 223 | 230 | 235 | 240 | 241 | 250 | 259 | 268 | 275 | 282 | 289 | 296 | 303 | 310 | 317 | 324 | 331 | 338 | 345 | 352 | 359 | 366 | 373 | 380 | 387 | 394 | 401 | 408 | 415 | 422 | 429 | 436 | 443 | 450 | 457 | 464 | 471 | 478 | 479 | -------------------------------------------------------------------------------- /data/icons/symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 51 | 55 | 66 | 67 | 77 | 87 | 88 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | # Install icons 2 | icon_sizes = ['16', '24', '32', '48', '64', '128'] 3 | 4 | foreach i : icon_sizes 5 | install_data ( 6 | join_paths ('icons', i, meson.project_name () + '.svg'), 7 | install_dir: join_paths (get_option ('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps') 8 | ) 9 | install_data ( 10 | join_paths ('icons', i, meson.project_name() + '.svg'), 11 | install_dir: join_paths (get_option ('datadir'), 'icons', 'hicolor', i + 'x' + i + '@2', 'apps') 12 | ) 13 | endforeach 14 | 15 | # Translate and install our .desktop file so the Applications Menu will see it 16 | i18n.merge_file ( 17 | input: meson.project_name () + '.desktop.in', 18 | output: meson.project_name () + '.desktop', 19 | po_dir: join_paths (meson.source_root (), 'po', 'extra'), 20 | type: 'desktop', 21 | install: true, 22 | install_dir: join_paths (get_option ('datadir'), 'applications') 23 | ) 24 | 25 | # Translate and install our .appdata.xml file so AppCenter will see it 26 | i18n.merge_file ( 27 | input: meson.project_name () + '.appdata.xml.in', 28 | output: meson.project_name () + '.appdata.xml', 29 | po_dir: join_paths (meson.source_root (), 'po', 'extra'), 30 | install: true, 31 | install_dir: join_paths (get_option ('datadir'), 'metainfo') 32 | ) 33 | 34 | install_data ( 35 | meson.project_name () + '.gschema.xml', 36 | install_dir: join_paths (get_option ('datadir'), 'glib-2.0', 'schemas') 37 | ) 38 | -------------------------------------------------------------------------------- /data/styles/ColorButton.css: -------------------------------------------------------------------------------- 1 | .color-button.none check { 2 | background: @base_color; 3 | color: mix (@text_color, @base_color, 0.5); 4 | min-height: 14px; 5 | min-width: 14px; 6 | -gtk-icon-source: -gtk-icontheme("close-symbolic"); 7 | } 8 | -------------------------------------------------------------------------------- /data/styles/SourceRow.css: -------------------------------------------------------------------------------- 1 | .source-color { 2 | background: @colorAccent; 3 | border: 1px solid @borders; 4 | border-radius: 50%; 5 | box-shadow: 6 | inset 0 1px 0 0 alpha (@inset_dark_color, 0.7), 7 | inset 0 0 0 1px alpha (@inset_dark_color, 0.3), 8 | 0 1px 0 0 alpha (@bg_highlight_color, 0.3); 9 | min-height: 14px; 10 | min-width: 14px; 11 | } 12 | -------------------------------------------------------------------------------- /data/styles/Style.css: -------------------------------------------------------------------------------- 1 | .fab { 2 | background: #3689e6; 3 | border-radius: 50%; 4 | } 5 | 6 | .white_label { 7 | color: #fafafa; 8 | } 9 | 10 | .black_label { 11 | color: black; 12 | } 13 | 14 | .note { 15 | background: #fff394; 16 | } 17 | 18 | .rounded_corners { 19 | border-radius: 5px; 20 | } 21 | 22 | .transparent { 23 | background: transparent; 24 | } 25 | 26 | .editable-label { 27 | background: transparent; 28 | border: none; 29 | box-shadow: none; 30 | color: inherit; 31 | padding: 0; 32 | } 33 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # Project name, programming language and version 2 | project ( 3 | 'com.github.brain_child.moobo', 4 | 'c', 'vala', 5 | version: '0.1.3' 6 | ) 7 | 8 | # GNOME module 9 | gnome = import ('gnome') 10 | 11 | # Translation module 12 | i18n = import ('i18n') 13 | 14 | # Project arguments 15 | add_project_arguments ( 16 | '-DGETTEXT_PACKAGE="@0@"'.format (meson.project_name ()), 17 | language: 'c' 18 | ) 19 | 20 | # Compiling resources 21 | asresources = gnome.compile_resources ( 22 | 'as-resources', 23 | join_paths ('data', meson.project_name ()) + '.gresource.xml', 24 | source_dir: 'data', 25 | c_name: 'as' 26 | ) 27 | 28 | # Listing dependencies 29 | dependencies = [ 30 | dependency ('gtk+-3.0'), 31 | dependency ('granite'), 32 | dependency ('libhandy-1'), 33 | dependency ('json-glib-1.0') 34 | ] 35 | 36 | 37 | subdir ('src') 38 | 39 | # Define executable 40 | executable( 41 | meson.project_name (), 42 | sources, 43 | asresources, 44 | dependencies: dependencies, 45 | install: true 46 | ) 47 | 48 | subdir ('data') 49 | subdir ('po') 50 | 51 | meson.add_install_script ('meson/post_install.py') 52 | -------------------------------------------------------------------------------- /meson/post_install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path, environ 4 | import subprocess 5 | 6 | prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local') 7 | schemadir = path.join(environ['MESON_INSTALL_PREFIX'], 'share', 'glib-2.0', 'schemas') 8 | datadir = path.join(prefix, 'share') 9 | desktop_database_dir = path.join(datadir, 'applications') 10 | 11 | if not environ.get('DESTDIR'): 12 | print('Compiling gsettings schemas…') 13 | subprocess.call(['glib-compile-schemas', schemadir]) 14 | print('Updating desktop database…') 15 | subprocess.call(['update-desktop-database', '-q', desktop_database_dir]) 16 | print('Updating icon cache…') 17 | subprocess.call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')]) 18 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | de 2 | fr 3 | pt 4 | -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | src/Application.vala 2 | src/Window.vala 3 | src/Movable.vala 4 | 5 | src/Controllers/RowController.vala 6 | src/Controllers/ImageController.vala 7 | 8 | #src/Helper/DemoBoard.vala 9 | 10 | src/Widgets/FloatingButton.vala 11 | src/Widgets/Row.vala 12 | -------------------------------------------------------------------------------- /po/com.github.brain_child.moobo.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.github.brain_child.moobo package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: com.github.brain_child.moobo\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: src/Application.vala:104 21 | msgid "Shortcuts" 22 | msgstr "" 23 | 24 | #: src/Application.vala:123 25 | msgid "Application" 26 | msgstr "" 27 | 28 | #: src/Application.vala:124 29 | msgid "Show shortcuts:" 30 | msgstr "" 31 | 32 | #: src/Application.vala:126 33 | msgid "Save and Quit:" 34 | msgstr "" 35 | 36 | #: src/Application.vala:129 src/Window.vala:71 37 | msgid "Boards" 38 | msgstr "" 39 | 40 | #: src/Application.vala:130 41 | msgid "Rename:" 42 | msgstr "" 43 | 44 | #: src/Application.vala:132 45 | msgid "Widgets" 46 | msgstr "" 47 | 48 | #: src/Application.vala:133 49 | msgid "Increase font size:" 50 | msgstr "" 51 | 52 | #: src/Application.vala:135 53 | msgid "Decrease font size:" 54 | msgstr "" 55 | 56 | #: src/Window.vala:56 57 | msgid "New Board" 58 | msgstr "" 59 | 60 | #: src/Movable.vala:67 61 | msgid "Change Color" 62 | msgstr "" 63 | 64 | #: src/Movable.vala:86 65 | msgid "Change Font" 66 | msgstr "" 67 | 68 | #: src/Movable.vala:100 69 | msgid "Scale" 70 | msgstr "" 71 | 72 | #: src/Movable.vala:125 src/Controllers/RowController.vala:127 73 | msgid "Delete" 74 | msgstr "" 75 | 76 | #: src/Controllers/RowController.vala:89 src/Widgets/Row.vala:7 77 | msgid "Board" 78 | msgstr "" 79 | 80 | #: src/Controllers/RowController.vala:120 81 | #, c-format 82 | msgid "Do you want to delete \"%s\"?" 83 | msgstr "" 84 | 85 | #: src/Controllers/RowController.vala:121 86 | msgid "" 87 | "The content of this board will be deleted completely and cannot be restored." 88 | msgstr "" 89 | 90 | #: src/Controllers/ImageController.vala:44 91 | msgid "Open File" 92 | msgstr "" 93 | 94 | #: src/Controllers/ImageController.vala:47 95 | msgid "Open" 96 | msgstr "" 97 | 98 | #: src/Controllers/ImageController.vala:48 99 | msgid "Cancel" 100 | msgstr "" 101 | 102 | #: src/Widgets/FloatingButton.vala:35 103 | msgid "Text" 104 | msgstr "" 105 | 106 | #: src/Widgets/FloatingButton.vala:36 107 | msgid "Label" 108 | msgstr "" 109 | 110 | #: src/Widgets/FloatingButton.vala:37 111 | msgid "Image" 112 | msgstr "" 113 | 114 | #: src/Widgets/Row.vala:77 115 | msgid "Rename" 116 | msgstr "" 117 | -------------------------------------------------------------------------------- /po/de.po: -------------------------------------------------------------------------------- 1 | # German translations for com.github.brain_child.moobo package. 2 | # Copyright (C) 2021 THE com.github.brain_child.moobo'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.github.brain_child.moobo package. 4 | # Automatically generated, 2021. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: com.github.brain_child.moobo\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 11 | "PO-Revision-Date: 2021-11-06 16:35+0100\n" 12 | "Last-Translator: Automatically generated\n" 13 | "Language-Team: none\n" 14 | "Language: de\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: src/Application.vala:104 21 | msgid "Shortcuts" 22 | msgstr "Tastenkürzel" 23 | 24 | #: src/Application.vala:123 25 | msgid "Application" 26 | msgstr "Anwendung" 27 | 28 | #: src/Application.vala:124 29 | msgid "Show shortcuts:" 30 | msgstr "Zeige Tastenkürzel" 31 | 32 | #: src/Application.vala:126 33 | msgid "Save and Quit:" 34 | msgstr "Speichern und schließen" 35 | 36 | #: src/Application.vala:129 src/Window.vala:71 37 | msgid "Boards" 38 | msgstr "" 39 | 40 | #: src/Application.vala:130 41 | msgid "Rename:" 42 | msgstr "Umbenennen:" 43 | 44 | #: src/Application.vala:132 45 | msgid "Widgets" 46 | msgstr "" 47 | 48 | #: src/Application.vala:133 49 | msgid "Increase font size:" 50 | msgstr "Schrift vergrößern" 51 | 52 | #: src/Application.vala:135 53 | msgid "Decrease font size:" 54 | msgstr "Schrift verkleinern" 55 | 56 | #: src/Window.vala:56 57 | msgid "New Board" 58 | msgstr "Neues Board" 59 | 60 | #: src/Movable.vala:67 61 | msgid "Change Color" 62 | msgstr "Farbe ändern" 63 | 64 | #: src/Movable.vala:86 65 | msgid "Change Font" 66 | msgstr "Schriftart ändern" 67 | 68 | #: src/Movable.vala:100 69 | msgid "Scale" 70 | msgstr "Skalieren" 71 | 72 | #: src/Movable.vala:125 src/Controllers/RowController.vala:127 73 | msgid "Delete" 74 | msgstr "Löschen" 75 | 76 | #: src/Controllers/RowController.vala:89 src/Widgets/Row.vala:7 77 | msgid "Board" 78 | msgstr "" 79 | 80 | #: src/Controllers/RowController.vala:120 81 | #, c-format 82 | msgid "Do you want to delete \"%s\"?" 83 | msgstr "Möchtest du \"%s\" löschen?" 84 | 85 | #: src/Controllers/RowController.vala:121 86 | msgid "" 87 | "The content of this board will be deleted completely and cannot be restored." 88 | msgstr "" 89 | "Der Inhalt des Boards wird komplett gelöscht und kann nicht " 90 | "wiederhergestellt werden." 91 | 92 | #: src/Controllers/ImageController.vala:44 93 | msgid "Open File" 94 | msgstr "Datei Öffnen" 95 | 96 | #: src/Controllers/ImageController.vala:47 97 | msgid "Open" 98 | msgstr "Öffnen" 99 | 100 | #: src/Controllers/ImageController.vala:48 101 | msgid "Cancel" 102 | msgstr "Abbrechen" 103 | 104 | #: src/Widgets/FloatingButton.vala:35 105 | msgid "Text" 106 | msgstr "" 107 | 108 | #: src/Widgets/FloatingButton.vala:36 109 | msgid "Label" 110 | msgstr "" 111 | 112 | #: src/Widgets/FloatingButton.vala:37 113 | msgid "Image" 114 | msgstr "Bild" 115 | 116 | #: src/Widgets/Row.vala:77 117 | msgid "Rename" 118 | msgstr "Umbenennen" 119 | 120 | #~ msgid "Double click anywhere to add a widget" 121 | #~ msgstr "Mach irgendwo einen doppelklick um ein Widget zu erstellen" 122 | 123 | #~ msgid "" 124 | #~ "Change font size: ctrl +/-\n" 125 | #~ "save and close app: ctrl q/w" 126 | #~ msgstr "" 127 | #~ "Schriftgröße ändern: strg +/-\n" 128 | #~ "Speichern und schließen: strg q/w" 129 | 130 | #~ msgid "" 131 | #~ "Right click on widget for\n" 132 | #~ "context menu.\n" 133 | #~ "(when 'grabbing cursor' appears)" 134 | #~ msgstr "" 135 | #~ "Rechtsklick auf einem Widget\n" 136 | #~ "öffnet ein Kontextmenü.\n" 137 | #~ "(wenn 'Handcursor' erscheint)" 138 | 139 | #~ msgid "Create new board" 140 | #~ msgstr "Neues Board erstellen" 141 | 142 | #~ msgid "Right click to edit" 143 | #~ msgstr "Rechtsklick zum ändern" 144 | -------------------------------------------------------------------------------- /po/extra/LINGUAS: -------------------------------------------------------------------------------- 1 | de 2 | fr 3 | pt 4 | -------------------------------------------------------------------------------- /po/extra/POTFILES: -------------------------------------------------------------------------------- 1 | data/com.github.brain_child.moobo.desktop.in 2 | data/com.github.brain_child.moobo.appdata.xml.in 3 | -------------------------------------------------------------------------------- /po/extra/de.po: -------------------------------------------------------------------------------- 1 | # German translations for extra package. 2 | # Copyright (C) 2021 THE extra'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # Automatically generated, 2021. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 11 | "PO-Revision-Date: 2021-11-10 11:25+0100\n" 12 | "Last-Translator: Automatically generated\n" 13 | "Language-Team: none\n" 14 | "Language: de\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: data/com.github.brain_child.moobo.desktop.in:3 21 | #: data/com.github.brain_child.moobo.appdata.xml.in:6 22 | msgid "Moobo" 23 | msgstr "Moobo" 24 | 25 | #: data/com.github.brain_child.moobo.desktop.in:4 26 | msgid "Moodboard Application" 27 | msgstr "Moodboard Applikation" 28 | 29 | #: data/com.github.brain_child.moobo.desktop.in:5 30 | msgid "Visualize your ideas" 31 | msgstr "Visualisiere deine Ideen" 32 | 33 | #: data/com.github.brain_child.moobo.desktop.in:8 34 | msgid "com.github.brain_child.moobo" 35 | msgstr "com.github.brain_child.moobo" 36 | 37 | #: data/com.github.brain_child.moobo.desktop.in:11 38 | msgid "notes;moobo;moodboard;idea;" 39 | msgstr "notizen;moobo;moodbord;idee" 40 | 41 | #: data/com.github.brain_child.moobo.appdata.xml.in:7 42 | msgid "Visualize your ideas on moodboards" 43 | msgstr "Visualisiere deine Ideen auf Moodboards" 44 | 45 | #: data/com.github.brain_child.moobo.appdata.xml.in:10 46 | msgid "Use different widgets to visualize your thoughts." 47 | msgstr "Benutze verschiedene Widgets um deine Gedanken zu visualisieren." 48 | 49 | #: data/com.github.brain_child.moobo.appdata.xml.in:13 50 | msgid "" 51 | "⚠ NOTE: The app is not finished. At this point, consider Moobo as proof of " 52 | "concept." 53 | msgstr "Die App ist noch nicht fertig. Betrachte sie im Moment als Nachweis der Machbarkeit." 54 | 55 | #: data/com.github.brain_child.moobo.appdata.xml.in:22 56 | msgid "brain·​child" 57 | msgstr "brain·​child" 58 | 59 | #: data/com.github.brain_child.moobo.appdata.xml.in:42 60 | msgid "Add shortcuts for QWERTY and AZERTY keyboard layout" 61 | msgstr "Shortcuts für QWERTY und AZERTY Tastaturen" 62 | 63 | #: data/com.github.brain_child.moobo.appdata.xml.in:49 64 | msgid "Window size is calculated dynamically" 65 | msgstr "Fenstergöße wird dynamisch berechnet" 66 | 67 | #: data/com.github.brain_child.moobo.appdata.xml.in:50 68 | msgid "Show shortcuts by pressing ESC" 69 | msgstr "Anzeigen von Tastenkürzel mit ESC" 70 | 71 | #: data/com.github.brain_child.moobo.appdata.xml.in:51 72 | msgid "Rename board by pressing F2" 73 | msgstr "Boards mit F2 umbenennen" 74 | 75 | #: data/com.github.brain_child.moobo.appdata.xml.in:52 76 | msgid "Show placeholder when adding an image" 77 | msgstr "Platzhalter wird angezeigt beim Hinzufügen eines Bildes" 78 | 79 | #: data/com.github.brain_child.moobo.appdata.xml.in:53 80 | msgid "Add French translation" 81 | msgstr "Französische Übersetzung" 82 | 83 | #: data/com.github.brain_child.moobo.appdata.xml.in:54 84 | msgid "Add Portuguese translation" 85 | msgstr "Portugiesische Übersetzung" 86 | 87 | #: data/com.github.brain_child.moobo.appdata.xml.in:55 88 | msgid "Visual improvements" 89 | msgstr "Optische Verbesserungen" 90 | 91 | #: data/com.github.brain_child.moobo.appdata.xml.in:62 92 | msgid "Fix productpage in AppCenter" 93 | msgstr "Fix für die Produktseite im AppCenter" 94 | 95 | #: data/com.github.brain_child.moobo.appdata.xml.in:69 96 | msgid "First release!" 97 | msgstr "Erste Veröffentlichung!" 98 | -------------------------------------------------------------------------------- /po/extra/extra.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: extra\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: data/com.github.brain_child.moobo.desktop.in:3 21 | #: data/com.github.brain_child.moobo.appdata.xml.in:6 22 | msgid "Moobo" 23 | msgstr "" 24 | 25 | #: data/com.github.brain_child.moobo.desktop.in:4 26 | msgid "Moodboard Application" 27 | msgstr "" 28 | 29 | #: data/com.github.brain_child.moobo.desktop.in:5 30 | msgid "Visualize your ideas" 31 | msgstr "" 32 | 33 | #: data/com.github.brain_child.moobo.desktop.in:8 34 | msgid "com.github.brain_child.moobo" 35 | msgstr "" 36 | 37 | #: data/com.github.brain_child.moobo.desktop.in:11 38 | msgid "notes;moobo;moodboard;idea;" 39 | msgstr "" 40 | 41 | #: data/com.github.brain_child.moobo.appdata.xml.in:7 42 | msgid "Visualize your ideas on moodboards" 43 | msgstr "" 44 | 45 | #: data/com.github.brain_child.moobo.appdata.xml.in:10 46 | msgid "Use different widgets to visualize your thoughts." 47 | msgstr "" 48 | 49 | #: data/com.github.brain_child.moobo.appdata.xml.in:13 50 | msgid "" 51 | "⚠ NOTE: The app is not finished. At this point, consider Moobo as proof of " 52 | "concept." 53 | msgstr "" 54 | 55 | #: data/com.github.brain_child.moobo.appdata.xml.in:22 56 | msgid "brain·​child" 57 | msgstr "" 58 | 59 | #: data/com.github.brain_child.moobo.appdata.xml.in:42 60 | msgid "Add shortcuts for QWERTY and AZERTY keyboard layout" 61 | msgstr "" 62 | 63 | #: data/com.github.brain_child.moobo.appdata.xml.in:49 64 | msgid "Window size is calculated dynamically" 65 | msgstr "" 66 | 67 | #: data/com.github.brain_child.moobo.appdata.xml.in:50 68 | msgid "Show shortcuts by pressing ESC" 69 | msgstr "" 70 | 71 | #: data/com.github.brain_child.moobo.appdata.xml.in:51 72 | msgid "Rename board by pressing F2" 73 | msgstr "" 74 | 75 | #: data/com.github.brain_child.moobo.appdata.xml.in:52 76 | msgid "Show placeholder when adding an image" 77 | msgstr "" 78 | 79 | #: data/com.github.brain_child.moobo.appdata.xml.in:53 80 | msgid "Add French translation" 81 | msgstr "" 82 | 83 | #: data/com.github.brain_child.moobo.appdata.xml.in:54 84 | msgid "Add Portuguese translation" 85 | msgstr "" 86 | 87 | #: data/com.github.brain_child.moobo.appdata.xml.in:55 88 | msgid "Visual improvements" 89 | msgstr "" 90 | 91 | #: data/com.github.brain_child.moobo.appdata.xml.in:62 92 | msgid "Fix productpage in AppCenter" 93 | msgstr "" 94 | 95 | #: data/com.github.brain_child.moobo.appdata.xml.in:69 96 | msgid "First release!" 97 | msgstr "" 98 | -------------------------------------------------------------------------------- /po/extra/fr.po: -------------------------------------------------------------------------------- 1 | # French translations for extra package. 2 | # Copyright (C) 2021 THE extra'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # Nathan Bonnemains (@NathanBnm), 2021. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 11 | "PO-Revision-Date: 2021-11-19 21:06+0100\n" 12 | "Last-Translator: Nathan Bonnemains (@NathanBnm)\n" 13 | "Language-Team: none\n" 14 | "Language: fr\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 19 | 20 | #: data/com.github.brain_child.moobo.desktop.in:3 21 | #: data/com.github.brain_child.moobo.appdata.xml.in:6 22 | msgid "Moobo" 23 | msgstr "Moobo" 24 | 25 | #: data/com.github.brain_child.moobo.desktop.in:4 26 | msgid "Moodboard Application" 27 | msgstr "Application de moodboard" 28 | 29 | #: data/com.github.brain_child.moobo.desktop.in:5 30 | msgid "Visualize your ideas" 31 | msgstr "Visualisez vos idées" 32 | 33 | #: data/com.github.brain_child.moobo.desktop.in:8 34 | msgid "com.github.brain_child.moobo" 35 | msgstr "com.github.brain_child.moobo" 36 | 37 | #: data/com.github.brain_child.moobo.desktop.in:11 38 | msgid "notes;moobo;moodboard;idea;" 39 | msgstr "notes;moobo;moodboard;idée;" 40 | 41 | #: data/com.github.brain_child.moobo.appdata.xml.in:7 42 | msgid "Visualize your ideas on moodboards" 43 | msgstr "Visualisez vos idées sur des moodboards" 44 | 45 | #: data/com.github.brain_child.moobo.appdata.xml.in:10 46 | msgid "Use different widgets to visualize your thoughts." 47 | msgstr "" 48 | 49 | #: data/com.github.brain_child.moobo.appdata.xml.in:13 50 | msgid "" 51 | "⚠ NOTE: The app is not finished. At this point, consider Moobo as proof of " 52 | "concept." 53 | msgstr "" 54 | 55 | #: data/com.github.brain_child.moobo.appdata.xml.in:22 56 | msgid "brain·​child" 57 | msgstr "brain·​child" 58 | 59 | #: data/com.github.brain_child.moobo.appdata.xml.in:42 60 | msgid "Add shortcuts for QWERTY and AZERTY keyboard layout" 61 | msgstr "" 62 | 63 | #: data/com.github.brain_child.moobo.appdata.xml.in:49 64 | msgid "Window size is calculated dynamically" 65 | msgstr "" 66 | 67 | #: data/com.github.brain_child.moobo.appdata.xml.in:50 68 | msgid "Show shortcuts by pressing ESC" 69 | msgstr "" 70 | 71 | #: data/com.github.brain_child.moobo.appdata.xml.in:51 72 | msgid "Rename board by pressing F2" 73 | msgstr "" 74 | 75 | #: data/com.github.brain_child.moobo.appdata.xml.in:52 76 | msgid "Show placeholder when adding an image" 77 | msgstr "" 78 | 79 | #: data/com.github.brain_child.moobo.appdata.xml.in:53 80 | msgid "Add French translation" 81 | msgstr "" 82 | 83 | #: data/com.github.brain_child.moobo.appdata.xml.in:54 84 | msgid "Add Portuguese translation" 85 | msgstr "" 86 | 87 | #: data/com.github.brain_child.moobo.appdata.xml.in:55 88 | msgid "Visual improvements" 89 | msgstr "" 90 | 91 | #: data/com.github.brain_child.moobo.appdata.xml.in:62 92 | msgid "Fix productpage in AppCenter" 93 | msgstr "Correction de la page de l'application dans le Centre d'Applications" 94 | 95 | #: data/com.github.brain_child.moobo.appdata.xml.in:69 96 | msgid "First release!" 97 | msgstr "Première version !" 98 | -------------------------------------------------------------------------------- /po/extra/meson.build: -------------------------------------------------------------------------------- 1 | # Install metadata translations 2 | i18n.gettext ('extra', 3 | args: [ 4 | '--directory=' + meson.source_root (), 5 | '--from-code=UTF-8' 6 | ], 7 | install: false 8 | ) 9 | -------------------------------------------------------------------------------- /po/extra/pt.po: -------------------------------------------------------------------------------- 1 | # Portuguese translations for extra package. 2 | # Copyright (C) 2021 THE extra'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # Automatically generated, 2021. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 11 | "PO-Revision-Date: 2021-11-19 16:27+0100\n" 12 | "Last-Translator: Automatically generated\n" 13 | "Language-Team: none\n" 14 | "Language: pt\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: data/com.github.brain_child.moobo.desktop.in:3 21 | #: data/com.github.brain_child.moobo.appdata.xml.in:6 22 | msgid "Moobo" 23 | msgstr "Moobo" 24 | 25 | #: data/com.github.brain_child.moobo.desktop.in:4 26 | msgid "Moodboard Application" 27 | msgstr "aplicação Moodboard" 28 | 29 | #: data/com.github.brain_child.moobo.desktop.in:5 30 | msgid "Visualize your ideas" 31 | msgstr "Visualize as suas ideas" 32 | 33 | #: data/com.github.brain_child.moobo.desktop.in:8 34 | msgid "com.github.brain_child.moobo" 35 | msgstr "com.github.brain_child.moobo" 36 | 37 | #: data/com.github.brain_child.moobo.desktop.in:11 38 | msgid "notes;moobo;moodboard;idea;" 39 | msgstr "notas;moobo;moodboard;ideas" 40 | 41 | #: data/com.github.brain_child.moobo.appdata.xml.in:7 42 | msgid "Visualize your ideas on moodboards" 43 | msgstr "Visualize as suas ideas num moodboard" 44 | 45 | #: data/com.github.brain_child.moobo.appdata.xml.in:10 46 | msgid "Use different widgets to visualize your thoughts." 47 | msgstr "" 48 | 49 | #: data/com.github.brain_child.moobo.appdata.xml.in:13 50 | msgid "" 51 | "⚠ NOTE: The app is not finished. At this point, consider Moobo as proof of " 52 | "concept." 53 | msgstr "" 54 | 55 | #: data/com.github.brain_child.moobo.appdata.xml.in:22 56 | msgid "brain·​child" 57 | msgstr "brain·​child" 58 | 59 | #: data/com.github.brain_child.moobo.appdata.xml.in:42 60 | msgid "Add shortcuts for QWERTY and AZERTY keyboard layout" 61 | msgstr "" 62 | 63 | #: data/com.github.brain_child.moobo.appdata.xml.in:49 64 | msgid "Window size is calculated dynamically" 65 | msgstr "" 66 | 67 | #: data/com.github.brain_child.moobo.appdata.xml.in:50 68 | msgid "Show shortcuts by pressing ESC" 69 | msgstr "" 70 | 71 | #: data/com.github.brain_child.moobo.appdata.xml.in:51 72 | msgid "Rename board by pressing F2" 73 | msgstr "" 74 | 75 | #: data/com.github.brain_child.moobo.appdata.xml.in:52 76 | msgid "Show placeholder when adding an image" 77 | msgstr "" 78 | 79 | #: data/com.github.brain_child.moobo.appdata.xml.in:53 80 | msgid "Add French translation" 81 | msgstr "" 82 | 83 | #: data/com.github.brain_child.moobo.appdata.xml.in:54 84 | msgid "Add Portuguese translation" 85 | msgstr "" 86 | 87 | #: data/com.github.brain_child.moobo.appdata.xml.in:55 88 | msgid "Visual improvements" 89 | msgstr "" 90 | 91 | #: data/com.github.brain_child.moobo.appdata.xml.in:62 92 | msgid "Fix productpage in AppCenter" 93 | msgstr "Consertado pela site do produto no AppCenter" 94 | 95 | #: data/com.github.brain_child.moobo.appdata.xml.in:69 96 | msgid "First release!" 97 | msgstr "Publicada pela primeira vez!" 98 | -------------------------------------------------------------------------------- /po/fr.po: -------------------------------------------------------------------------------- 1 | # French translations for com.github.brain_child.moobo package. 2 | # Copyright (C) 2021 THE com.github.brain_child.moobo'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.github.brain_child.moobo package. 4 | # Nathan Bonnemains (@NathanBnm), 2021. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: com.github.brain_child.moobo\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 11 | "PO-Revision-Date: 2021-11-19 21:06+0100\n" 12 | "Last-Translator: Nathan Bonnemains (@NathanBnm)\n" 13 | "Language-Team: none\n" 14 | "Language: fr\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 19 | 20 | #: src/Application.vala:104 21 | msgid "Shortcuts" 22 | msgstr "" 23 | 24 | #: src/Application.vala:123 25 | msgid "Application" 26 | msgstr "" 27 | 28 | #: src/Application.vala:124 29 | msgid "Show shortcuts:" 30 | msgstr "" 31 | 32 | #: src/Application.vala:126 33 | msgid "Save and Quit:" 34 | msgstr "" 35 | 36 | #: src/Application.vala:129 src/Window.vala:71 37 | msgid "Boards" 38 | msgstr "Tableaux" 39 | 40 | #: src/Application.vala:130 41 | msgid "Rename:" 42 | msgstr "Renommer:" 43 | 44 | #: src/Application.vala:132 45 | msgid "Widgets" 46 | msgstr "" 47 | 48 | #: src/Application.vala:133 49 | msgid "Increase font size:" 50 | msgstr "" 51 | 52 | #: src/Application.vala:135 53 | msgid "Decrease font size:" 54 | msgstr "" 55 | 56 | #: src/Window.vala:56 57 | msgid "New Board" 58 | msgstr "Nouveau tableau" 59 | 60 | #: src/Movable.vala:67 61 | msgid "Change Color" 62 | msgstr "Modifier la couleur" 63 | 64 | #: src/Movable.vala:86 65 | msgid "Change Font" 66 | msgstr "Modifier la police" 67 | 68 | #: src/Movable.vala:100 69 | msgid "Scale" 70 | msgstr "Modifier la taille" 71 | 72 | #: src/Movable.vala:125 src/Controllers/RowController.vala:127 73 | msgid "Delete" 74 | msgstr "Supprimer" 75 | 76 | #: src/Controllers/RowController.vala:89 src/Widgets/Row.vala:7 77 | msgid "Board" 78 | msgstr "Tableau" 79 | 80 | #: src/Controllers/RowController.vala:120 81 | #, c-format 82 | msgid "Do you want to delete \"%s\"?" 83 | msgstr "Voulez-vous supprimer « %s » ?" 84 | 85 | #: src/Controllers/RowController.vala:121 86 | msgid "" 87 | "The content of this board will be deleted completely and cannot be restored." 88 | msgstr "" 89 | "Le contenu de ce tableau sera entièrement supprimé et ne pourra pas être " 90 | "restauré." 91 | 92 | #: src/Controllers/ImageController.vala:44 93 | msgid "Open File" 94 | msgstr "Ouvrir un fichier" 95 | 96 | #: src/Controllers/ImageController.vala:47 97 | msgid "Open" 98 | msgstr "Ouvrir" 99 | 100 | #: src/Controllers/ImageController.vala:48 101 | msgid "Cancel" 102 | msgstr "Annuler" 103 | 104 | #: src/Widgets/FloatingButton.vala:35 105 | msgid "Text" 106 | msgstr "Texte" 107 | 108 | #: src/Widgets/FloatingButton.vala:36 109 | msgid "Label" 110 | msgstr "Étiquette" 111 | 112 | #: src/Widgets/FloatingButton.vala:37 113 | msgid "Image" 114 | msgstr "Image" 115 | 116 | #: src/Widgets/Row.vala:77 117 | msgid "Rename" 118 | msgstr "Renommer" 119 | 120 | #~ msgid "Demo" 121 | #~ msgstr "Démo" 122 | 123 | #~ msgid "Double click anywhere to add a widget" 124 | #~ msgstr "Effectuez un double-clic où vous voulez pour ajouter un widget" 125 | 126 | #~ msgid "" 127 | #~ "Change font size: ctrl +/-\n" 128 | #~ "save and close app: ctrl q/w" 129 | #~ msgstr "" 130 | #~ "Modifier la taille de la police : Ctrl +/-\n" 131 | #~ "Enregistrer et fermer l'application : Ctrl Q/W" 132 | 133 | #~ msgid "" 134 | #~ "Right click on widget for\n" 135 | #~ "context menu.\n" 136 | #~ "(when 'grabbing cursor' appears)" 137 | #~ msgstr "" 138 | #~ "Effectuez un clic droit sur un widget\n" 139 | #~ "pour afficher le menu contextuel.\n" 140 | #~ "(lorsque le curseur « main » apparaît)" 141 | 142 | #~ msgid "Create new board" 143 | #~ msgstr "Créer un nouveau tableau" 144 | 145 | #~ msgid "Right click to edit" 146 | #~ msgstr "Effectuez un clic droit pour modifier" 147 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | # Install main translations 2 | i18n.gettext (meson.project_name (), 3 | args: [ 4 | '--directory=' + meson.source_root (), 5 | '--from-code=UTF-8', 6 | '-cTRANSLATORS' 7 | ] 8 | ) 9 | 10 | subdir ('extra') 11 | -------------------------------------------------------------------------------- /po/pt.po: -------------------------------------------------------------------------------- 1 | # Portuguese translations for com.github.brain_child.moobo package. 2 | # Copyright (C) 2021 THE com.github.brain_child.moobo'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.github.brain_child.moobo package. 4 | # Automatically generated, 2021. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: com.github.brain_child.moobo\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2022-05-28 21:00+0200\n" 11 | "PO-Revision-Date: 2021-11-14 20:17+0100\n" 12 | "Last-Translator: Automatically generated\n" 13 | "Language-Team: none\n" 14 | "Language: pt\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: src/Application.vala:104 21 | msgid "Shortcuts" 22 | msgstr "" 23 | 24 | #: src/Application.vala:123 25 | msgid "Application" 26 | msgstr "" 27 | 28 | #: src/Application.vala:124 29 | msgid "Show shortcuts:" 30 | msgstr "" 31 | 32 | #: src/Application.vala:126 33 | msgid "Save and Quit:" 34 | msgstr "" 35 | 36 | #: src/Application.vala:129 src/Window.vala:71 37 | msgid "Boards" 38 | msgstr "" 39 | 40 | #: src/Application.vala:130 41 | msgid "Rename:" 42 | msgstr "mudar o nome:" 43 | 44 | #: src/Application.vala:132 45 | msgid "Widgets" 46 | msgstr "" 47 | 48 | #: src/Application.vala:133 49 | msgid "Increase font size:" 50 | msgstr "aumentar tamanho da fonte:" 51 | 52 | #: src/Application.vala:135 53 | msgid "Decrease font size:" 54 | msgstr "diminuir tamanho da fonte" 55 | 56 | #: src/Window.vala:56 57 | msgid "New Board" 58 | msgstr "Board novo" 59 | 60 | #: src/Movable.vala:67 61 | msgid "Change Color" 62 | msgstr "alterar cor" 63 | 64 | #: src/Movable.vala:86 65 | msgid "Change Font" 66 | msgstr "alterar fonte" 67 | 68 | #: src/Movable.vala:100 69 | msgid "Scale" 70 | msgstr "alterar escala" 71 | 72 | #: src/Movable.vala:125 src/Controllers/RowController.vala:127 73 | msgid "Delete" 74 | msgstr "deletar" 75 | 76 | #: src/Controllers/RowController.vala:89 src/Widgets/Row.vala:7 77 | msgid "Board" 78 | msgstr "Board" 79 | 80 | #: src/Controllers/RowController.vala:120 81 | #, c-format 82 | msgid "Do you want to delete \"%s\"?" 83 | msgstr "Você quer deletar \"%s\"?" 84 | 85 | #: src/Controllers/RowController.vala:121 86 | msgid "" 87 | "The content of this board will be deleted completely and cannot be restored." 88 | msgstr "" 89 | "O conteudo deste board vai ser deletado completamente sem possibilidade de " 90 | "ser restaurado depois." 91 | 92 | #: src/Controllers/ImageController.vala:44 93 | msgid "Open File" 94 | msgstr "abrir arquivo" 95 | 96 | #: src/Controllers/ImageController.vala:47 97 | msgid "Open" 98 | msgstr "abrir" 99 | 100 | #: src/Controllers/ImageController.vala:48 101 | msgid "Cancel" 102 | msgstr "cancelar" 103 | 104 | #: src/Widgets/FloatingButton.vala:35 105 | msgid "Text" 106 | msgstr "texto" 107 | 108 | #: src/Widgets/FloatingButton.vala:36 109 | msgid "Label" 110 | msgstr "" 111 | 112 | #: src/Widgets/FloatingButton.vala:37 113 | msgid "Image" 114 | msgstr "imagem" 115 | 116 | #: src/Widgets/Row.vala:77 117 | msgid "Rename" 118 | msgstr "mudar o nome" 119 | 120 | #~ msgid "Double click anywhere to add a widget" 121 | #~ msgstr "Faz um clique duplo em algum lugar para criar um widget" 122 | 123 | #~ msgid "" 124 | #~ "Change font size: ctrl +/-\n" 125 | #~ "save and close app: ctrl q/w" 126 | #~ msgstr "" 127 | #~ "alterar fonte strg: +/-\n" 128 | #~ "salvar e fechar: strg q/w" 129 | 130 | #~ msgid "" 131 | #~ "Right click on widget for\n" 132 | #~ "context menu.\n" 133 | #~ "(when 'grabbing cursor' appears)" 134 | #~ msgstr "" 135 | #~ "clique com botão direito em cima de um widget\n" 136 | #~ "abrir menu contextual.\n" 137 | #~ "( em caso ponteiro de mão aparece )" 138 | 139 | #~ msgid "Create new board" 140 | #~ msgstr "criar um Board novo" 141 | 142 | #~ msgid "Right click to edit" 143 | #~ msgstr "clique com botão direito para alterar" 144 | -------------------------------------------------------------------------------- /preview/moobo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brain-child/moobo/c830fd639fa76dcb4293f4b2770dc0fc912c72a9/preview/moobo_dark.png -------------------------------------------------------------------------------- /preview/moobo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brain-child/moobo/c830fd639fa76dcb4293f4b2770dc0fc912c72a9/preview/moobo_light.png -------------------------------------------------------------------------------- /src/Application.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-3.0-or-later 3 | * SPDX-FileCopyrightText: 2021 Pierre Fabarius 4 | */ 5 | 6 | public class Application : Gtk.Application { 7 | 8 | public static int main (string[] args) { 9 | return new Application ().run (args); 10 | } 11 | 12 | protected override void activate () { 13 | 14 | var css_provider = new Gtk.CssProvider (); 15 | css_provider.load_from_resource ("com/github/brain_child/moobo/styles/Style.css"); 16 | Gtk.StyleContext.add_provider_for_screen ( 17 | Gdk.Screen.get_default (), 18 | css_provider, 19 | Gtk.STYLE_PROVIDER_PRIORITY_USER 20 | ); 21 | 22 | var gtk_settings = Gtk.Settings.get_default (); 23 | 24 | var granite_settings = Granite.Settings.get_default (); 25 | gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; 26 | 27 | 28 | granite_settings.notify["prefers-color-scheme"].connect (() => { 29 | gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; 30 | }); 31 | 32 | var window = new Window (this) { 33 | resizable = false, 34 | }; 35 | calc_window_size_from_screen_resolution (window); 36 | 37 | var quit_action = new SimpleAction ("quit", null); 38 | add_action (quit_action); 39 | set_accels_for_action ("app.quit", {"q", "w"}); 40 | 41 | var rename_action = new SimpleAction ("rename", null); 42 | add_action (rename_action); 43 | set_accels_for_action ("app.rename", {"F2"}); 44 | 45 | var help_action = new SimpleAction ("help", null); 46 | add_action (help_action); 47 | set_accels_for_action ("app.help", {"Escape"}); 48 | 49 | add_window (window); 50 | window.show_all (); 51 | 52 | quit_action.activate.connect (() => { 53 | window.save (); 54 | window.destroy (); 55 | }); 56 | 57 | rename_action.activate.connect (() => { 58 | window.rename_selected_board (); 59 | }); 60 | 61 | help_action.activate.connect (() => { 62 | help_window (window).show_all (); 63 | }); 64 | } 65 | 66 | private void calc_window_size_from_screen_resolution (Window window) { 67 | var display = Gdk.Screen.get_default ().get_display (); 68 | var monitor = display.get_monitor_at_window (window.get_window ()); 69 | var rect = monitor.get_geometry (); 70 | 71 | var width = (int) (rect.width * Const.WIN_SCALE_X); 72 | var height = (int) (rect.height * Const.WIN_SCALE_Y); 73 | 74 | window.set_size_request (width, height); 75 | } 76 | 77 | private Hdy.Window help_window (Hdy.ApplicationWindow main_window) { 78 | 79 | var layout = new Gtk.Grid () { 80 | orientation = Gtk.Orientation.VERTICAL, 81 | }; 82 | 83 | var shortcut_window = new Hdy.Window () { 84 | window_position = Gtk.WindowPosition.CENTER_ON_PARENT, 85 | transient_for = main_window, 86 | skip_taskbar_hint = true, 87 | resizable = false, 88 | }; 89 | 90 | shortcut_window.set_keep_above (true); 91 | shortcut_window.focus_out_event.connect (() => { 92 | shortcut_window.destroy (); 93 | return true; 94 | }); 95 | 96 | shortcut_window.key_press_event.connect ((event) => { 97 | if (event.keyval == Gdk.Key.Escape) { 98 | shortcut_window.destroy (); 99 | } 100 | return true; 101 | }); 102 | 103 | var headerbar = new Gtk.HeaderBar () { 104 | title = _("Shortcuts"), 105 | has_subtitle = false, 106 | show_close_button = true 107 | }; 108 | unowned Gtk.StyleContext headerbar_context = headerbar.get_style_context (); 109 | headerbar_context.add_class ("default-decoration"); 110 | headerbar_context.add_class (Gtk.STYLE_CLASS_FLAT); 111 | headerbar_context.add_class (Gtk.STYLE_CLASS_TITLEBAR); 112 | 113 | layout.add (headerbar); 114 | 115 | var shortcuts = new Gtk.Grid () { 116 | orientation = Gtk.Orientation.VERTICAL, 117 | column_spacing = 12, 118 | row_spacing = 12, 119 | margin = 36, 120 | margin_top = 12 121 | }; 122 | 123 | shortcuts.attach (new Granite.HeaderLabel (_("Application")), 0, 0, 2); 124 | shortcuts.attach (new Gtk.Label (_("Show shortcuts:")){ halign = Gtk.Align.END }, 1, 1); 125 | shortcuts.attach (new Granite.AccelLabel ("", "Escape"){ halign = Gtk.Align.START },2, 1); 126 | shortcuts.attach (new Gtk.Label (_("Save and Quit:")){ halign = Gtk.Align.END }, 1, 2); 127 | shortcuts.attach (new Granite.AccelLabel ("", "W"){ halign = Gtk.Align.START },2, 2); 128 | shortcuts.attach (new Granite.AccelLabel ("", "Q"){ halign = Gtk.Align.START }, 2, 3); 129 | shortcuts.attach (new Granite.HeaderLabel (_("Boards")), 0, 4, 2); 130 | shortcuts.attach (new Gtk.Label (_("Rename:")){ halign = Gtk.Align.END }, 1, 5); 131 | shortcuts.attach (new Granite.AccelLabel ("", "F2"){ halign = Gtk.Align.START }, 2, 5); 132 | shortcuts.attach (new Granite.HeaderLabel (_("Widgets")), 0, 6, 2); 133 | shortcuts.attach (new Gtk.Label (_("Increase font size:")){ halign = Gtk.Align.END }, 1, 7); 134 | shortcuts.attach (new Granite.AccelLabel ("", "plus"){ halign = Gtk.Align.START }, 2, 7); 135 | shortcuts.attach (new Gtk.Label (_("Decrease font size:")){ halign = Gtk.Align.END }, 1, 8); 136 | shortcuts.attach (new Granite.AccelLabel ("", "minus"){ halign = Gtk.Align.START }, 2, 8); 137 | 138 | layout.add (shortcuts); 139 | shortcut_window.add (layout); 140 | return shortcut_window; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/Controllers/BoardController.vala: -------------------------------------------------------------------------------- 1 | public class BoardController { 2 | 3 | public Board board { private set; get; } 4 | public Row row { private set; get; } 5 | public BoardModel model { private set; get; } 6 | public Gtk.Overlay overlay { private set; get; } 7 | public Movable selected_widget { set; get; } 8 | public Window window { private set; get; } 9 | 10 | public RowController row_controller; 11 | 12 | public BoardController (Window window, BoardModel model) { 13 | this.window = window; 14 | this.model = model; 15 | this.board = new Board (this); 16 | this.overlay = board.overlay; 17 | this.row_controller = new RowController (this, "Board", model.color); 18 | row = row_controller.row; 19 | 20 | row.focus_in_event.connect (on_focus_in); 21 | board.enter_notify_event.connect (on_enter_notify); 22 | row.activate.connect (on_activate); 23 | row.delete_row.connect (on_delete); 24 | row.renamed.connect (on_rename); 25 | board.button_press_event.connect (on_button_press); 26 | model.changed_event.connect (changed_handler); 27 | model.changed_trigger (); 28 | } 29 | 30 | public void changed_handler (Gee.ArrayList widgets, string color, string title) { 31 | board.title = title; 32 | 33 | foreach (var widget in widgets) { 34 | Gtk.Widget w = null; 35 | switch (widget.model) { 36 | case "TextWidget": 37 | var controller = new TextController (this, widget.x, widget.y, (TextModel) widget); 38 | w = controller.movable; 39 | break; 40 | case "LabelWidget": 41 | var controller = new LabelController (this, widget.x, widget.y, (LabelModel) widget); 42 | w = controller.movable; 43 | break; 44 | case "ImageWidget": 45 | var controller = new ImageController (this, widget.x, widget.y, (ImageModel) widget); 46 | w = controller.movable; 47 | break; 48 | } 49 | overlay.add_overlay (w); 50 | } 51 | row.display_name_label.label = title; 52 | set_color (color); 53 | } 54 | 55 | private bool on_focus_in () { 56 | row.activate (); 57 | return false; 58 | } 59 | 60 | private bool on_enter_notify () { 61 | if (window.listbox.get_selected_row () != row) { 62 | row.activate (); 63 | } 64 | return false; 65 | } 66 | 67 | private bool on_button_press (Gdk.EventButton event) { 68 | board.grab_focus (); 69 | board.revealer.reveal_child = false; 70 | 71 | if (event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS) { 72 | var selection = window.fab_selection; 73 | 74 | var x = (int) event.x; 75 | var y = (int) event.y; 76 | Movable widget = null; 77 | 78 | switch (selection.name ()) { 79 | case "TextWidget": 80 | var controller = new TextController (this, x, y); 81 | model.widgets.add (controller.model); 82 | widget = controller.movable; 83 | break; 84 | case "LabelWidget": 85 | var controller = new LabelController (this, x, y); 86 | model.widgets.add (controller.model); 87 | widget = controller.movable; 88 | break; 89 | case "ImageWidget": 90 | var controller = new ImageController (this, x, y); 91 | model.widgets.add (controller.model); 92 | widget = controller.movable; 93 | break; 94 | } 95 | 96 | widget.rel_pos_x = widget.margin_start = x; 97 | widget.rel_pos_y = widget.margin_top = y; 98 | 99 | overlay.add_overlay (widget); 100 | 101 | switch (selection.name ()) { 102 | case "TextWidget": 103 | var text_widget = widget as TextWidget; 104 | text_widget.textview.grab_focus (); 105 | break; 106 | case "LabelWidget": 107 | var label_widget = widget as LabelWidget; 108 | label_widget.textview.grab_focus (); 109 | break; 110 | } 111 | } 112 | 113 | return true; 114 | } 115 | 116 | private void on_activate () { 117 | window.board_header.title = board.title; 118 | if (window.deck.get_visible_child () != board) { 119 | window.deck.set_visible_child (board); 120 | } 121 | } 122 | 123 | private void on_delete (int index) { 124 | var children = window.deck.get_children (); 125 | if (children.nth_data (0) != board) { 126 | if (window.deck.get_visible_child () == board) { 127 | window.deck.set_visible_child (children.nth_data (index - 1)); 128 | var board = children.nth_data (index - 1) as Board; 129 | board.controller.row.activate (); 130 | } 131 | } 132 | } 133 | 134 | private void on_rename (string name) { 135 | board.title = name; 136 | if (window.deck.get_visible_child () == board) { 137 | window.board_header.title = board.title; 138 | } 139 | } 140 | 141 | private void set_color (string color) { 142 | var hex_color = Colors.from_name (color); 143 | 144 | string style = """ 145 | @define-color colorAccent %s; 146 | @define-color accent_color %s; 147 | """.printf (hex_color, hex_color); 148 | 149 | var style_provider = new Gtk.CssProvider (); 150 | try { 151 | style_provider.load_from_data (style, style.length); 152 | } catch (Error e) { 153 | warning (e.message); 154 | } 155 | unowned Gtk.StyleContext style_context = row.source_color.get_style_context (); 156 | style_context.add_provider (style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/Controllers/FloatingButtonController.vala: -------------------------------------------------------------------------------- 1 | public class FloatingButtonController { 2 | 3 | public signal void selection (Type type); 4 | 5 | private FloatingButton floating_button; 6 | 7 | public FloatingButtonController (FloatingButton floating_button) { 8 | this.floating_button = floating_button; 9 | floating_button.mode_button.mode_changed.connect ( () => { 10 | var index = floating_button.mode_button.selected; 11 | switch (index) { 12 | case 0: 13 | selection ( typeof (TextWidget) ); 14 | break; 15 | case 1: 16 | selection ( typeof (LabelWidget) ); 17 | break; 18 | case 2: 19 | selection ( typeof (ImageWidget) ); 20 | break; 21 | } 22 | }); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/Controllers/ImageController.vala: -------------------------------------------------------------------------------- 1 | public class ImageController { 2 | 3 | public ImageWidget movable { private set; get; } 4 | public ImageModel model { private set; get; } 5 | 6 | private BoardController board_controller; 7 | private string app_dir = "%s/com.github.brain_child.moobo".printf (Environment.get_user_data_dir ()); 8 | private int width; 9 | private int height; 10 | 11 | public ImageController (BoardController board_controller, int x, int y, ImageModel model = new ImageModel ()) { 12 | this.board_controller = board_controller; 13 | this.movable = new ImageWidget (this); 14 | this.model = model; 15 | 16 | if (model.path == "") { 17 | var placeholder = new Gtk.Image.from_icon_name ("image-x-generic", Gtk.IconSize.DIALOG) { 18 | valign = Gtk.Align.START, 19 | halign = Gtk.Align.START, 20 | margin_start = x, 21 | margin_top = y 22 | }; 23 | board_controller.overlay.add_overlay (placeholder); 24 | placeholder.show (); 25 | model.path = get_file (); 26 | board_controller.overlay.remove (placeholder); 27 | } 28 | 29 | Gdk.Pixbuf.get_file_info (model.path, out width, out height); 30 | 31 | if (model.path != "") { 32 | movable.init (model.path); 33 | update_widget (); 34 | movable.handle_events (board_controller); 35 | board_controller.board.scale.value_changed.connect (on_value_changed); 36 | } else { 37 | movable.destroy (); 38 | } 39 | } 40 | 41 | public string get_file () { 42 | string file_path = ""; 43 | var file_chooser = new Gtk.FileChooserNative ( 44 | _("Open File"), 45 | board_controller.window, 46 | Gtk.FileChooserAction.OPEN, 47 | _("Open"), 48 | _("Cancel") 49 | ); 50 | var filter = new Gtk.FileFilter (); 51 | filter.add_pixbuf_formats (); 52 | file_chooser.set_transient_for (board_controller.window); 53 | file_chooser.filter = filter; 54 | var res = file_chooser.run (); 55 | if (res == Gtk.ResponseType.ACCEPT) { 56 | var source_file = file_chooser.get_file (); 57 | file_path = copy_file (source_file); 58 | } else { 59 | movable.destroy (); 60 | } 61 | return file_path; 62 | } 63 | 64 | private void update_widget () { 65 | movable.rel_pos_x = movable.margin_start = model.x; 66 | movable.rel_pos_y = movable.margin_top = model.y; 67 | 68 | var scaled_width = (int) (width * model.scale_factor); 69 | var scaled_height = (int) (height * model.scale_factor); 70 | 71 | movable.pixbuf = movable.pixbuf.scale_simple (scaled_width, scaled_height, Gdk.InterpType.HYPER); 72 | } 73 | 74 | private string copy_file (File source_file) { 75 | var target_file_path = "%s/%d".printf (app_dir, (int) source_file.hash ()); 76 | var target_file = File.new_for_path (target_file_path); 77 | var is_existing = target_file.query_exists (); 78 | if (is_existing == true) { 79 | return target_file_path; 80 | } 81 | try { 82 | source_file.copy (target_file, GLib.FileCopyFlags.NONE, null, null); 83 | } catch (Error e) { 84 | warning (e.message); 85 | } 86 | return target_file_path; 87 | } 88 | 89 | private void on_value_changed () { 90 | if (board_controller.selected_widget == movable) { 91 | var scale_val = board_controller.board.scale.get_value (); 92 | var new_width = (int) (scale_val * width); 93 | var new_height = (int) (scale_val * height); 94 | 95 | try { 96 | movable.image.pixbuf = new Gdk.Pixbuf.from_file_at_size (model.path, new_width, new_height); 97 | } catch (Error e) { 98 | warning (e.message); 99 | } 100 | model.scale_factor = scale_val; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Controllers/LabelController.vala: -------------------------------------------------------------------------------- 1 | public class LabelController { 2 | 3 | public LabelWidget movable { private set; get; } 4 | public LabelModel model { private set; get; } 5 | private BoardController board_controller; 6 | private Gtk.Overlay overlay; 7 | private const string CSS = """ 8 | .color { 9 | background: %s; 10 | color: %s; 11 | text-shadow: none; 12 | } 13 | """; 14 | 15 | public LabelController (BoardController board_controller, int x, int y, LabelModel model = new LabelModel ()) { 16 | this.board_controller = board_controller; 17 | this.overlay = board_controller.overlay; 18 | this.movable = new LabelWidget (this); 19 | this.model = model; 20 | model.x = x; 21 | model.y = y; 22 | 23 | movable.textview.key_press_event.connect (on_key_press); 24 | movable.textview.key_release_event.connect (on_key_release); 25 | 26 | update_widget (); 27 | 28 | set_font_size (movable.font_size); 29 | movable.handle_events (board_controller); 30 | } 31 | 32 | private bool on_key_press (Gdk.EventKey event) { 33 | var key_name = event.state.to_string (); 34 | 35 | if (key_name == "GDK_CONTROL_MASK" && (event.keyval == Gdk.Key.plus || event.keyval == Gdk.Key.equal)) { 36 | movable.font_size += 10; 37 | set_font_size (movable.font_size); 38 | } 39 | if (key_name == "GDK_CONTROL_MASK" && event.keyval == Gdk.Key.minus) { 40 | movable.font_size -= 10; 41 | set_font_size (movable.font_size); 42 | } 43 | return false; 44 | } 45 | 46 | public bool on_key_release () { 47 | model.content = movable.buffer.text; 48 | return false; 49 | } 50 | 51 | private void update_widget () { 52 | movable.rel_pos_x = movable.margin_start = model.x; 53 | movable.rel_pos_y = movable.margin_top = model.y; 54 | movable.buffer.text = model.content; 55 | movable.font_size = model.font_size; 56 | var rgba = Gdk.RGBA (); 57 | if (rgba.parse (model.color)) { change_color (rgba); } 58 | } 59 | 60 | public void change_color (Gdk.RGBA rgba) { 61 | var css_provider = new Gtk.CssProvider (); 62 | 63 | var css = CSS.printf ( 64 | rgba.to_string (), 65 | Granite.contrasting_foreground_color (rgba).to_string () 66 | ); 67 | try { 68 | css_provider.load_from_data (css, css.length); 69 | } catch (Error e) { 70 | warning (e.message); 71 | } 72 | 73 | movable.textview.get_style_context ().add_provider ( 74 | css_provider, 75 | Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION 76 | ); 77 | movable.card.get_style_context ().add_provider ( 78 | css_provider, 79 | Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION 80 | ); 81 | 82 | movable.textview.get_style_context ().add_class ("color"); 83 | movable.card.get_style_context ().add_class ("color"); 84 | } 85 | 86 | private void set_font_size (int font_size) { 87 | model.font_size = font_size; 88 | string style = "textview { 89 | font-size: %d%; 90 | }".printf (font_size); 91 | 92 | var style_provider = new Gtk.CssProvider (); 93 | try { 94 | style_provider.load_from_data (style, style.length); 95 | var style_context = movable.textview.get_style_context (); 96 | style_context.add_provider (style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 97 | } catch (Error e) { 98 | warning (e.message); 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/Controllers/RowController.vala: -------------------------------------------------------------------------------- 1 | public class RowController { 2 | 3 | public Row row { private set; get; } 4 | private BoardController board_controller; 5 | private string name; 6 | private string color; 7 | 8 | public RowController (BoardController board_controller, string name, string color) { 9 | row = new Row (board_controller, name, color); 10 | this.board_controller = board_controller; 11 | this.name = name; 12 | this.color = color; 13 | 14 | row.state_flags_changed.connect (on_state_flags_changed); 15 | row.delete_eventbox.button_press_event.connect (on_delete_button_press); 16 | row.grid_eventbox.button_press_event.connect (on_button_press); 17 | row.rename_item.activate.connect (on_activate); 18 | row.menu_item.color_changed.connect (on_color_changed); 19 | } 20 | 21 | private void on_color_changed (string color) { 22 | change_color (color); 23 | } 24 | 25 | private void on_activate () { 26 | rename (); 27 | } 28 | 29 | private bool on_delete_button_press () { 30 | show_delete_dialog (); 31 | return false; 32 | } 33 | 34 | private bool on_button_press (Gdk.EventButton event) { 35 | if (event.button == 3) { 36 | row.menu.popup_at_pointer (); 37 | } 38 | return false; 39 | } 40 | 41 | private void on_state_flags_changed () { 42 | var flag_string = row.get_state_flags ().to_string (); 43 | if (flag_string == "GTK_STATE_FLAG_PRELIGHT") { 44 | var b = row.grid.get_child_at (2, 0); 45 | row.delete_button.set_from_icon_name ("application-exit-symbolic", Gtk.IconSize.BUTTON); 46 | if (b == null) { 47 | } 48 | } else { 49 | if (flag_string != "GTK_STATE_FLAG_ACTIVE") { 50 | row.delete_button.set_from_icon_name ("", Gtk.IconSize.BUTTON); 51 | } 52 | } 53 | } 54 | 55 | private void change_color (string color_name) { 56 | var color = Colors.from_name (color_name); 57 | string style = """ 58 | @define-color colorAccent %s; 59 | @define-color accent_color %s; 60 | """.printf (color, color); 61 | 62 | var style_provider = new Gtk.CssProvider (); 63 | try { 64 | style_provider.load_from_data (style, style.length); 65 | } catch (Error e) { 66 | warning (e.message); 67 | } 68 | unowned Gtk.StyleContext style_context = row.source_color.get_style_context (); 69 | style_context.add_provider (style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 70 | 71 | board_controller.model.color = color_name; 72 | } 73 | 74 | public void rename () { 75 | row.display_name_label.hide (); 76 | var textview = new Gtk.TextView () { 77 | wrap_mode = Gtk.WrapMode.NONE, 78 | halign = Gtk.Align.START, 79 | valign = Gtk.Align.CENTER, 80 | can_focus = true, 81 | hexpand = true, 82 | }; 83 | textview.grab_focus (); 84 | var textbuffer = textview.get_buffer (); 85 | textbuffer.set_text (row.display_name_label.label); 86 | 87 | textview.focus_out_event.connect (() => { 88 | if (textbuffer.text.strip () == "") { 89 | textbuffer.text = _("Board"); 90 | } 91 | textview.hide (); 92 | board_controller.model.title = textbuffer.text; 93 | row.display_name_label.label = textbuffer.text; 94 | row.display_name_label.show (); 95 | row.renamed (textbuffer.text); 96 | return false; 97 | }); 98 | 99 | textview.key_press_event.connect ((event) => { 100 | if (event.keyval == Gdk.Key.Return) { 101 | board_controller.board.grab_focus (); 102 | return true; 103 | } 104 | return false; 105 | }); 106 | 107 | Gtk.TextIter start_iter, end_iter; 108 | textview.buffer.get_start_iter (out start_iter); 109 | textview.buffer.get_end_iter (out end_iter); 110 | textview.buffer.select_range (start_iter, end_iter); 111 | 112 | textview.get_style_context ().add_class ("editable-label"); 113 | row.grid.attach (textview, 1, 0); 114 | textview.show (); 115 | textview.grab_focus (); 116 | } 117 | 118 | private void show_delete_dialog () { 119 | var message_dialog = new Granite.MessageDialog.with_image_from_icon_name ( 120 | _("Do you want to delete \"%s\"?").printf (row.display_name_label.label), 121 | _("The content of this board will be deleted completely and cannot be restored."), 122 | "dialog-warning", 123 | Gtk.ButtonsType.CANCEL 124 | ); 125 | message_dialog.transient_for = row.get_toplevel () as Gtk.Window; 126 | 127 | var suggested_button = new Gtk.Button.with_label (_("Delete")); 128 | suggested_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); 129 | message_dialog.add_action_widget (suggested_button, Gtk.ResponseType.ACCEPT); 130 | 131 | message_dialog.show_all (); 132 | row.activate (); 133 | board_controller.window.sensitive = false; 134 | message_dialog.response.connect ((response_id) => { 135 | if (response_id == Gtk.ResponseType.ACCEPT) { 136 | row.delete_row (row.get_index ()); 137 | if (board_controller.window.listbox.get_children ().length () == 1) { 138 | board_controller.window.create_new_board (); 139 | } 140 | board_controller.board.destroy (); 141 | row.destroy (); 142 | } else { 143 | board_controller.window.sensitive = true; 144 | } 145 | message_dialog.close (); 146 | }); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/Controllers/TextController.vala: -------------------------------------------------------------------------------- 1 | public class TextController { 2 | 3 | public TextWidget movable { private set; get; } 4 | public TextModel model { private set; get; } 5 | private BoardController board_controller; 6 | private Gtk.Overlay overlay; 7 | 8 | public TextController (BoardController board_controller, int x, int y, TextModel model = new TextModel ()) { 9 | this.board_controller = board_controller; 10 | this.overlay = board_controller.overlay; 11 | this.movable = new TextWidget (this); 12 | this.model = model; 13 | model.x = x; 14 | model.y = y; 15 | 16 | movable.textview.key_press_event.connect (on_key_press); 17 | movable.textview.key_release_event.connect (on_key_release); 18 | 19 | update_widget (); 20 | 21 | set_font_size (movable.font_size); 22 | movable.handle_events (board_controller); 23 | } 24 | 25 | private bool on_key_press (Gdk.EventKey event) { 26 | var key_name = event.state.to_string (); 27 | 28 | if (key_name == "GDK_CONTROL_MASK" && (event.keyval == Gdk.Key.plus || event.keyval == Gdk.Key.equal)) { 29 | movable.font_size += 10; 30 | set_font_size (movable.font_size); 31 | } 32 | if (key_name == "GDK_CONTROL_MASK" && event.keyval == Gdk.Key.minus) { 33 | movable.font_size -= 10; 34 | set_font_size (movable.font_size); 35 | } 36 | return false; 37 | } 38 | 39 | public bool on_key_release () { 40 | model.content = movable.buffer.text; 41 | return false; 42 | } 43 | 44 | private void update_widget () { 45 | movable.rel_pos_x = movable.margin_start = model.x; 46 | movable.rel_pos_y = movable.margin_top = model.y; 47 | movable.buffer.text = model.content; 48 | movable.font_size = model.font_size; 49 | } 50 | 51 | private void set_font_size (int font_size) { 52 | model.font_size = font_size; 53 | string style = "textview { 54 | font-size: %d%; 55 | }".printf (font_size); 56 | 57 | var style_provider = new Gtk.CssProvider (); 58 | try { 59 | style_provider.load_from_data (style, style.length); 60 | var style_context = movable.textview.get_style_context (); 61 | style_context.add_provider (style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 62 | } catch (Error e) { 63 | warning ("%s\n", e.message); 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/Helper/Colors.vala: -------------------------------------------------------------------------------- 1 | namespace Colors { 2 | 3 | public string from_index (int index) { 4 | string color = "transparent"; 5 | switch (index) { 6 | case 0: 7 | break; 8 | case 1: 9 | color = "strawberry"; 10 | break; 11 | case 2: 12 | color = "orange"; 13 | break; 14 | case 3: 15 | color = "banana"; 16 | break; 17 | case 4: 18 | color = "lime"; 19 | break; 20 | case 5: 21 | color = "mint"; 22 | break; 23 | case 6: 24 | color = "blueberry"; 25 | break; 26 | case 7: 27 | color = "grape"; 28 | break; 29 | case 8: 30 | color = "bubblegum"; 31 | break; 32 | case 9: 33 | color = "cocoa"; 34 | break; 35 | case 10: 36 | color = "slate"; 37 | break; 38 | } 39 | return color; 40 | } 41 | 42 | public string from_name (string color_name) { 43 | var color = "transparent"; 44 | switch (color_name) { 45 | case "transparent": 46 | break; 47 | case "strawberry": 48 | color = "#c6262e"; 49 | break; 50 | case "orange": 51 | color = "#f37329"; 52 | break; 53 | case "banana": 54 | color = "#e6a92a"; 55 | break; 56 | case "lime": 57 | color = "#68b723"; 58 | break; 59 | case "mint": 60 | color = "#0e9a83"; 61 | break; 62 | case "blueberry": 63 | color = "#3689e6"; 64 | break; 65 | case "grape": 66 | color = "#a56de2"; 67 | break; 68 | case "bubblegum": 69 | color = "#de3e80"; 70 | break; 71 | case "cocoa": 72 | color = "#8a715e"; 73 | break; 74 | case "slate": 75 | color = "#667885"; 76 | break; 77 | } 78 | return color; 79 | } 80 | 81 | public int from_name_to_index (string color_name) { 82 | var index = 0; 83 | switch (color_name) { 84 | case "transparent": 85 | break; 86 | case "strawberry": 87 | index = 1; 88 | break; 89 | case "orange": 90 | index = 2; 91 | break; 92 | case "banana": 93 | index = 3; 94 | break; 95 | case "lime": 96 | index = 4; 97 | break; 98 | case "mint": 99 | index = 5; 100 | break; 101 | case "blueberry": 102 | index = 6; 103 | break; 104 | case "grape": 105 | index = 7; 106 | break; 107 | case "bubblegum": 108 | index = 8; 109 | break; 110 | case "cocoa": 111 | index = 9; 112 | break; 113 | case "slate": 114 | index = 10; 115 | break; 116 | } 117 | return index; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/Helper/Constants.vala: -------------------------------------------------------------------------------- 1 | namespace Const { 2 | public const string APP_NAME = "Moobo"; 3 | public const string APP_ID = "com.github.brain_child.moobo"; 4 | public const string APP_PATH = "/" + APP_ID; 5 | 6 | public const int WIN_RATIO_X = 14; 7 | public const int WIN_RATIO_Y = 9; 8 | public const int IMG_MAX_WIDTH = 1200; 9 | public const int IMG_MAX_HEIGHT= 800; 10 | 11 | public const double WIN_SCALE_X = 0.75; 12 | public const double WIN_SCALE_Y = 0.833; 13 | public const double MAX_SCALE = 3.0; 14 | } 15 | -------------------------------------------------------------------------------- /src/Helper/DemoBoard.vala: -------------------------------------------------------------------------------- 1 | namespace DemoBoard { 2 | 3 | private const string TITLE = _("Demo"); 4 | private const string TEXT_A = _("Widget selection"); 5 | private const string TEXT_B = _("Double click anywhere to add a widget"); 6 | private const string TEXT_C = _("Change font size: ctrl +/-\nsave and close app: ctrl q/w"); 7 | private const string TEXT_D = _("Right click on widget for\ncontext menu.\n(when 'grabbing cursor' appears)"); 8 | private const string TEXT_E = _("Create new board"); 9 | private const string TEXT_F = _("Right click to edit"); 10 | 11 | public string get_demo_board () { 12 | return """ 13 | [ 14 | { 15 | "title" : "%s", 16 | "color" : "mint", 17 | "active" : true, 18 | "widgets" : [ 19 | { 20 | "model" : "TextWidget", 21 | "x" : 915, 22 | "y" : 695, 23 | "font-size" : 150, 24 | "content" : "%s -->" 25 | }, 26 | { 27 | "model" : "LabelWidget", 28 | "x" : 149, 29 | "y" : 136, 30 | "font-size" : 150, 31 | "content" : "%s", 32 | "color" : "rgb(32,74,135)" 33 | }, 34 | { 35 | "model" : "LabelWidget", 36 | "x" : 645, 37 | "y" : 261, 38 | "font-size" : 150, 39 | "content" : "%s", 40 | "color" : "rgb(255,255,255)" 41 | }, 42 | { 43 | "model" : "TextWidget", 44 | "x" : 407, 45 | "y" : 441, 46 | "font-size" : 170, 47 | "content" : "%s" 48 | }, 49 | { 50 | "model" : "TextWidget", 51 | "x" : 1, 52 | "y" : 728, 53 | "font-size" : 120, 54 | "content" : "<-- %s" 55 | }, 56 | { 57 | "model" : "TextWidget", 58 | "x" : 1, 59 | "y" : 25, 60 | "font-size" : 130, 61 | "content" : "<-- %s" 62 | }, 63 | { 64 | "model" : "TextWidget", 65 | "x" : 474, 66 | "y" : 117, 67 | "font-size" : 210, 68 | "content" : "🙃️" 69 | } 70 | ] 71 | } 72 | ] 73 | """.printf (TITLE, TEXT_A, TEXT_B, TEXT_C, TEXT_D, TEXT_E, TEXT_F); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Helper/Deserializer.vala: -------------------------------------------------------------------------------- 1 | namespace Deserializer { 2 | 3 | public Gee.ArrayList from_json () { 4 | var app_dir = Environment.get_user_data_dir () + Const.APP_PATH; 5 | var path = "%s/%s".printf (app_dir, "boards.json"); 6 | 7 | var boards_list = new Gee.ArrayList (); 8 | 9 | try { 10 | var parser = new Json.Parser (); 11 | parser.load_from_file (path); 12 | var root = parser.get_root (); 13 | 14 | parse_board (root, boards_list); 15 | } catch (Error e) { 16 | info (e.message); 17 | 18 | var settings = new GLib.Settings (Const.APP_ID); 19 | var first_time_running = settings.get_boolean ("first-run"); 20 | 21 | if (first_time_running) { 22 | try { 23 | File dir = File.new_for_path (app_dir); 24 | dir.make_directory (); 25 | // var parser = new Json.Parser (); 26 | // parser.load_from_data (DemoBoard.get_demo_board ()); 27 | // var root = parser.get_root (); 28 | // parse_board (root, boards_list); 29 | } catch (Error e) { 30 | warning (e.message); 31 | } 32 | settings.set_boolean ("first-run", false); 33 | } 34 | 35 | } 36 | return boards_list; 37 | } 38 | 39 | private void parse_board (Json.Node node, Gee.ArrayList boards_list) { 40 | var array = node.get_array (); 41 | array.foreach_element ((array, index, node) => { 42 | var board_model = new BoardModel (); 43 | var object = node.get_object (); 44 | var widgets = object.get_array_member ("widgets"); 45 | foreach (var item in widgets.get_elements ()) { 46 | var n = item.get_object (); 47 | board_model.widgets.add (parse_widgets (n)); 48 | } 49 | board_model.title = object.get_string_member ("title"); 50 | board_model.color = object.get_string_member ("color"); 51 | board_model.is_active = object.get_boolean_member ("active"); 52 | 53 | boards_list.add (board_model); 54 | }); 55 | } 56 | 57 | private BaseModel parse_widgets (Json.Object node) { 58 | BaseModel model = null; 59 | var name = node.get_string_member ("model"); 60 | switch (name) { 61 | case "TextWidget": 62 | model = new TextModel () { 63 | x = (int) node.get_int_member ("x"), 64 | y = (int) node.get_int_member ("y"), 65 | font_size = (int) node.get_int_member ("font-size"), 66 | content = node.get_string_member ("content") 67 | }; 68 | break; 69 | case "LabelWidget": 70 | model = new LabelModel () { 71 | x = (int) node.get_int_member ("x"), 72 | y = (int) node.get_int_member ("y"), 73 | font_size = (int) node.get_int_member ("font-size"), 74 | content = node.get_string_member ("content"), 75 | color = node.get_string_member ("color") 76 | }; 77 | break; 78 | case "ImageWidget": 79 | model = new ImageModel () { 80 | x = (int) node.get_int_member ("x"), 81 | y = (int) node.get_int_member ("y"), 82 | path = node.get_string_member ("path"), 83 | scale_factor = node.get_double_member ("scale-factor"), 84 | }; 85 | break; 86 | } 87 | return model; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/Helper/Serializer.vala: -------------------------------------------------------------------------------- 1 | namespace Serializer { 2 | 3 | public void to_json (GLib.List widgets) { 4 | var node_array = new Json.Array (); 5 | 6 | foreach (var widget in widgets) { 7 | var board = (Board) widget; 8 | var node = serilize_board (board.controller.model); 9 | node_array.add_element (node); 10 | } 11 | 12 | var object = new Json.Object (); 13 | object.set_array_member ("boards", node_array); 14 | write_file (object.get_member ("boards")); 15 | } 16 | 17 | private Json.Node serilize_board (BoardModel model) { 18 | var widgets = model.widgets; 19 | var title = model.title; 20 | var color = model.color; 21 | var is_active = model.is_active; 22 | 23 | var builder = new Json.Builder (); 24 | builder.begin_object (); 25 | 26 | builder.set_member_name ("title"); 27 | builder.add_string_value (title); 28 | builder.set_member_name ("color"); 29 | builder.add_string_value (color); 30 | builder.set_member_name ("active"); 31 | builder.add_boolean_value (is_active); 32 | 33 | builder.set_member_name ("widgets"); 34 | builder.begin_array (); 35 | foreach (var widget in widgets) { 36 | BaseModel base_model = null; 37 | switch (widget.model) { 38 | case "TextWidget": 39 | base_model = widget as TextModel; 40 | var text_model = base_model as TextModel; 41 | text_model.content = text_model.content.strip (); 42 | if (text_model.content == "") { continue; } 43 | break; 44 | case "LabelWidget": 45 | base_model = widget as LabelModel; 46 | var label_model = base_model as LabelModel; 47 | label_model.content = label_model.content.strip (); 48 | if (label_model.content == "") { continue; } 49 | break; 50 | case "ImageWidget": 51 | base_model = widget as ImageModel; 52 | var image_model = base_model as ImageModel; 53 | if (image_model.path == "") { continue; } 54 | break; 55 | } 56 | var r = Json.gobject_serialize (base_model); 57 | builder.add_value (r); 58 | } 59 | builder.end_array (); 60 | builder.end_object (); 61 | 62 | return builder.get_root (); 63 | } 64 | 65 | private void write_file (Json.Node node) { 66 | var generator = new Json.Generator () { 67 | pretty = true, 68 | root = node 69 | }; 70 | 71 | var app_dir = Environment.get_user_data_dir () + Const.APP_PATH; 72 | var path = "%s/%s.json".printf (app_dir, "boards"); 73 | try { 74 | generator.to_file (path); 75 | } catch (Error e) { 76 | warning (e.message); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/Models/BaseModel.vala: -------------------------------------------------------------------------------- 1 | public abstract class BaseModel : GLib.Object { 2 | 3 | public string model { set; get; } 4 | public int x { set; get; } 5 | public int y { set; get; } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/Models/BoardModel.vala: -------------------------------------------------------------------------------- 1 | public class BoardModel : GLib.Object { 2 | 3 | public signal void changed_event (Gee.ArrayList widgets, string color, string title); 4 | 5 | public Gee.ArrayList widgets { set; get; } 6 | public string color { set; get; } 7 | public string title { set; get; } 8 | public bool is_active { set; get; } 9 | 10 | public BoardModel () { 11 | widgets = new Gee.ArrayList (); 12 | color = "transparent"; 13 | title = _("Board"); 14 | is_active = false; 15 | } 16 | 17 | public void changed_trigger () { 18 | changed_event (widgets, color, title); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Models/ImageModel.vala: -------------------------------------------------------------------------------- 1 | public class ImageModel : BaseModel { 2 | 3 | public string path { set; get; } 4 | public double scale_factor { set; get; } 5 | 6 | public ImageModel () { 7 | model = "ImageWidget"; 8 | path = ""; 9 | scale_factor = 1.0; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/Models/LabelModel.vala: -------------------------------------------------------------------------------- 1 | public class LabelModel : BaseModel { 2 | 3 | public int font_size { set; get; } 4 | public string content { set; get; } 5 | public string color { set; get; } 6 | 7 | public LabelModel () { 8 | model = "LabelWidget"; 9 | font_size = 150; 10 | content = ""; 11 | color = ""; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/Models/TextModel.vala: -------------------------------------------------------------------------------- 1 | public class TextModel : BaseModel { 2 | 3 | public int font_size { set; get; } 4 | public string content { set; get; } 5 | 6 | public TextModel () { 7 | model = "TextWidget"; 8 | font_size = 150; 9 | content = ""; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/Movable.vala: -------------------------------------------------------------------------------- 1 | public abstract class Movable : Gtk.EventBox { 2 | 3 | public int rel_pos_x { set; get; } 4 | public int rel_pos_y { set; get; } 5 | public int overlay_width { set; get; } 6 | public int overlay_height { set; get; } 7 | 8 | private BoardController controller; 9 | private Gee.ArrayList widgets; 10 | private Gtk.Revealer revealer; 11 | private Gtk.Scale scale; 12 | private Gtk.Overlay overlay; 13 | private int abs_pos_x; 14 | private int abs_pos_y; 15 | 16 | public void handle_events (BoardController controller) { 17 | this.controller = controller; 18 | this.widgets = controller.model.widgets; 19 | this.revealer = controller.board.revealer; 20 | this.scale = controller.board.scale; 21 | this.overlay = controller.overlay; 22 | 23 | valign = Gtk.Align.START; 24 | halign = Gtk.Align.START; 25 | 26 | button_press_event.connect (on_button_press); 27 | button_release_event.connect (on_button_release); 28 | enter_notify_event.connect (on_enter_notify); 29 | 30 | show_all (); 31 | } 32 | 33 | private bool on_button_press (Gtk.Widget widget, Gdk.EventButton event) { 34 | can_focus = false; 35 | 36 | if (widget is TextWidget) { 37 | var text_widget = widget as TextWidget; 38 | var model = text_widget.controller.model; 39 | widgets.remove (model); 40 | widgets.add (model); 41 | } 42 | else if (widget is LabelWidget) { 43 | var label_widget = widget as LabelWidget; 44 | var model = label_widget.controller.model; 45 | widgets.remove (model); 46 | widgets.add (model); 47 | } 48 | else if (widget is ImageWidget) { 49 | can_focus = true; 50 | grab_focus (); 51 | var image_widget = widget as ImageWidget; 52 | image_widget.grab_focus (); 53 | var model = image_widget.controller.model; 54 | widgets.remove (model); 55 | widgets.add (model); 56 | } 57 | 58 | if (event.button == 1) { 59 | motion_notify_event.connect (on_motion_notify); 60 | var display = get_display (); 61 | var cursor = new Gdk.Cursor.from_name (display, "grabbing"); 62 | get_window ().set_cursor (cursor); 63 | } 64 | 65 | if (event.button == 3) { 66 | Gtk.Menu menu = new Gtk.Menu (); 67 | Gtk.MenuItem color_item = new Gtk.MenuItem.with_label (_("Change Color")) { 68 | sensitive = false 69 | }; 70 | color_item.activate.connect (() => { 71 | var dialog = new Gtk.ColorChooserDialog (null, null); 72 | dialog.response.connect ((id) => { 73 | if (id == -5) { 74 | if (widget is LabelWidget) { 75 | var label_widget = (LabelWidget) widget; 76 | var rgba = dialog.get_rgba (); 77 | label_widget.controller.change_color (rgba); 78 | label_widget.controller.model.color = rgba.to_string (); 79 | } 80 | } 81 | dialog.close (); 82 | }); 83 | dialog.run (); 84 | }); 85 | 86 | var font_item = new Gtk.MenuItem.with_label (_("Change Font")) { 87 | sensitive = false 88 | }; 89 | font_item.activate.connect (() => { 90 | var dialog = new Gtk.FontChooserDialog (null, null); 91 | dialog.response.connect ((id) => { 92 | if (id == -5) { 93 | dialog.get_font (); 94 | } 95 | dialog.close (); 96 | }); 97 | dialog.run (); 98 | }); 99 | 100 | var resize_item = new Gtk.MenuItem.with_label (_("Scale")) { 101 | sensitive = false 102 | }; 103 | resize_item.activate.connect (() => { 104 | overlay.reorder_overlay (controller.board.revealer, -2); 105 | var image_widget = widget as ImageWidget; 106 | var scale_val = image_widget.controller.model.scale_factor; 107 | 108 | int width; 109 | int height; 110 | Gdk.Pixbuf.get_file_info (image_widget.controller.model.path, out width, out height); 111 | 112 | var max_x = (double) Const.IMG_MAX_WIDTH; 113 | var max_y = (double) Const.IMG_MAX_HEIGHT; 114 | var max_scale = Const.MAX_SCALE; 115 | 116 | max_scale = max_x / width; 117 | var tmp = max_y / height; 118 | if (tmp < max_scale) { max_scale = tmp; } 119 | scale.set_range (0.1, max_scale); 120 | 121 | scale.set_value (scale_val); 122 | revealer.reveal_child = true; 123 | }); 124 | 125 | Gtk.MenuItem delete_item = new Gtk.MenuItem.with_label (_("Delete")); 126 | delete_item.activate.connect (() => { 127 | if (widget is TextWidget) { 128 | var text_widget = widget as TextWidget; 129 | widgets.remove (text_widget.controller.model); 130 | } 131 | else if (widget is LabelWidget) { 132 | var label_widget = widget as LabelWidget; 133 | widgets.remove (label_widget.controller.model); 134 | } 135 | else if (widget is ImageWidget) { 136 | var image_widget = widget as ImageWidget; 137 | widgets.remove (image_widget.controller.model); 138 | } 139 | this.destroy (); 140 | }); 141 | menu.attach_to_widget (widget, null); 142 | menu.add (color_item); 143 | // menu.add (font_item); 144 | menu.add (resize_item); 145 | menu.add (new Gtk.SeparatorMenuItem ()); 146 | menu.add (delete_item); 147 | menu.show_all (); 148 | menu.popup_at_pointer (event); 149 | 150 | if (widget is LabelWidget) { 151 | color_item.sensitive = true; 152 | font_item.sensitive = true; 153 | } 154 | if (widget is TextWidget) { 155 | font_item.sensitive = true; 156 | } 157 | if (widget is ImageWidget) { 158 | resize_item.sensitive = true; 159 | } 160 | } 161 | 162 | widget.grab_focus (); 163 | 164 | revealer.reveal_child = false; 165 | 166 | abs_pos_x = (int) event.x_root; 167 | abs_pos_y = (int) event.y_root; 168 | 169 | overlay.reorder_overlay (widget, -2); 170 | controller.selected_widget = this; 171 | return true; 172 | } 173 | 174 | private bool on_button_release (Gtk.Widget widget, Gdk.EventButton event) { 175 | motion_notify_event.disconnect (on_motion_notify); 176 | 177 | var display = get_display (); 178 | var cursor = new Gdk.Cursor.from_name (display, "grab"); 179 | get_window ().set_cursor (cursor); 180 | 181 | widget.set_opacity (1); 182 | 183 | double dx = event.x_root - abs_pos_x; 184 | double dy = event.y_root - abs_pos_y; 185 | 186 | double x = rel_pos_x + dx; 187 | double y = rel_pos_y + dy; 188 | 189 | int dw = overlay.get_allocated_width () - get_allocated_width (); 190 | int dh = overlay.get_allocated_height () - get_allocated_height (); 191 | 192 | if (x > dw) { x = dw; } 193 | if (y > dh) { y = dh; } 194 | 195 | if (x < 1) { x = 1; } 196 | if (y < 1) { y = 1; } 197 | 198 | rel_pos_x = (int) x; 199 | rel_pos_y = (int) y; 200 | 201 | var type = widget.name; 202 | switch (type) { 203 | case "TextWidget": 204 | var text_widget = widget as TextWidget; 205 | text_widget.controller.model.x = rel_pos_x; 206 | text_widget.controller.model.y = rel_pos_y; 207 | break; 208 | case "LabelWidget": 209 | var label_widget = widget as LabelWidget; 210 | label_widget.controller.model.x = rel_pos_x; 211 | label_widget.controller.model.y = rel_pos_y; 212 | break; 213 | case "ImageWidget": 214 | var image_widget = widget as ImageWidget; 215 | image_widget.controller.model.x = rel_pos_x; 216 | image_widget.controller.model.y = rel_pos_y; 217 | break; 218 | } 219 | 220 | return false; 221 | } 222 | 223 | private bool on_motion_notify (Gtk.Widget widget, Gdk.EventMotion event) { 224 | widget.set_opacity (0.7); 225 | 226 | var display = get_display (); 227 | var cursor = new Gdk.Cursor.from_name (display, "grabbing"); 228 | get_window ().set_cursor (cursor); 229 | 230 | var dx = event.x_root - abs_pos_x; 231 | var dy = event.y_root - abs_pos_y; 232 | 233 | int x = (int) (rel_pos_x + dx); 234 | int y = (int) (rel_pos_y + dy); 235 | 236 | int dw = overlay.get_allocated_width () - get_allocated_width (); 237 | int dh = overlay.get_allocated_height () - get_allocated_height (); 238 | 239 | if (x > dw) { x = dw; } 240 | if (y > dh) { y = dh; } 241 | 242 | if (x < 1) { x = 1; } 243 | if (y < 1) { y = 1; } 244 | 245 | margin_start = x; 246 | margin_top = y; 247 | 248 | return true; 249 | } 250 | 251 | private bool on_enter_notify () { 252 | var display = get_display (); 253 | var cursor = new Gdk.Cursor.from_name (display, "grab"); 254 | get_window ().set_cursor (cursor); 255 | return true; 256 | } 257 | 258 | } 259 | -------------------------------------------------------------------------------- /src/Widgets/Board.vala: -------------------------------------------------------------------------------- 1 | public class Board : Gtk.EventBox { 2 | 3 | public string title { set; get; default = _("Board"); } 4 | public Gtk.Overlay overlay { private set; get; } 5 | public BoardController controller { private set; get; } 6 | public Gtk.Revealer revealer { private set; get; } 7 | public Gtk.Scale scale { set; get; } 8 | 9 | public Board (BoardController controller) { 10 | this.controller = controller; 11 | can_focus = true; 12 | 13 | scale = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0.1, 2, 0.1) { 14 | value_pos = Gtk.PositionType.BOTTOM, 15 | halign = Gtk.Align.CENTER, 16 | valign = Gtk.Align.END, 17 | draw_value = false, 18 | }; 19 | scale.add_mark (1, Gtk.PositionType.BOTTOM, null); 20 | scale.set_value (1); 21 | scale.set_size_request (300, -1); 22 | 23 | revealer = new Gtk.Revealer () { 24 | valign = Gtk.Align.END, 25 | margin = 30, 26 | }; 27 | revealer.add (scale); 28 | 29 | overlay = new Gtk.Overlay (); 30 | overlay.add (draw_grid ()); 31 | overlay.add_overlay (revealer); 32 | 33 | add (overlay); 34 | show_all (); 35 | } 36 | 37 | private Gtk.DrawingArea draw_grid () { 38 | var drawing_area = new Gtk.DrawingArea (); 39 | drawing_area.draw.connect ((widget, context) => { 40 | var width = get_allocated_width (); 41 | var height = get_allocated_height (); 42 | context.set_source_rgb (0.7, 0.7, 0.7); 43 | context.fill (); 44 | var spacing = 20; 45 | var y = 0; 46 | var x = spacing; 47 | context.set_line_width (2.0); 48 | while (x <= width ) { 49 | while (y <= height) { 50 | context.move_to (x, y); 51 | context.line_to (x, y + 2); 52 | y += spacing; 53 | } 54 | y = 0; 55 | x += spacing; 56 | } 57 | context.stroke (); 58 | return true; 59 | }); 60 | 61 | 62 | return drawing_area; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/Widgets/ColorButton.vala: -------------------------------------------------------------------------------- 1 | private class ColorButton : Gtk.CheckButton { 2 | private static Gtk.CssProvider css_provider; 3 | public string color_name { get; construct; } 4 | 5 | static construct { 6 | css_provider = new Gtk.CssProvider (); 7 | css_provider.load_from_resource ("com/github/brain_child/moobo/styles/ColorButton.css"); 8 | } 9 | 10 | public ColorButton (string color_name) { 11 | Object (color_name: color_name); 12 | } 13 | 14 | construct { 15 | var style_context = get_style_context (); 16 | style_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 17 | style_context.add_class (Granite.STYLE_CLASS_COLOR_BUTTON); 18 | style_context.add_class (color_name); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Widgets/FloatingButton.vala: -------------------------------------------------------------------------------- 1 | public class FloatingButton : Gtk.MenuButton { 2 | 3 | public Granite.Widgets.ModeButton mode_button { private set; get; } 4 | 5 | public FloatingButton () { 6 | mode_button = create_mode_button (); 7 | var popover = create_popover (); 8 | 9 | var button = new Gtk.Button.from_icon_name ( 10 | "list-add-symbolic", 11 | Gtk.IconSize.SMALL_TOOLBAR 12 | ); 13 | button.get_style_context ().add_class ("white_label"); 14 | add (button); 15 | 16 | get_style_context ().add_class ("fab"); 17 | this.popover = popover; 18 | } 19 | 20 | construct { 21 | direction = Gtk.ArrowType.NONE; 22 | valign = Gtk.Align.END; 23 | halign = Gtk.Align.END; 24 | height_request = 40; 25 | width_request = 40; 26 | margin = 30; 27 | } 28 | 29 | private Granite.Widgets.ModeButton create_mode_button () { 30 | mode_button = new Granite.Widgets.ModeButton () { 31 | orientation = Gtk.Orientation.VERTICAL, 32 | homogeneous = false, 33 | margin = 5 34 | }; 35 | mode_button.append_text (_("Text")); 36 | mode_button.append_text (_("Label")); 37 | mode_button.append_text (_("Image")); 38 | mode_button.set_active (0); 39 | mode_button.show_all (); 40 | return mode_button; 41 | } 42 | 43 | private Gtk.Popover create_popover () { 44 | var popover = new Gtk.Popover (null); 45 | popover.add (mode_button); 46 | return popover; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Widgets/ImageWidget.vala: -------------------------------------------------------------------------------- 1 | public class ImageWidget : Movable { 2 | 3 | public ImageController controller { private set; get; } 4 | public Gtk.Image image { set; get; } 5 | public Gdk.Pixbuf pixbuf { set; get; } 6 | 7 | public ImageWidget (ImageController controller) { 8 | this.controller = controller; 9 | image = new Gtk.Image (); 10 | } 11 | 12 | public void init (string path) { 13 | var max_x = Const.IMG_MAX_WIDTH; 14 | var max_y = Const.IMG_MAX_HEIGHT; 15 | 16 | int width; 17 | int height; 18 | Gdk.Pixbuf.get_file_info (path, out width, out height); 19 | var scale_factor = controller.model.scale_factor; 20 | 21 | if (width * scale_factor > max_x) { 22 | scale_factor = ((double) max_x / width); 23 | } 24 | if (height * scale_factor > max_y) { 25 | scale_factor = ((double) max_y / height); 26 | } 27 | width = (int) (scale_factor * width); 28 | height = (int) (scale_factor * height); 29 | 30 | try { 31 | pixbuf = new Gdk.Pixbuf.from_file_at_size (path, width, height); 32 | } catch (Error e) { 33 | warning (e.message); 34 | } 35 | image = new Gtk.Image.from_pixbuf (pixbuf); 36 | 37 | add (image); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Widgets/LabelWidget.vala: -------------------------------------------------------------------------------- 1 | public class LabelWidget : Movable { 2 | 3 | public int font_size { set; get; } 4 | public Gtk.TextBuffer buffer { private set; get; } 5 | public Gtk.TextView textview { private set; get; } 6 | public LabelController controller { private set; get; } 7 | public Gtk.Grid card { private set; get; } 8 | 9 | public LabelWidget (LabelController controller) { 10 | this.controller = controller; 11 | init (); 12 | } 13 | 14 | private void init () { 15 | textview = new Gtk.TextView () { 16 | wrap_mode = Gtk.WrapMode.NONE, 17 | vexpand = true, 18 | margin = 10, 19 | }; 20 | buffer = textview.get_buffer (); 21 | 22 | card = new Gtk.Grid () { 23 | margin = 5 24 | }; 25 | var card_context = card.get_style_context (); 26 | card_context.add_class (Granite.STYLE_CLASS_CARD); 27 | card_context.add_class (Granite.STYLE_CLASS_ROUNDED); 28 | 29 | card.add (textview); 30 | add (card); 31 | 32 | show_all (); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/Widgets/MenuItemColor.vala: -------------------------------------------------------------------------------- 1 | public class MenuItemColor : Gtk.MenuItem { 2 | public signal void color_changed (string color); 3 | private Gee.ArrayList color_buttons; 4 | private const int COLORBOX_SPACING = 3; 5 | 6 | construct { 7 | var color_button_remove = new ColorButton ("none"); 8 | color_buttons = new Gee.ArrayList (); 9 | color_buttons.add (new ColorButton ("red")); 10 | color_buttons.add (new ColorButton ("orange")); 11 | color_buttons.add (new ColorButton ("yellow")); 12 | color_buttons.add (new ColorButton ("green")); 13 | color_buttons.add (new ColorButton ("mint")); 14 | color_buttons.add (new ColorButton ("blue")); 15 | color_buttons.add (new ColorButton ("purple")); 16 | color_buttons.add (new ColorButton ("pink")); 17 | color_buttons.add (new ColorButton ("brown")); 18 | color_buttons.add (new ColorButton ("slate")); 19 | 20 | var colorbox = new Gtk.Grid () { 21 | column_spacing = COLORBOX_SPACING, 22 | margin_start = 3, 23 | halign = Gtk.Align.START 24 | }; 25 | 26 | colorbox.add (color_button_remove); 27 | 28 | for (int i = 0; i < color_buttons.size; i++) { 29 | colorbox.add (color_buttons[i]); 30 | } 31 | 32 | add (colorbox); 33 | 34 | try { 35 | string css = ".nohover { background: none; }"; 36 | 37 | var css_provider = new Gtk.CssProvider (); 38 | css_provider.load_from_data (css, -1); 39 | 40 | var style_context = get_style_context (); 41 | style_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 42 | style_context.add_class ("nohover"); 43 | } catch (GLib.Error e) { 44 | warning ("Failed to parse css style : %s", e.message); 45 | } 46 | 47 | show_all (); 48 | 49 | button_press_event.connect (button_pressed_cb); 50 | 51 | } 52 | 53 | private void clear_checks () { 54 | color_buttons.foreach ((b) => { b.active = false; return true;}); 55 | } 56 | 57 | public void check_color (int color) { 58 | if (color == 0 || color > color_buttons.size) { 59 | return; 60 | } 61 | color_buttons[color - 1].active = true; 62 | } 63 | 64 | private bool button_pressed_cb (Gtk.Widget widget, Gdk.EventButton event) { 65 | 66 | var color_button_width = color_buttons[0].get_allocated_width (); 67 | 68 | int y0 = (get_allocated_height () - color_button_width) / 2; 69 | int x0 = COLORBOX_SPACING + color_button_width; 70 | 71 | if (event.y < y0 || event.y > y0 + color_button_width) { 72 | return true; 73 | } 74 | 75 | if (Gtk.StateFlags.DIR_RTL in get_style_context ().get_state ()) { 76 | var width = get_allocated_width (); 77 | int x = width - 27; 78 | for (int i = 0; i < 10; i++) { 79 | if (event.x <= x && event.x >= x - color_button_width) { 80 | color_changed (Colors.from_index (i)); 81 | clear_checks (); 82 | check_color (i); 83 | break; 84 | } 85 | 86 | x -= x0; 87 | } 88 | } else { 89 | int x = 27; 90 | for (int i = 0; i <= 10; i++) { 91 | if (event.x >= x && event.x <= x + color_button_width) { 92 | color_changed (Colors.from_index (i)); 93 | clear_checks (); 94 | check_color (i); 95 | break; 96 | } 97 | 98 | x += x0; 99 | } 100 | } 101 | 102 | return true; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/Widgets/Row.vala: -------------------------------------------------------------------------------- 1 | public class Row : Gtk.ListBoxRow { 2 | 3 | public signal void delete_row (int index); 4 | public signal void renamed (string title); 5 | 6 | public Gtk.Grid source_color { private set; get; } 7 | public string label { set; get; default = _("Board"); } 8 | public Gtk.Label display_name_label { set; get; } 9 | public Gtk.EventBox delete_eventbox { private set; get; } 10 | public Gtk.Grid grid { private set; get; } 11 | public Gtk.EventBox grid_eventbox { private set; get; } 12 | public Gtk.Image delete_button { private set; get; } 13 | public Gtk.Menu menu { private set; get; } 14 | public Gtk.MenuItem rename_item { private set; get; } 15 | public MenuItemColor menu_item { private set; get; } 16 | 17 | private BoardController board_controller; 18 | private Gtk.CssProvider listrow_provider; 19 | private string color; 20 | 21 | public Row (BoardController board_controller, string name, string color) { 22 | this.board_controller = board_controller; 23 | label = name; 24 | this.display_name_label.label = label; 25 | this.display_name_label.label = label; 26 | this.color = color; 27 | create_menu (); 28 | } 29 | 30 | construct { 31 | listrow_provider = new Gtk.CssProvider (); 32 | listrow_provider.load_from_resource ("com/github/brain_child/moobo/styles/SourceRow.css"); 33 | 34 | source_color = new Gtk.Grid () { 35 | halign = Gtk.Align.START, 36 | }; 37 | 38 | var source_color_context = source_color.get_style_context (); 39 | source_color_context.add_class ("source-color"); 40 | source_color_context.add_provider (listrow_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 41 | 42 | display_name_label = new Gtk.Label ("") { 43 | halign = Gtk.Align.START, 44 | hexpand = true, 45 | }; 46 | 47 | delete_button = new Gtk.Image.from_icon_name ("", Gtk.IconSize.BUTTON) { 48 | halign = Gtk.Align.END, 49 | margin = 0 50 | }; 51 | 52 | delete_eventbox = new Gtk.EventBox () { 53 | halign = Gtk.Align.END 54 | }; 55 | delete_eventbox.add (delete_button); 56 | 57 | grid = new Gtk.Grid () { 58 | column_spacing = 6, 59 | margin_start = 12, 60 | margin_end = 6 61 | }; 62 | grid.attach (source_color, 0, 0); 63 | grid.attach (display_name_label, 1, 0); 64 | grid.attach (delete_eventbox, 2, 0); 65 | 66 | grid_eventbox = new Gtk.EventBox (); 67 | grid_eventbox.add (grid); 68 | 69 | add (grid_eventbox); 70 | show_all (); 71 | } 72 | 73 | private void create_menu () { 74 | 75 | rename_item = new Gtk.MenuItem (); 76 | rename_item.add (new Granite.AccelLabel ( 77 | _("Rename"), 78 | "F2" 79 | )); 80 | 81 | menu_item = new MenuItemColor (); 82 | menu_item.check_color (Colors.from_name_to_index (color)); 83 | 84 | menu = new Gtk.Menu (); 85 | menu.add (rename_item); 86 | menu.add (new Gtk.SeparatorMenuItem ()); 87 | menu.add (menu_item); 88 | menu.show_all (); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/Widgets/TextWidget.vala: -------------------------------------------------------------------------------- 1 | public class TextWidget : Movable { 2 | 3 | public int font_size { set; get; } 4 | public Gtk.TextBuffer buffer { private set; get; } 5 | public Gtk.TextView textview { private set; get; } 6 | public TextController controller { private set; get; } 7 | 8 | public TextWidget (TextController controller) { 9 | this.controller = controller; 10 | init (); 11 | } 12 | 13 | private void init () { 14 | textview = new Gtk.TextView () { 15 | wrap_mode = Gtk.WrapMode.NONE, 16 | vexpand = true, 17 | margin = 10 18 | }; 19 | 20 | var style_context = textview.get_style_context (); 21 | style_context.add_class ("transparent"); 22 | style_context.add_class ("font-size"); 23 | 24 | buffer = textview.get_buffer (); 25 | 26 | add (textview); 27 | show_all (); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Window.vala: -------------------------------------------------------------------------------- 1 | public class Window : Hdy.ApplicationWindow { 2 | 3 | public Type fab_selection { private set; get; default = typeof (TextWidget); } 4 | public Gtk.ListBox listbox { private set; get; } 5 | public Hdy.HeaderBar board_header { private set; get; } 6 | public Hdy.Deck deck { private set; get; } 7 | 8 | private GLib.Settings settings; 9 | 10 | public Window (Gtk.Application application) { 11 | Object ( 12 | application: application 13 | ); 14 | 15 | delete_event.connect (() => { 16 | save (); 17 | return false; 18 | }); 19 | } 20 | 21 | static construct { 22 | Hdy.init (); 23 | } 24 | 25 | construct { 26 | settings = new GLib.Settings (Const.APP_ID); 27 | move (settings.get_int ("win-x"), settings.get_int ("win-y")); 28 | 29 | var sidebar_header = new Hdy.HeaderBar () { 30 | decoration_layout = "close", 31 | show_close_button = true 32 | }; 33 | sidebar_header.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); 34 | 35 | deck = new Hdy.Deck () { 36 | transition_type = Hdy.DeckTransitionType.SLIDE, 37 | orientation = Gtk.Orientation.VERTICAL, 38 | hexpand = true, 39 | vexpand = true 40 | }; 41 | 42 | listbox = new Gtk.ListBox () { 43 | selection_mode = Gtk.SelectionMode.BROWSE, 44 | }; 45 | 46 | var scrolled_window = new Gtk.ScrolledWindow (null, null) { 47 | expand = true, 48 | hscrollbar_policy = Gtk.PolicyType.NEVER 49 | }; 50 | scrolled_window.add (listbox); 51 | 52 | var add_button = new Gtk.Button () { 53 | image = new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.SMALL_TOOLBAR), 54 | always_show_image = true, 55 | focus_on_click = true, 56 | label = _("New Board"), 57 | }; 58 | 59 | add_button.button_release_event.connect (() => { 60 | var board = create_new_board (); 61 | board.controller.row.activate (); 62 | return false; 63 | }); 64 | 65 | var actionbar = new Gtk.ActionBar (); 66 | actionbar.add (add_button); 67 | 68 | var actionbar_style_context = actionbar.get_style_context (); 69 | actionbar_style_context.add_class (Gtk.STYLE_CLASS_FLAT); 70 | 71 | var label = new Gtk.Label (_("Boards")) { 72 | halign = Gtk.Align.START 73 | }; 74 | label.get_style_context ().add_class (Granite.STYLE_CLASS_H4_LABEL); 75 | 76 | var sidebar_grid = new Gtk.Grid (); 77 | sidebar_grid.attach (sidebar_header, 0, 0); 78 | sidebar_grid.attach (label, 0, 1); 79 | sidebar_grid.attach (scrolled_window, 0, 2); 80 | sidebar_grid.attach (actionbar, 0, 3); 81 | 82 | var style_context = sidebar_grid.get_style_context (); 83 | style_context.add_class (Gtk.STYLE_CLASS_SIDEBAR); 84 | 85 | var floating_button = new FloatingButton (); 86 | var floating_button_controller = new FloatingButtonController (floating_button); 87 | floating_button_controller.selection.connect ( (type) => { 88 | fab_selection = type; 89 | }); 90 | 91 | var overlay = new Gtk.Overlay (); 92 | overlay.add_overlay (deck); 93 | overlay.add_overlay (floating_button); 94 | 95 | board_header = new Hdy.HeaderBar () { 96 | has_subtitle = true, 97 | decoration_layout = "", 98 | show_close_button = true, 99 | title = Const.APP_NAME 100 | }; 101 | 102 | var board_header_context = board_header.get_style_context (); 103 | board_header_context.add_class ("default-decoration"); 104 | board_header_context.add_class (Gtk.STYLE_CLASS_FLAT); 105 | 106 | var listview_stack = new Gtk.Stack () { 107 | hexpand = true, 108 | vexpand = true 109 | }; 110 | 111 | var board_grid = new Gtk.Grid () { 112 | column_homogeneous = true 113 | }; 114 | board_grid.attach (board_header, 0, 0); 115 | board_grid.attach (overlay, 0, 1); 116 | 117 | var paned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL); 118 | paned.pack1 (sidebar_grid, false, false); 119 | paned.pack2 (board_grid, true, false); 120 | 121 | load_boards (); 122 | add (paned); 123 | show_all (); 124 | } 125 | 126 | public Board create_new_board (BoardModel model = new BoardModel ()) { 127 | var board_controller = new BoardController (this, model); 128 | 129 | listbox.add (board_controller.row); 130 | deck.add (board_controller.board); 131 | 132 | if (model.is_active) { 133 | board_controller.row.activate (); 134 | model.is_active = false; 135 | } 136 | return board_controller.board; 137 | } 138 | 139 | private void load_boards () { 140 | var list = Deserializer.from_json (); 141 | if (list.size == 0) { 142 | create_new_board (); 143 | } 144 | foreach (var model in list) { 145 | create_new_board (model); 146 | } 147 | } 148 | 149 | public void save () { 150 | int x, y; 151 | get_position (out x, out y); 152 | 153 | settings.set_int ("win-x", x); 154 | settings.set_int ("win-y", y); 155 | 156 | var active_board = (Board) deck.get_visible_child (); 157 | if (active_board != null) { 158 | active_board.controller.model.is_active = true; 159 | } 160 | Serializer.to_json (deck.get_children ()); 161 | } 162 | 163 | public void rename_selected_board () { 164 | var active_board = (Board) deck.get_visible_child (); 165 | if (active_board != null) { 166 | active_board.controller.row_controller.rename (); 167 | } 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | sources = files( 2 | 'Application.vala', 3 | 'Window.vala', 4 | 'Movable.vala', 5 | 6 | 'Controllers/BoardController.vala', 7 | 'Controllers/RowController.vala', 8 | 'Controllers/TextController.vala', 9 | 'Controllers/LabelController.vala', 10 | 'Controllers/ImageController.vala', 11 | 'Controllers/FloatingButtonController.vala', 12 | 13 | 'Helper/Colors.vala', 14 | 'Helper/Constants.vala', 15 | 'Helper/Serializer.vala', 16 | 'Helper/Deserializer.vala', 17 | 'Helper/DemoBoard.vala', 18 | 19 | 'Models/BoardModel.vala', 20 | 'Models/BaseModel.vala', 21 | 'Models/TextModel.vala', 22 | 'Models/LabelModel.vala', 23 | 'Models/ImageModel.vala', 24 | 25 | 'Widgets/Board.vala', 26 | 'Widgets/Row.vala', 27 | 'Widgets/MenuItemColor.vala', 28 | 'Widgets/ColorButton.vala', 29 | 'Widgets/FloatingButton.vala', 30 | 'Widgets/TextWidget.vala', 31 | 'Widgets/LabelWidget.vala', 32 | 'Widgets/ImageWidget.vala' 33 | ) 34 | --------------------------------------------------------------------------------