Add Windows runner files for high DPI support and console output

- Created runner.exe.manifest to enable DPI awareness and dark mode support.
- Implemented utility functions in utils.cpp and utils.h for console creation and command line argument handling.
- Developed Win32Window class in win32_window.cpp and win32_window.h to manage high DPI-aware windows, including theme updates and message handling.
This commit is contained in:
2026-05-13 12:03:40 +02:00
commit 96f8f95924
107 changed files with 6568 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
part 'app_database.g.dart';
@DataClassName('DbNote')
class Notes extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text()();
TextColumn get body => text()();
DateTimeColumn get createdAt => dateTime()();
DateTimeColumn get updatedAt => dateTime()();
IntColumn get sortIndex => integer().named('sort_index')();
}
@DriftDatabase(tables: [Notes])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
Future<List<DbNote>> getAllNotes() {
return (select(notes)..orderBy([
(note) => OrderingTerm(expression: note.sortIndex),
])).get();
}
Future<int> insertNoteAtTop(NotesCompanion note) {
return transaction(() async {
await customStatement('UPDATE notes SET sort_index = sort_index + 1');
return into(notes).insert(note.copyWith(sortIndex: const Value<int>(0)));
});
}
Future<void> replaceAllNotes(List<NotesCompanion> noteList) {
return transaction(() async {
await delete(notes).go();
for (final NotesCompanion note in noteList) {
await into(notes).insert(note);
}
});
}
Future<void> updateNoteRow(DbNote note) {
return update(notes).replace(note);
}
Future<void> deleteNoteAndShift({
required int id,
required int removedIndex,
}) {
return transaction(() async {
await (delete(notes)..where((note) => note.id.equals(id))).go();
await customStatement(
'UPDATE notes SET sort_index = sort_index - 1 WHERE sort_index > ?',
[removedIndex],
);
});
}
Future<void> moveNote({
required int id,
required int oldIndex,
required int newIndex,
}) {
if (oldIndex == newIndex) {
return Future<void>.value();
}
return transaction(() async {
if (oldIndex < newIndex) {
await customStatement(
'UPDATE notes SET sort_index = sort_index - 1 WHERE sort_index > ? AND sort_index <= ?',
[oldIndex, newIndex],
);
} else {
await customStatement(
'UPDATE notes SET sort_index = sort_index + 1 WHERE sort_index >= ? AND sort_index < ?',
[newIndex, oldIndex],
);
}
await customStatement(
'UPDATE notes SET sort_index = ? WHERE id = ?',
[newIndex, id],
);
});
}
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final Directory supportDir = await getApplicationSupportDirectory();
final File file = File(p.join(supportDir.path, 'notes.sqlite'));
return NativeDatabase(file);
});
}
+624
View File
@@ -0,0 +1,624 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'app_database.dart';
// ignore_for_file: type=lint
class $NotesTable extends Notes with TableInfo<$NotesTable, DbNote> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$NotesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id',
aliasedName,
false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'PRIMARY KEY AUTOINCREMENT',
),
);
static const VerificationMeta _titleMeta = const VerificationMeta('title');
@override
late final GeneratedColumn<String> title = GeneratedColumn<String>(
'title',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _bodyMeta = const VerificationMeta('body');
@override
late final GeneratedColumn<String> body = GeneratedColumn<String>(
'body',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt',
);
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at',
aliasedName,
false,
type: DriftSqlType.dateTime,
requiredDuringInsert: true,
);
static const VerificationMeta _updatedAtMeta = const VerificationMeta(
'updatedAt',
);
@override
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
'updated_at',
aliasedName,
false,
type: DriftSqlType.dateTime,
requiredDuringInsert: true,
);
static const VerificationMeta _sortIndexMeta = const VerificationMeta(
'sortIndex',
);
@override
late final GeneratedColumn<int> sortIndex = GeneratedColumn<int>(
'sort_index',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
);
@override
List<GeneratedColumn> get $columns => [
id,
title,
body,
createdAt,
updatedAt,
sortIndex,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'notes';
@override
VerificationContext validateIntegrity(
Insertable<DbNote> instance, {
bool isInserting = false,
}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('title')) {
context.handle(
_titleMeta,
title.isAcceptableOrUnknown(data['title']!, _titleMeta),
);
} else if (isInserting) {
context.missing(_titleMeta);
}
if (data.containsKey('body')) {
context.handle(
_bodyMeta,
body.isAcceptableOrUnknown(data['body']!, _bodyMeta),
);
} else if (isInserting) {
context.missing(_bodyMeta);
}
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
} else if (isInserting) {
context.missing(_createdAtMeta);
}
if (data.containsKey('updated_at')) {
context.handle(
_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
);
} else if (isInserting) {
context.missing(_updatedAtMeta);
}
if (data.containsKey('sort_index')) {
context.handle(
_sortIndexMeta,
sortIndex.isAcceptableOrUnknown(data['sort_index']!, _sortIndexMeta),
);
} else if (isInserting) {
context.missing(_sortIndexMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
DbNote map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return DbNote(
id: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}id'],
)!,
title: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}title'],
)!,
body: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}body'],
)!,
createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
)!,
updatedAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}updated_at'],
)!,
sortIndex: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}sort_index'],
)!,
);
}
@override
$NotesTable createAlias(String alias) {
return $NotesTable(attachedDatabase, alias);
}
}
class DbNote extends DataClass implements Insertable<DbNote> {
final int id;
final String title;
final String body;
final DateTime createdAt;
final DateTime updatedAt;
final int sortIndex;
const DbNote({
required this.id,
required this.title,
required this.body,
required this.createdAt,
required this.updatedAt,
required this.sortIndex,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['title'] = Variable<String>(title);
map['body'] = Variable<String>(body);
map['created_at'] = Variable<DateTime>(createdAt);
map['updated_at'] = Variable<DateTime>(updatedAt);
map['sort_index'] = Variable<int>(sortIndex);
return map;
}
NotesCompanion toCompanion(bool nullToAbsent) {
return NotesCompanion(
id: Value(id),
title: Value(title),
body: Value(body),
createdAt: Value(createdAt),
updatedAt: Value(updatedAt),
sortIndex: Value(sortIndex),
);
}
factory DbNote.fromJson(
Map<String, dynamic> json, {
ValueSerializer? serializer,
}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return DbNote(
id: serializer.fromJson<int>(json['id']),
title: serializer.fromJson<String>(json['title']),
body: serializer.fromJson<String>(json['body']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
sortIndex: serializer.fromJson<int>(json['sortIndex']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'title': serializer.toJson<String>(title),
'body': serializer.toJson<String>(body),
'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'sortIndex': serializer.toJson<int>(sortIndex),
};
}
DbNote copyWith({
int? id,
String? title,
String? body,
DateTime? createdAt,
DateTime? updatedAt,
int? sortIndex,
}) => DbNote(
id: id ?? this.id,
title: title ?? this.title,
body: body ?? this.body,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
sortIndex: sortIndex ?? this.sortIndex,
);
DbNote copyWithCompanion(NotesCompanion data) {
return DbNote(
id: data.id.present ? data.id.value : this.id,
title: data.title.present ? data.title.value : this.title,
body: data.body.present ? data.body.value : this.body,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
sortIndex: data.sortIndex.present ? data.sortIndex.value : this.sortIndex,
);
}
@override
String toString() {
return (StringBuffer('DbNote(')
..write('id: $id, ')
..write('title: $title, ')
..write('body: $body, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('sortIndex: $sortIndex')
..write(')'))
.toString();
}
@override
int get hashCode =>
Object.hash(id, title, body, createdAt, updatedAt, sortIndex);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is DbNote &&
other.id == this.id &&
other.title == this.title &&
other.body == this.body &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.sortIndex == this.sortIndex);
}
class NotesCompanion extends UpdateCompanion<DbNote> {
final Value<int> id;
final Value<String> title;
final Value<String> body;
final Value<DateTime> createdAt;
final Value<DateTime> updatedAt;
final Value<int> sortIndex;
const NotesCompanion({
this.id = const Value.absent(),
this.title = const Value.absent(),
this.body = const Value.absent(),
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
this.sortIndex = const Value.absent(),
});
NotesCompanion.insert({
this.id = const Value.absent(),
required String title,
required String body,
required DateTime createdAt,
required DateTime updatedAt,
required int sortIndex,
}) : title = Value(title),
body = Value(body),
createdAt = Value(createdAt),
updatedAt = Value(updatedAt),
sortIndex = Value(sortIndex);
static Insertable<DbNote> custom({
Expression<int>? id,
Expression<String>? title,
Expression<String>? body,
Expression<DateTime>? createdAt,
Expression<DateTime>? updatedAt,
Expression<int>? sortIndex,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (title != null) 'title': title,
if (body != null) 'body': body,
if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt,
if (sortIndex != null) 'sort_index': sortIndex,
});
}
NotesCompanion copyWith({
Value<int>? id,
Value<String>? title,
Value<String>? body,
Value<DateTime>? createdAt,
Value<DateTime>? updatedAt,
Value<int>? sortIndex,
}) {
return NotesCompanion(
id: id ?? this.id,
title: title ?? this.title,
body: body ?? this.body,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
sortIndex: sortIndex ?? this.sortIndex,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (title.present) {
map['title'] = Variable<String>(title.value);
}
if (body.present) {
map['body'] = Variable<String>(body.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = Variable<DateTime>(updatedAt.value);
}
if (sortIndex.present) {
map['sort_index'] = Variable<int>(sortIndex.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('NotesCompanion(')
..write('id: $id, ')
..write('title: $title, ')
..write('body: $body, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('sortIndex: $sortIndex')
..write(')'))
.toString();
}
}
abstract class _$AppDatabase extends GeneratedDatabase {
_$AppDatabase(QueryExecutor e) : super(e);
$AppDatabaseManager get managers => $AppDatabaseManager(this);
late final $NotesTable notes = $NotesTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [notes];
}
typedef $$NotesTableCreateCompanionBuilder =
NotesCompanion Function({
Value<int> id,
required String title,
required String body,
required DateTime createdAt,
required DateTime updatedAt,
required int sortIndex,
});
typedef $$NotesTableUpdateCompanionBuilder =
NotesCompanion Function({
Value<int> id,
Value<String> title,
Value<String> body,
Value<DateTime> createdAt,
Value<DateTime> updatedAt,
Value<int> sortIndex,
});
class $$NotesTableFilterComposer extends Composer<_$AppDatabase, $NotesTable> {
$$NotesTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<int> get id => $composableBuilder(
column: $table.id,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get title => $composableBuilder(
column: $table.title,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get body => $composableBuilder(
column: $table.body,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<int> get sortIndex => $composableBuilder(
column: $table.sortIndex,
builder: (column) => ColumnFilters(column),
);
}
class $$NotesTableOrderingComposer
extends Composer<_$AppDatabase, $NotesTable> {
$$NotesTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<int> get id => $composableBuilder(
column: $table.id,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get title => $composableBuilder(
column: $table.title,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get body => $composableBuilder(
column: $table.body,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get sortIndex => $composableBuilder(
column: $table.sortIndex,
builder: (column) => ColumnOrderings(column),
);
}
class $$NotesTableAnnotationComposer
extends Composer<_$AppDatabase, $NotesTable> {
$$NotesTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<String> get title =>
$composableBuilder(column: $table.title, builder: (column) => column);
GeneratedColumn<String> get body =>
$composableBuilder(column: $table.body, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
GeneratedColumn<int> get sortIndex =>
$composableBuilder(column: $table.sortIndex, builder: (column) => column);
}
class $$NotesTableTableManager
extends
RootTableManager<
_$AppDatabase,
$NotesTable,
DbNote,
$$NotesTableFilterComposer,
$$NotesTableOrderingComposer,
$$NotesTableAnnotationComposer,
$$NotesTableCreateCompanionBuilder,
$$NotesTableUpdateCompanionBuilder,
(DbNote, BaseReferences<_$AppDatabase, $NotesTable, DbNote>),
DbNote,
PrefetchHooks Function()
> {
$$NotesTableTableManager(_$AppDatabase db, $NotesTable table)
: super(
TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
$$NotesTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
$$NotesTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
$$NotesTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
Value<int> id = const Value.absent(),
Value<String> title = const Value.absent(),
Value<String> body = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<DateTime> updatedAt = const Value.absent(),
Value<int> sortIndex = const Value.absent(),
}) => NotesCompanion(
id: id,
title: title,
body: body,
createdAt: createdAt,
updatedAt: updatedAt,
sortIndex: sortIndex,
),
createCompanionCallback:
({
Value<int> id = const Value.absent(),
required String title,
required String body,
required DateTime createdAt,
required DateTime updatedAt,
required int sortIndex,
}) => NotesCompanion.insert(
id: id,
title: title,
body: body,
createdAt: createdAt,
updatedAt: updatedAt,
sortIndex: sortIndex,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$NotesTableProcessedTableManager =
ProcessedTableManager<
_$AppDatabase,
$NotesTable,
DbNote,
$$NotesTableFilterComposer,
$$NotesTableOrderingComposer,
$$NotesTableAnnotationComposer,
$$NotesTableCreateCompanionBuilder,
$$NotesTableUpdateCompanionBuilder,
(DbNote, BaseReferences<_$AppDatabase, $NotesTable, DbNote>),
DbNote,
PrefetchHooks Function()
>;
class $AppDatabaseManager {
final _$AppDatabase _db;
$AppDatabaseManager(this._db);
$$NotesTableTableManager get notes =>
$$NotesTableTableManager(_db, _db.notes);
}
+80
View File
@@ -0,0 +1,80 @@
import 'package:notas/data/app_database.dart';
import 'package:notas/models/note.dart';
class NoteRepository {
NoteRepository({AppDatabase? database}) : _database = database ?? _sharedDatabase;
static final AppDatabase _sharedDatabase = AppDatabase();
final AppDatabase _database;
Future<List<Note>> loadNotes() async {
return _loadNotesFromDatabase();
}
Future<Note> createNote(Note note) async {
final int id = await _database.insertNoteAtTop(
NotesCompanion.insert(
title: note.title,
body: note.body,
createdAt: note.createdAt,
updatedAt: note.updatedAt,
sortIndex: 0,
),
);
return note.copyWith(id: id, index: 0);
}
Future<Note> updateNote(Note note) async {
final int noteId = note.id ?? (throw ArgumentError('Note id is required to update a note.'));
await _database.updateNoteRow(
DbNote(
id: noteId,
title: note.title,
body: note.body,
createdAt: note.createdAt,
updatedAt: note.updatedAt,
sortIndex: note.index,
),
);
return note;
}
Future<void> deleteNote(Note note) async {
final int noteId = note.id ?? (throw ArgumentError('Note id is required to delete a note.'));
await _database.deleteNoteAndShift(
id: noteId,
removedIndex: note.index,
);
}
Future<void> moveNote(Note note, int newIndex) async {
final int noteId = note.id ?? (throw ArgumentError('Note id is required to reorder a note.'));
await _database.moveNote(
id: noteId,
oldIndex: note.index,
newIndex: newIndex,
);
}
Future<List<Note>> _loadNotesFromDatabase() async {
final List<DbNote> rows = await _database.getAllNotes();
return rows.map(_fromRow).toList();
}
Note _fromRow(DbNote row) {
return Note(
id: row.id,
title: row.title,
body: row.body,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
index: row.sortIndex,
);
}
}