refactor: remove Objectbox plugin references and update tile caching implementation

This commit is contained in:
ZhuJHua
2025-03-02 07:05:48 +08:00
parent 3fb5573a7c
commit d992cc173d
10 changed files with 257 additions and 335 deletions

View File

@@ -1,145 +0,0 @@
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:latlong2/latlong.dart';
class ChinaRegions {
// 定义中国所有省份的瓦片区域
static final regions = {
'Beijing': RectangleRegion(
LatLngBounds(
const LatLng(39.4427, 115.4213), const LatLng(41.0599, 117.5143)),
),
'Tianjin': RectangleRegion(
LatLngBounds(
const LatLng(38.5547, 116.7257), const LatLng(40.2555, 118.0481)),
),
'Hebei': RectangleRegion(
LatLngBounds(
const LatLng(36.0513, 113.4620), const LatLng(42.6502, 119.8485)),
),
'Shanxi': RectangleRegion(
LatLngBounds(
const LatLng(34.4824, 110.1619), const LatLng(40.9982, 114.7482)),
),
'Inner Mongolia': RectangleRegion(
LatLngBounds(
const LatLng(37.4229, 97.1681), const LatLng(53.3318, 126.0386)),
),
'Liaoning': RectangleRegion(
LatLngBounds(
const LatLng(38.7216, 118.9151), const LatLng(43.4845, 125.5690)),
),
'Jilin': RectangleRegion(
LatLngBounds(
const LatLng(40.8624, 121.3644), const LatLng(46.6387, 131.2433)),
),
'Heilongjiang': RectangleRegion(
LatLngBounds(
const LatLng(43.4329, 121.1836), const LatLng(53.5581, 135.0865)),
),
'Shanghai': RectangleRegion(
LatLngBounds(
const LatLng(30.6647, 120.8526), const LatLng(31.8832, 122.1084)),
),
'Jiangsu': RectangleRegion(
LatLngBounds(
const LatLng(30.4505, 116.8286), const LatLng(35.1224, 121.1320)),
),
'Zhejiang': RectangleRegion(
LatLngBounds(
const LatLng(27.1934, 118.0219), const LatLng(31.0895, 122.2274)),
),
'Anhui': RectangleRegion(
LatLngBounds(
const LatLng(29.4119, 114.8926), const LatLng(34.6568, 119.6455)),
),
'Fujian': RectangleRegion(
LatLngBounds(
const LatLng(23.5636, 116.7407), const LatLng(28.4387, 120.8369)),
),
'Jiangxi': RectangleRegion(
LatLngBounds(
const LatLng(24.4124, 113.5877), const LatLng(30.0756, 118.6083)),
),
'Shandong': RectangleRegion(
LatLngBounds(
const LatLng(34.3944, 114.8054), const LatLng(38.3072, 122.6978)),
),
'Henan': RectangleRegion(
LatLngBounds(
const LatLng(31.3802, 110.3787), const LatLng(36.5819, 116.6942)),
),
'Hubei': RectangleRegion(
LatLngBounds(
const LatLng(29.0470, 108.4110), const LatLng(33.4238, 116.0797)),
),
'Hunan': RectangleRegion(
LatLngBounds(
const LatLng(24.6366, 108.7877), const LatLng(30.1238, 114.2341)),
),
'Guangdong': RectangleRegion(
LatLngBounds(
const LatLng(20.0268, 109.6642), const LatLng(25.5186, 117.2037)),
),
'Guangxi': RectangleRegion(
LatLngBounds(
const LatLng(20.5400, 104.4759), const LatLng(26.2504, 112.0472)),
),
'Hainan': RectangleRegion(
LatLngBounds(
const LatLng(18.1460, 108.5117), const LatLng(20.2706, 111.0257)),
),
'Chongqing': RectangleRegion(
LatLngBounds(
const LatLng(28.1124, 105.2875), const LatLng(32.1310, 110.4740)),
),
'Sichuan': RectangleRegion(
LatLngBounds(
const LatLng(26.0490, 97.3516), const LatLng(34.3143, 108.5740)),
),
'Guizhou': RectangleRegion(
LatLngBounds(
const LatLng(24.3965, 103.5901), const LatLng(29.2275, 109.2114)),
),
'Yunnan': RectangleRegion(
LatLngBounds(
const LatLng(21.1375, 97.6400), const LatLng(29.2227, 106.1124)),
),
'Tibet': RectangleRegion(
LatLngBounds(
const LatLng(26.0000, 78.3949), const LatLng(36.4538, 99.1089)),
),
'Shaanxi': RectangleRegion(
LatLngBounds(
const LatLng(31.7024, 105.4964), const LatLng(39.5957, 111.2605)),
),
'Gansu': RectangleRegion(
LatLngBounds(
const LatLng(32.1043, 92.1316), const LatLng(42.7932, 108.7309)),
),
'Qinghai': RectangleRegion(
LatLngBounds(
const LatLng(31.5875, 89.6198), const LatLng(39.7931, 101.8822)),
),
'Ningxia': RectangleRegion(
LatLngBounds(
const LatLng(35.3135, 104.1455), const LatLng(39.3749, 107.9047)),
),
'Xinjiang': RectangleRegion(
LatLngBounds(
const LatLng(34.2231, 73.4994), const LatLng(49.1720, 96.3877)),
),
'Hong Kong': RectangleRegion(
LatLngBounds(
const LatLng(22.1193, 113.8252), const LatLng(22.5645, 114.4462)),
),
'Macau': RectangleRegion(
LatLngBounds(
const LatLng(22.1092, 113.5286), const LatLng(22.2176, 113.6069)),
),
'Taiwan': RectangleRegion(
LatLngBounds(
const LatLng(20.5170, 119.4189), const LatLng(25.4152, 122.0184)),
),
};
}

View File

@@ -35,30 +35,42 @@ class WindowButtons extends StatelessWidget {
mouseOver: colorScheme.errorContainer,
iconMouseOver: colorScheme.onErrorContainer,
);
return GestureDetector(
onPanStart: (_) => appWindow.startDragging(),
behavior: HitTestBehavior.translucent,
child: SizedBox(
height: 46,
child: Visibility(
visible: Platform.isWindows,
child: Align(
alignment: Alignment.topRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
MinimizeWindowButton(
colors: secondaryButtonColors,
animate: true,
),
MaximizeWindowButton(
colors: primaryButtonColors,
animate: true,
),
CloseWindowButton(colors: closeButtonColors, animate: true),
],
var isMaximized = appWindow.isMaximized;
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
MinimizeWindowButton(colors: secondaryButtonColors, animate: true),
StatefulBuilder(
builder: (context, setState) {
return isMaximized
? RestoreWindowButton(
colors: primaryButtonColors,
animate: true,
onPressed: () {
appWindow.maximizeOrRestore();
setState(() {
isMaximized = !isMaximized;
});
},
)
: MaximizeWindowButton(
colors: primaryButtonColors,
animate: true,
onPressed: () {
appWindow.maximizeOrRestore();
setState(() {
isMaximized = !isMaximized;
});
},
);
},
),
),
CloseWindowButton(colors: closeButtonColors, animate: true),
],
),
),
);
@@ -86,7 +98,13 @@ class MoveTitle extends StatelessWidget {
return GestureDetector(
onPanStart: (_) => appWindow.startDragging(),
behavior: HitTestBehavior.translucent,
child: const SizedBox(height: 32),
child: SizedBox(
height: 32,
child: Visibility(
visible: Platform.isWindows || Platform.isLinux,
child: const WindowButtons(),
),
),
);
}
}

View File

@@ -6,7 +6,6 @@ import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/find_locale.dart';
@@ -46,8 +45,6 @@ Future<void> _initSystem() async {
macOS: true,
windows: true,
);
await FMTCObjectBoxBackend().initialise();
await const FMTCStore('mapStore').manage.create();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(

View File

@@ -1,14 +1,16 @@
import 'dart:io';
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';
import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:moodiary/components/base/button.dart';
import 'package:moodiary/components/bubble/bubble_view.dart';
import 'package:moodiary/main.dart';
import 'package:moodiary/utils/file_util.dart';
import 'package:moodiary/utils/http_util.dart';
import 'package:refreshed/refreshed.dart';
import 'map_logic.dart';
@@ -31,98 +33,131 @@ class MapPage extends StatelessWidget {
builder: (_) {
return state.currentLatLng != null && state.tiandituKey != null
? FlutterMap(
mapController: logic.mapController,
options: MapOptions(
initialCenter: state.currentLatLng!,
minZoom: 4.0,
initialZoom: 16.0,
maxZoom: 18.0),
children: [
TileLayer(
urlTemplate:
'http://t6.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${state.tiandituKey}',
tileProvider:
FMTCTileProvider(stores: const {'mapStore': null}),
tileSize: 256,
mapController: logic.mapController,
options: MapOptions(
initialCenter: state.currentLatLng!,
minZoom: 4.0,
initialZoom: 16.0,
maxZoom: 18.0,
),
children: [
TileLayer(
urlTemplate:
'http://t6.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${state.tiandituKey}',
tileProvider: CachedTileProvider(
store: HiveCacheStore(
FileUtil.getRealPath('hive_cache', ''),
hiveBoxName: 'HiveCache',
),
dio: HttpUtil().dio,
),
TileLayer(
urlTemplate:
'http://t6.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${state.tiandituKey}',
tileProvider:
FMTCTileProvider(stores: const {'mapStore': null}),
tileSize: 256,
tileSize: 256,
),
TileLayer(
urlTemplate:
'http://t6.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${state.tiandituKey}',
tileProvider: CachedTileProvider(
store: HiveCacheStore(
FileUtil.getRealPath('hive_cache', ''),
hiveBoxName: 'HiveCache',
),
dio: HttpUtil().dio,
),
MarkerClusterLayerWidget(
options: MarkerClusterLayerOptions(
markers: List.generate(
state.diaryMapItemList.length, (index) {
return Marker(
point: state.diaryMapItemList[index].latLng,
child: GestureDetector(
onTap: () async {
await logic.toDiaryPage(
isarId:
state.diaryMapItemList[index].id);
},
child: state.diaryMapItemList[index]
.coverImageName.isNotEmpty
? Bubble(
backgroundColor:
colorScheme.tertiary,
borderRadius: 8,
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(4),
image: DecorationImage(
image: FileImage(File(
FileUtil.getRealPath(
'image',
state
.diaryMapItemList[
index]
.coverImageName))),
fit: BoxFit.cover),
),
))
: FaIcon(
FontAwesomeIcons.locationDot,
color: colorScheme.tertiary,
),
),
width: state.diaryMapItemList[index]
.coverImageName.isNotEmpty
? 56
: 30,
height: state.diaryMapItemList[index]
.coverImageName.isNotEmpty
? 64
: 30);
}),
rotate: true,
maxZoom: 18.0,
forceIntegerZoomLevel: true,
showPolygon: false,
builder: (context, markers) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: colorScheme.tertiaryContainer,
border: Border.all(
color: colorScheme.tertiary, width: 2)),
child: Center(
child: Text(
markers.length.toString(),
style: TextStyle(
color: colorScheme.onTertiaryContainer),
),
),
tileSize: 256,
),
MarkerClusterLayerWidget(
options: MarkerClusterLayerOptions(
markers: List.generate(state.diaryMapItemList.length, (
index,
) {
return Marker(
point: state.diaryMapItemList[index].latLng,
child: GestureDetector(
onTap: () async {
await logic.toDiaryPage(
isarId: state.diaryMapItemList[index].id,
);
})),
],
)
},
child:
state
.diaryMapItemList[index]
.coverImageName
.isNotEmpty
? Bubble(
backgroundColor: colorScheme.tertiary,
borderRadius: 8,
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
4,
),
image: DecorationImage(
image: FileImage(
File(
FileUtil.getRealPath(
'image',
state
.diaryMapItemList[index]
.coverImageName,
),
),
),
fit: BoxFit.cover,
),
),
),
)
: FaIcon(
FontAwesomeIcons.locationDot,
color: colorScheme.tertiary,
),
),
width:
state
.diaryMapItemList[index]
.coverImageName
.isNotEmpty
? 56
: 30,
height:
state
.diaryMapItemList[index]
.coverImageName
.isNotEmpty
? 64
: 30,
);
}),
rotate: true,
maxZoom: 18.0,
forceIntegerZoomLevel: true,
showPolygon: false,
builder: (context, markers) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: colorScheme.tertiaryContainer,
border: Border.all(
color: colorScheme.tertiary,
width: 2,
),
),
child: Center(
child: Text(
markers.length.toString(),
style: TextStyle(
color: colorScheme.onTertiaryContainer,
),
),
),
);
},
),
),
],
)
: const Center(child: CircularProgressIndicator());
},
),

View File

@@ -10,30 +10,33 @@ import 'notice_util.dart';
class HttpUtil {
Dio? _dio;
final bool _enableLogging = false;
final bool _enableLogging = kDebugMode;
Dio get dio {
if (_dio == null) {
_dio = Dio(BaseOptions(connectTimeout: const Duration(seconds: 5)));
_dio!.interceptors.add(InterceptorsWrapper(
onError: (error, handler) {
NoticeUtil.showToast('网络异常!');
handler.next(error);
},
onRequest: (options, handler) {
if (_enableLogging) {
LogUtil.printInfo('Request: ${options.method} ${options.path}');
}
handler.next(options);
},
onResponse: (response, handler) {
if (_enableLogging) {
LogUtil.printInfo(
'Response [${response.statusCode}]: ${response.data}');
}
handler.next(response);
},
));
_dio?.interceptors.add(
InterceptorsWrapper(
onError: (error, handler) {
if (error.type != DioExceptionType.cancel) {
NoticeUtil.showToast('Network Error ${error.error}');
}
handler.next(error);
},
onRequest: (options, handler) {
if (_enableLogging) {
LogUtil.printInfo('Request: ${options.method} ${options.path}');
}
handler.next(options);
},
onResponse: (response, handler) {
if (_enableLogging) {
LogUtil.printInfo('Response ${response.statusCode}');
}
handler.next(response);
},
),
);
}
return _dio!;
}
@@ -67,27 +70,46 @@ class HttpUtil {
);
}
Future<Response<T>> get<T>(String path,
{Map<String, dynamic>? parameters, ResponseType? type}) {
Future<Response<T>> get<T>(
String path, {
Map<String, dynamic>? parameters,
ResponseType? type,
}) {
return _request(path, method: 'GET', parameters: parameters, type: type);
}
Future<Response<T>> post<T>(String path,
{Map<String, dynamic>? header, data, Options? option}) {
return _request(path,
method: 'POST', header: header, data: data, option: option);
Future<Response<T>> post<T>(
String path, {
Map<String, dynamic>? header,
data,
Options? option,
}) {
return _request(
path,
method: 'POST',
header: header,
data: data,
option: option,
);
}
Future<Stream<String>?> postStream(String path,
{Map<String, dynamic>? header, Object? data}) async {
final Response<ResponseBody> response = await dio.post(path,
options: Options(responseType: ResponseType.stream, headers: header),
data: data);
Future<Stream<String>?> postStream(
String path, {
Map<String, dynamic>? header,
Object? data,
}) async {
final Response<ResponseBody> response = await dio.post(
path,
options: Options(responseType: ResponseType.stream, headers: header),
data: data,
);
final StreamTransformer<Uint8List, List<int>> transformer =
StreamTransformer.fromHandlers(handleData: (data, sink) {
sink.add(List<int>.from(data));
});
StreamTransformer.fromHandlers(
handleData: (data, sink) {
sink.add(List<int>.from(data));
},
);
return response.data?.stream
.transform(transformer)
.transform(const Utf8Decoder())

View File

@@ -27,7 +27,6 @@ import macos_window_utils
import media_kit_libs_macos_video
import media_kit_video
import network_info_plus
import objectbox_flutter_libs
import package_info_plus
import path_provider_foundation
import quill_native_bridge_macos
@@ -64,7 +63,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin"))
ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
QuillNativeBridgePlugin.register(with: registry.registrar(forPlugin: "QuillNativeBridgePlugin"))

View File

@@ -558,6 +558,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.8.0+1"
dio_cache_interceptor:
dependency: transitive
description:
name: dio_cache_interceptor
sha256: "1346705a2057c265014d7696e3e2318b560bfb00b484dac7f9b01e2ceaebb07d"
url: "https://pub.dev"
source: hosted
version: "3.5.1"
dio_cache_interceptor_hive_store:
dependency: "direct main"
description:
name: dio_cache_interceptor_hive_store
sha256: "127cda24a485f5b6469172edd8b81fac819147318bedd654be167527c498bf55"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
dio_web_adapter:
dependency: transitive
description:
@@ -710,14 +726,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.70.2"
flat_buffers:
dependency: transitive
description:
name: flat_buffers
sha256: "380bdcba5664a718bfd4ea20a45d39e13684f5318fcd8883066a55e21f37f4c3"
url: "https://pub.dev"
source: hosted
version: "23.5.26"
flutter:
dependency: "direct main"
description: flutter
@@ -952,6 +960,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.2"
flutter_map_cache:
dependency: "direct main"
description:
name: flutter_map_cache
sha256: "5b30c9b0d36315a22f4ee070737104a6017e7ff990e8addc8128ba81786e03ef"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
flutter_map_marker_cluster:
dependency: "direct main"
description:
@@ -968,14 +984,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
flutter_map_tile_caching:
dependency: "direct main"
description:
name: flutter_map_tile_caching
sha256: "1839c6157cf9b444083a626b30f3ba9f6db802ac8bb5292440e1628882faa392"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
flutter_markdown:
dependency: "direct main"
description:
@@ -1242,6 +1250,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
hive_ce:
dependency: transitive
description:
name: hive_ce
sha256: ac66daee46ad46486a1ed12cf91e9d7479c875fb46889be8d2c96b557406647f
url: "https://pub.dev"
source: hosted
version: "2.10.1"
html:
dependency: transitive
description:
@@ -1737,22 +1753,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.0"
objectbox:
dependency: transitive
description:
name: objectbox
sha256: "3d1cb5f9aa564f95c76ba251299f6cb1591c3dd8ff05fd76fa0549d899d9fe31"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
objectbox_flutter_libs:
dependency: transitive
description:
name: objectbox_flutter_libs
sha256: "4f54ebbd7a3b72f1a5ef4fea76cb01cc36440a4cac1f63bfb6719afba400eedb"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
octo_image:
dependency: transitive
description:

View File

@@ -61,8 +61,9 @@ dependencies:
cross_file: 0.3.4+2
fc_native_video_thumbnail: 0.17.2
flutter_map: 7.0.2
flutter_map_tile_caching: 10.0.0
flutter_map_cache: 1.5.2
flutter_map_marker_cluster: 1.4.0
dio_cache_interceptor_hive_store: 4.0.0
latlong2: 0.9.1
shelf: 1.4.2
shelf_multipart: 2.0.1

View File

@@ -21,7 +21,6 @@
#include <local_auth_windows/local_auth_plugin.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <objectbox_flutter_libs/objectbox_flutter_libs_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <record_windows/record_windows_plugin_c_api.h>
#include <rive_common/rive_plugin.h>
@@ -60,8 +59,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
ObjectboxFlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ObjectboxFlutterLibsPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
RecordWindowsPluginCApiRegisterWithRegistrar(

View File

@@ -18,7 +18,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
local_auth_windows
media_kit_libs_windows_video
media_kit_video
objectbox_flutter_libs
permission_handler_windows
record_windows
rive_common