=
37 | Lazy::new(|| glib::ThreadPool::shared(None).expect("Unable to create thread pool"));
38 |
39 | fn main() {
40 | pretty_env_logger::init_timed();
41 |
42 | gettextrs::setlocale(LocaleCategory::LcAll, "");
43 | gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain");
44 | gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain");
45 |
46 | glib::set_application_name(&gettext("Noteworthy"));
47 |
48 | gst::init().expect("Unable to start GStreamer");
49 |
50 | gstgtk4::plugin_register_static().expect("Failed to register gstgtk4 plugin");
51 |
52 | let res = gio::Resource::load(RESOURCES_FILE).expect("Could not load gresource file");
53 | gio::resources_register(&res);
54 |
55 | let app = Application::new();
56 | app.run();
57 | }
58 |
--------------------------------------------------------------------------------
/data/meson.build:
--------------------------------------------------------------------------------
1 | subdir('icons')
2 | subdir('resources')
3 | # Desktop file
4 | desktop_conf = configuration_data()
5 | desktop_conf.set('icon', application_id)
6 | desktop_file = i18n.merge_file(
7 | type: 'desktop',
8 | input: configure_file(
9 | input: '@0@.desktop.in.in'.format(base_id),
10 | output: '@BASENAME@',
11 | configuration: desktop_conf
12 | ),
13 | output: '@0@.desktop'.format(application_id),
14 | po_dir: podir,
15 | install: true,
16 | install_dir: datadir / 'applications'
17 | )
18 | # Validate Desktop file
19 | if desktop_file_validate.found()
20 | test(
21 | 'validate-desktop',
22 | desktop_file_validate,
23 | args: [
24 | desktop_file.full_path()
25 | ],
26 | depends: desktop_file,
27 | )
28 | endif
29 |
30 | # Appdata
31 | appdata_conf = configuration_data()
32 | appdata_conf.set('app-id', application_id)
33 | appdata_conf.set('gettext-package', gettext_package)
34 | appdata_file = i18n.merge_file(
35 | input: configure_file(
36 | input: '@0@.metainfo.xml.in.in'.format(base_id),
37 | output: '@BASENAME@',
38 | configuration: appdata_conf
39 | ),
40 | output: '@0@.metainfo.xml'.format(application_id),
41 | po_dir: podir,
42 | install: true,
43 | install_dir: datadir / 'metainfo'
44 | )
45 | # Validate Appdata
46 | if appstream_util.found()
47 | test(
48 | 'validate-appdata', appstream_util,
49 | args: [
50 | 'validate', '--nonet', appdata_file.full_path()
51 | ],
52 | depends: appdata_file,
53 | )
54 | endif
55 |
56 | # GSchema
57 | gschema_conf = configuration_data()
58 | gschema_conf.set('app-id', application_id)
59 | gschema_conf.set('gettext-package', gettext_package)
60 | configure_file(
61 | input: '@0@.gschema.xml.in'.format(base_id),
62 | output: '@0@.gschema.xml'.format(application_id),
63 | configuration: gschema_conf,
64 | install: true,
65 | install_dir: datadir / 'glib-2.0' / 'schemas'
66 | )
67 |
68 | # Validata GSchema
69 | if glib_compile_schemas.found()
70 | test(
71 | 'validate-gschema', glib_compile_schemas,
72 | args: [
73 | '--strict', '--dry-run', meson.current_build_dir()
74 | ],
75 | )
76 | endif
77 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'noteworthy',
3 | 'rust',
4 | version: '0.1.0',
5 | license: 'GPL-3.0-or-later',
6 | meson_version: '>= 0.59',
7 | )
8 |
9 | i18n = import('i18n')
10 | gnome = import('gnome')
11 |
12 | base_id = 'io.github.seadve.Noteworthy'
13 |
14 | dependency('glib-2.0', version: '>= 2.66')
15 | dependency('gio-2.0', version: '>= 2.66')
16 | dependency('gtk4', version: '>= 4.5.0')
17 | dependency('libadwaita-1', version: '>= 1.0.0')
18 | dependency('gtksourceview-5', version: '>= 5.0.0')
19 | dependency('gstreamer-1.0', version: '>= 1.18')
20 | dependency('gstreamer-base-1.0', version: '>= 1.18')
21 | dependency('gstreamer-plugins-base-1.0', version: '>= 1.18')
22 |
23 | glib_compile_resources = find_program('glib-compile-resources', required: true)
24 | glib_compile_schemas = find_program('glib-compile-schemas', required: true)
25 | desktop_file_validate = find_program('desktop-file-validate', required: false)
26 | appstream_util = find_program('appstream-util', required: false)
27 | cargo = find_program('cargo', required: true)
28 |
29 | version = meson.project_version()
30 |
31 | prefix = get_option('prefix')
32 | bindir = prefix / get_option('bindir')
33 | localedir = prefix / get_option('localedir')
34 |
35 | datadir = prefix / get_option('datadir')
36 | pkgdatadir = datadir / meson.project_name()
37 | iconsdir = datadir / 'icons'
38 | podir = meson.project_source_root() / 'po'
39 | gettext_package = meson.project_name()
40 |
41 | if get_option('profile') == 'development'
42 | profile = 'Devel'
43 | vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD').stdout().strip()
44 | if vcs_tag == ''
45 | version_suffix = '-devel'
46 | else
47 | version_suffix = '-@0@'.format(vcs_tag)
48 | endif
49 | application_id = '@0@.@1@'.format(base_id, profile)
50 | else
51 | profile = ''
52 | version_suffix = ''
53 | application_id = base_id
54 | endif
55 |
56 | meson.add_dist_script(
57 | 'build-aux/dist-vendor.sh',
58 | meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version,
59 | meson.project_source_root()
60 | )
61 |
62 | subdir('data')
63 | subdir('po')
64 | subdir('src')
65 |
66 | gnome.post_install(
67 | gtk_update_icon_cache: true,
68 | glib_compile_schemas: true,
69 | update_desktop_database: true,
70 | )
71 |
--------------------------------------------------------------------------------
/data/resources/ui/content-attachment-view-audio-row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
12 |
13 |
36 |
37 |
38 |
39 | 3
40 |
41 |
42 | True
43 |
44 |
45 | 100
46 | 0
47 | 1
48 | 10
49 |
50 |
51 |
52 |
53 |
54 |
55 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Noteworthy
3 |
4 |
5 | Modern, Fast, and Version-Controlled Markdown Notes App
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | ## 0.1.0 Milestone
17 |
18 | - [x] Trash and pinning
19 | - [x] Note creation and deletion
20 | - [x] Note metadata
21 | - [x] Powerful tag system
22 | - [x] Filtering
23 | - [x] Basic markdown
24 | - [x] Batch notes selection and editing
25 | - [x] Attachments
26 | - [ ] Canvas drawing
27 | - [ ] Syncing (Barely working)
28 | - [ ] Git integration (Barely working)
29 | - [ ] Setup page
30 | - [ ] WYSIWG Editing
31 | - [ ] Homepage (Includes reminders, recents, mini notepads etc.)
32 |
33 |
34 | ## Installation Instructions
35 |
36 | Noteworthy is under heavy development. Thus, it is currently not recommended to
37 | be used for day-to-day tasks. However, it is possible to download the nightly
38 | build artifact from the [Actions page](https://github.com/SeaDve/Noteworthy/actions/),
39 | then install it locally by running `flatpak install noteworthy.flatpak`.
40 |
41 |
42 | ## Build Instructions
43 |
44 | ### GNOME Builder
45 |
46 | GNOME Builder is the environment used for developing this application.
47 | It can use Flatpak manifests to create a consistent building and running
48 | environment cross-distro. Thus, it is highly recommended you use it.
49 |
50 | 1. Download [GNOME Builder](https://flathub.org/apps/details/org.gnome.Builder).
51 | 2. In Builder, click the "Clone Repository" button at the bottom, using
52 | `https://github.com/SeaDve/Noteworthy.git` as the URL.
53 | 3. Click the build button at the top once the project is loaded.
54 |
55 | ### Meson
56 |
57 | #### Prerequisites
58 |
59 | The following packages are required to build Noteworthy:
60 |
61 | * meson
62 | * ninja
63 | * appstream-glib (for checks)
64 | * cargo
65 | * gstreamer
66 | * gstreamer-plugins-base
67 | * glib2
68 | * gtk4
69 | * gtksourceview5
70 | * libadwaita
71 |
72 | #### Build Instructions
73 |
74 | ```shell
75 | meson . _build
76 | ninja -C _build
77 | ninja -C _build install
78 | ```
79 |
--------------------------------------------------------------------------------
/src/core/audio_player_handler.rs:
--------------------------------------------------------------------------------
1 | use gtk::{
2 | glib::{self, clone},
3 | prelude::*,
4 | subclass::prelude::*,
5 | };
6 |
7 | use std::{cell::RefCell, collections::HashMap};
8 |
9 | use super::{AudioPlayer, PlaybackState};
10 |
11 | mod imp {
12 | use super::*;
13 |
14 | #[derive(Debug, Default)]
15 | pub struct AudioPlayerHandler {
16 | pub list: RefCell>,
17 | }
18 |
19 | #[glib::object_subclass]
20 | impl ObjectSubclass for AudioPlayerHandler {
21 | const NAME: &'static str = "NwtyAudioPlayerHandler";
22 | type Type = super::AudioPlayerHandler;
23 | }
24 |
25 | impl ObjectImpl for AudioPlayerHandler {}
26 | }
27 |
28 | glib::wrapper! {
29 | pub struct AudioPlayerHandler(ObjectSubclass);
30 | }
31 |
32 | impl AudioPlayerHandler {
33 | pub fn new() -> Self {
34 | glib::Object::new(&[]).expect("Failed to create AudioPlayerHandler.")
35 | }
36 |
37 | pub fn append(&self, audio_player: AudioPlayer) {
38 | let handler_id =
39 | audio_player.connect_state_notify(clone!(@weak self as obj => move |audio_player| {
40 | if audio_player.state() == PlaybackState::Playing {
41 | obj.stop_all_except(audio_player);
42 | log::info!("Stopping all except player with uri `{}`", audio_player.uri());
43 | }
44 | }));
45 |
46 | self.imp()
47 | .list
48 | .borrow_mut()
49 | .insert(audio_player, handler_id);
50 | }
51 |
52 | pub fn remove(&self, audio_player: &AudioPlayer) {
53 | let handler_id = self
54 | .imp()
55 | .list
56 | .borrow_mut()
57 | .remove(audio_player)
58 | .expect("Trying to remove audio_player that is not handled by this");
59 |
60 | audio_player.disconnect(handler_id);
61 | }
62 |
63 | pub fn stop_all(&self) {
64 | for audio_player in self.imp().list.borrow().keys() {
65 | audio_player.set_state(PlaybackState::Stopped);
66 | }
67 | }
68 |
69 | fn stop_all_except(&self, exception: &AudioPlayer) {
70 | for audio_player in self.imp().list.borrow().keys() {
71 | if audio_player != exception {
72 | audio_player.set_state(PlaybackState::Stopped);
73 | }
74 | }
75 | }
76 | }
77 |
78 | impl Default for AudioPlayerHandler {
79 | fn default() -> Self {
80 | Self::new()
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/meson.build:
--------------------------------------------------------------------------------
1 | global_conf = configuration_data()
2 | global_conf.set_quoted('APP_ID', application_id)
3 | global_conf.set_quoted('PKGDATADIR', pkgdatadir)
4 | global_conf.set_quoted('PROFILE', profile)
5 | global_conf.set_quoted('VERSION', version + version_suffix)
6 | global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package)
7 | global_conf.set_quoted('LOCALEDIR', localedir)
8 | config = configure_file(
9 | input: 'config.rs.in',
10 | output: 'config.rs',
11 | configuration: global_conf
12 | )
13 | # Copy the config.rs output to the source directory.
14 | run_command(
15 | 'cp',
16 | meson.project_build_root() / 'src' / 'config.rs',
17 | meson.project_source_root() / 'src' / 'config.rs',
18 | check: true
19 | )
20 |
21 | manifest_path = meson.project_source_root() / 'Cargo.toml'
22 | cargo_home = meson.project_build_root() / 'cargo-home'
23 | cargo_target_dir = meson.project_build_root() / 'src'
24 |
25 | cargo_options = [ '--manifest-path', manifest_path ]
26 | cargo_options += [ '--target-dir', cargo_target_dir ]
27 |
28 | if get_option('profile') == 'default'
29 | cargo_options += [ '--release' ]
30 | rust_target = 'release'
31 | message('Building in release mode')
32 | else
33 | rust_target = 'debug'
34 | message('Building in debug mode')
35 | endif
36 |
37 | cargo_env = [ 'CARGO_HOME=' + cargo_home ]
38 |
39 | cargo_build = custom_target(
40 | 'cargo-build',
41 | build_by_default: true,
42 | build_always_stale: true,
43 | output: meson.project_name(),
44 | console: true,
45 | install: true,
46 | install_dir: bindir,
47 | depends: resources,
48 | command: [
49 | 'env',
50 | cargo_env,
51 | cargo, 'build',
52 | cargo_options,
53 | '&&',
54 | 'cp', 'src' / rust_target / meson.project_name(), '@OUTPUT@',
55 | ]
56 | )
57 |
58 | test(
59 | 'cargo-test',
60 | cargo,
61 | args: [
62 | 'test',
63 | '--manifest-path=@0@'.format(manifest_path),
64 | '--target-dir=@0@'.format(cargo_target_dir),
65 | '--',
66 | '--nocapture',
67 | ],
68 | env: [
69 | 'CARGO_HOME=@0@'.format(cargo_home),
70 | 'PATH=/app/bin:/usr/bin:/usr/lib/sdk/rust-stable/bin',
71 | ],
72 | timeout: 300, # give cargo more time
73 | )
74 |
75 | test(
76 | 'cargo-clippy',
77 | cargo,
78 | args: [
79 | 'clippy',
80 | '--manifest-path=@0@'.format(manifest_path),
81 | '--target-dir=@0@'.format(cargo_target_dir),
82 | '--',
83 | '-D',
84 | 'warnings',
85 | ],
86 | env: [
87 | 'CARGO_HOME=@0@'.format(cargo_home),
88 | 'PATH=/app/bin:/usr/bin:/usr/lib/sdk/rust-stable/bin',
89 | ],
90 | timeout: 300, # give cargo more time
91 | )
92 |
--------------------------------------------------------------------------------
/data/resources/ui/content-view.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | never
7 |
8 |
9 | vertical
10 | 12
11 |
14 |
15 |
16 | vertical
17 | 6
18 |
19 |
20 | True
21 | word-char
22 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 6
33 |
34 |
35 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | NwtyContentView
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | True
58 | word
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/session/content/view/tag_bar/row.rs:
--------------------------------------------------------------------------------
1 | use adw::{prelude::*, subclass::prelude::*};
2 | use gtk::{glib, subclass::prelude::*};
3 |
4 | use std::cell::RefCell;
5 |
6 | use crate::model::Tag;
7 |
8 | mod imp {
9 | use super::*;
10 | use gtk::CompositeTemplate;
11 | use once_cell::sync::Lazy;
12 |
13 | #[derive(Debug, Default, CompositeTemplate)]
14 | #[template(resource = "/io/github/seadve/Noteworthy/ui/content-view-tag-bar-row.ui")]
15 | pub struct Row {
16 | #[template_child]
17 | pub label: TemplateChild,
18 |
19 | pub tag: RefCell