Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support IMap and IMapView #530

Merged
merged 36 commits into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
930d179
Organize imports
halildurmus Jul 15, 2022
6521269
Add `PedometerStepKind` enum
halildurmus Jul 15, 2022
ca7fa45
Update `GUIDFromString` method
halildurmus Jul 15, 2022
85805c8
Add `isSimilarType` helper method
halildurmus Jul 15, 2022
59a09bd
Add `IMap<K, V>` IIDs
halildurmus Jul 15, 2022
558d110
Override `==` operator and `hashCode` for `GUID`
halildurmus Jul 15, 2022
9d03586
Add helper methods
halildurmus Jul 15, 2022
bc5bdc4
Do not free `retValuePtr` for structs
halildurmus Jul 15, 2022
92ccaa3
Add `value` getter to `IPropertyValue`
halildurmus Jul 15, 2022
731f183
Project `IKeyValuePair`
halildurmus Jul 15, 2022
7e60bb9
Add `toMap` helper method
halildurmus Jul 15, 2022
320d85d
Project `IMapView`
halildurmus Jul 15, 2022
2682f68
Project `IMap`
halildurmus Jul 15, 2022
572b87a
Add `PropertySet`, `StringMap`, `ValueSet` and `MediaPropertySet`
halildurmus Jul 15, 2022
b6442a8
Update `excludedWindowsRuntimeTypes`
halildurmus Jul 15, 2022
679e209
Update exports
halildurmus Jul 15, 2022
1002117
Add factory constructors to `IMap`
halildurmus Jul 15, 2022
e564c6f
Expose the APIs that return `IMap` or `IMapView` correctly
halildurmus Jul 15, 2022
cd5a2be
Generate `NotificationData`
halildurmus Jul 15, 2022
4513225
There is no need to skip the test with the recent change to `getGuid()`
halildurmus Jul 15, 2022
5ec4a88
Add new TODOs
halildurmus Jul 15, 2022
87015c1
Add `isSupportedKeyValuePair` helper method
halildurmus Jul 16, 2022
6f4bb6e
Improve documentation
halildurmus Jul 16, 2022
2bef102
Update documentation
halildurmus Jul 16, 2022
7b08ecb
Use `isTrue`, `isFalse` matchers
halildurmus Jul 16, 2022
dcefcd5
Add tests
halildurmus Jul 16, 2022
6f0251b
Add tests for `IMapView<String, String?>`
halildurmus Jul 16, 2022
142a590
Simplify generic type check
halildurmus Jul 17, 2022
2e6c716
Return unmodifiable map
halildurmus Jul 17, 2022
23ccda3
Create an extension method to get the value stored in the IPropertyValue
halildurmus Jul 20, 2022
953ed0c
Add `PedometerReading`
halildurmus Jul 20, 2022
030b2bc
Create extension methods to support key types of `PedometerStepKind`
halildurmus Jul 20, 2022
2e47dc2
Make sure type is WinRT type
halildurmus Jul 20, 2022
b4f6ecc
Merge branch 'main' into map-mapview
halildurmus Jul 23, 2022
209eda6
Move helper functions to `ipropertyvalue_helpers.dart`
halildurmus Jul 23, 2022
6ef6068
Add `allocator` parameter to PropertySet
halildurmus Jul 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 17 additions & 1 deletion lib/src/guid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,22 @@ class GUID extends Struct {
Data4 = (int.parse(rawString.substring(0, 4), radix: 16) << 48) +
int.parse(rawString.substring(4, 16), radix: 16);
}

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is GUID &&
other.Data1 == Data1 &&
other.Data2 == Data2 &&
other.Data3 == Data3 &&
other.Data4 == Data4;
}

@override
int get hashCode =>
Data1.hashCode ^ Data2.hashCode ^ Data3.hashCode ^ Data4.hashCode;
Comment on lines +89 to +102
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These overrides are required for Map<GUID, XX> types (e.g. Map<GUID, Object?>) to work properly.

}

Pointer<GUID> GUIDFromString(String guid) => calloc<GUID>()..ref.setGUID(guid);
Pointer<GUID> GUIDFromString(String guid, {Allocator allocator = calloc}) =>
allocator<GUID>()..ref.setGUID(guid);
30 changes: 30 additions & 0 deletions lib/src/winrt/devices/sensors/enums.g.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Dart representations of common enumerations used in the Windows Runtime APIs.

// THIS FILE IS GENERATED AUTOMATICALLY AND SHOULD NOT BE EDITED DIRECTLY.

// ignore_for_file: constant_identifier_names

import '../../../winrt/foundation/winrt_enum.dart';

/// The type of step taken according to the pedometer.
///
/// {@category Enum}
enum PedometerStepKind implements WinRTEnum {
unknown(0),
walking(1),
running(2);

@override
final int value;

const PedometerStepKind(this.value);

factory PedometerStepKind.from(int value) =>
PedometerStepKind.values.firstWhere((e) => e.value == value,
orElse: () => throw ArgumentError.value(
value, 'value', 'No enum value with that value'));
}
278 changes: 278 additions & 0 deletions lib/src/winrt/foundation/collections/ikeyvaluepair.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// ikeyvaluepair.dart

// ignore_for_file: constant_identifier_names, non_constant_identifier_names

import 'dart:ffi';

import 'package:ffi/ffi.dart';

import '../../../api_ms_win_core_winrt_string_l1_1_0.dart';
import '../../../com/iinspectable.dart';
import '../../../combase.dart';
import '../../../exceptions.dart';
import '../../../guid.dart';
import '../../../macros.dart';
import '../../../types.dart';
import '../../../utils.dart';
import '../../../winrt_helpers.dart';
import '../../devices/sensors/enums.g.dart';
import '../../internal/map_helpers.dart';
import '../ipropertyvalue.dart';

/// Represents a key-value pair.
///
/// This is typically used as a constraint type when you need to encapsulate
/// two type parameters into one to satisfy the constraints of another generic
/// interface.
///
/// {@category Interface}
/// {@category winrt}
class IKeyValuePair<K, V> extends IInspectable {
// vtable begins at 6, is 2 entries long.
final V Function(Pointer<COMObject>)? _creator;
final V Function(int)? _enumCreator;

/// Creates an instance of [IKeyValuePair] using the given [ptr].
///
/// [K] must be of type `GUID`, `int`, `Object`, `String`, or `WinRTEnum`
/// (e.g. `PedometerStepKind`).
///
/// [V] must be of type `Object`, `String`, or `WinRT` (e.g. `IJsonValue`,
/// `ProductLicense`).
///
/// [creator] must be specified if [V] is a `WinRT` type.
/// ```dart
/// final keyValuePair =
/// IKeyValuePair<String, IJsonValue?>.fromRawPointer(ptr,
/// creator: IJsonValue.fromRawPointer);
/// ```
///
/// [enumCreator] must be specified if [V] is a `WinRTEnum` type.
/// ```dart
/// final keyValuePair =
/// IKeyValuePair<String, ChatMessageStatus?>.fromRawPointer(ptr,
/// enumCreator: ChatMessageStatus.from);
/// ```
IKeyValuePair.fromRawPointer(
super.ptr, {
V Function(Pointer<COMObject>)? creator,
V Function(int)? enumCreator,
}) : _creator = creator,
_enumCreator = enumCreator {
if (!isSupportedKeyValuePair<K, V>()) {
throw ArgumentError('Unsupported key-value pair: IKeyValuePair<$K, $V>');
}

if (isSubtypeOfInspectable<V>() && creator == null) {
throw ArgumentError.notNull('creator');
}

if (isSubtypeOfWinRTEnum<V>() && enumCreator == null) {
throw ArgumentError.notNull('enumCreator');
}
}

/// Gets the key of the key-value pair.
K get key {
if (isSameType<K, GUID>()) return _key_GUID as K;
if (isSameType<K, int>()) return _key_Uint32 as K;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used Uint32 for int keys since it is the only int type to be supported for IKeyValuePair: https://github.com/timsneath/win32/blob/339c6e4ac04a9d2f2c1933c958bc0893be65dee2/lib/src/winrt_constants.dart#L147-L148

if (isSameType<K, PedometerStepKind>()) {
return _key_PedometerStepKind as K;
}
if (isSameType<K, String>()) return _key_String as K;

return _key_Object;
}

GUID get _key_GUID {
final retValuePtr = calloc<GUID>();

final hr = ptr.ref.lpVtbl.value
.elementAt(6)
.cast<
Pointer<NativeFunction<HRESULT Function(Pointer, Pointer<GUID>)>>>()
.value
.asFunction<
int Function(
Pointer, Pointer<GUID>)>()(ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return retValuePtr.ref;
}

int get _key_Uint32 {
final retValuePtr = calloc<Uint32>();

try {
final hr = ptr.ref.lpVtbl.value
.elementAt(6)
.cast<
Pointer<
NativeFunction<HRESULT Function(Pointer, Pointer<Uint32>)>>>()
.value
.asFunction<
int Function(
Pointer, Pointer<Uint32>)>()(ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return retValuePtr.value;
} finally {
free(retValuePtr);
}
}

PedometerStepKind get _key_PedometerStepKind {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgive me, but this seems weird here. Why would such a specific type as PedometerStepKind be embedded in a generic collection object like this? There could be hundreds of these, couldn't there? What if we decide (as is likely) to partition Windows.Device.Sensors into a different export from Windows.Foundation.Collections?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are the supported key-value pairs of IKeyValuePair according to IIDs taken from here:
https://github.com/timsneath/win32/blob/339c6e4ac04a9d2f2c1933c958bc0893be65dee2/lib/src/winrt_constants.dart#L62-L148

According to these, there is only one enum type (PedometerStepKind) used as a key type of IKeyValuePair. It seemed easier to handle it this way instead of adding a new parameter (keyCreator) that takes an enum constructor for the key type of ÌKeyValuePair which will allow me to create an instance of that enum type.

I can modify the implementation to handle these types with the keyCreator parameter I mentioned above if that's what you want.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the best thing is to make the PedometerStepKind pieces an extension method in winrt/device/sensors? I don't know that this is the right answer, but it does feel like a very odd outlier. Are there other classes that might have a similar pattern, I wonder?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the best thing is to make the PedometerStepKind pieces an extension method in winrt/device/sensors?

I've created extension methods for the PedometerStepKind pieces but I'm not sure if it's the way you wanted it. Could you take a look?

Are there other classes that might have a similar pattern, I wonder?

I don't think there are.

final retValuePtr = calloc<Int32>();

try {
final hr = ptr.ref.lpVtbl.value
.elementAt(6)
.cast<
Pointer<
NativeFunction<HRESULT Function(Pointer, Pointer<Int32>)>>>()
.value
.asFunction<
int Function(
Pointer, Pointer<Int32>)>()(ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return PedometerStepKind.from(retValuePtr.value);
} finally {
free(retValuePtr);
}
}

String get _key_String {
final retValuePtr = calloc<HSTRING>();

try {
final hr = ptr.ref.lpVtbl.value
.elementAt(6)
.cast<
Pointer<
NativeFunction<
HRESULT Function(Pointer, Pointer<HSTRING>)>>>()
.value
.asFunction<int Function(Pointer, Pointer<HSTRING>)>()(
ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

final retValue = retValuePtr.toDartString();
return retValue;
} finally {
WindowsDeleteString(retValuePtr.value);
free(retValuePtr);
}
}

K get _key_Object {
final retValuePtr = calloc<COMObject>();

final hr = ptr.ref.lpVtbl.value
.elementAt(6)
.cast<
Pointer<
NativeFunction<
HRESULT Function(Pointer, Pointer<COMObject>)>>>()
.value
.asFunction<int Function(Pointer, Pointer<COMObject>)>()(
ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return IPropertyValue.fromRawPointer(retValuePtr).value as K;
}

/// Gets the value of the key-value pair.
V get value {
if (isSimilarType<V, String>()) return _value_String as V;
if (isSubtypeOfInspectable<V>()) return _value_COMObject;
if (isSubtypeOfWinRTEnum<V>()) return _value_enum;

return _value_Object as V;
}

V get _value_COMObject {
final retValuePtr = calloc<COMObject>();

final hr = ptr.ref.lpVtbl.value
.elementAt(7)
.cast<
Pointer<
NativeFunction<
HRESULT Function(Pointer, Pointer<COMObject>)>>>()
.value
.asFunction<int Function(Pointer, Pointer<COMObject>)>()(
ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return _creator!(retValuePtr);
}

V get _value_enum {
final retValuePtr = calloc<Int32>();
Comment on lines +195 to +196
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used Int32 here for enum values since all supported enum types for IKeyValuePair are Int32 type (e.g. AppCapabilityAccessStatus, ChatMessageStatus).


final hr = ptr.ref.lpVtbl.value
.elementAt(7)
.cast<
Pointer<
NativeFunction<HRESULT Function(Pointer, Pointer<Int32>)>>>()
.value
.asFunction<
int Function(
Pointer, Pointer<Int32>)>()(ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return _enumCreator!(retValuePtr.value);
}

Object? get _value_Object {
final retValuePtr = calloc<COMObject>();

final hr = ptr.ref.lpVtbl.value
.elementAt(7)
.cast<
Pointer<
NativeFunction<
HRESULT Function(Pointer, Pointer<COMObject>)>>>()
.value
.asFunction<int Function(Pointer, Pointer<COMObject>)>()(
ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

return IPropertyValue.fromRawPointer(retValuePtr).value;
}

String? get _value_String {
final retValuePtr = calloc<HSTRING>();

try {
final hr = ptr.ref.lpVtbl.value
.elementAt(7)
.cast<
Pointer<
NativeFunction<
HRESULT Function(Pointer, Pointer<HSTRING>)>>>()
.value
.asFunction<int Function(Pointer, Pointer<HSTRING>)>()(
ptr.ref.lpVtbl, retValuePtr);

if (FAILED(hr)) throw WindowsException(hr);

if (retValuePtr.value == 0) return null;

final retValue = retValuePtr.toDartString();
return retValue;
} finally {
WindowsDeleteString(retValuePtr.value);
free(retValuePtr);
}
}
}