From 5f2b1b4de71f5ec5af13392f64eaf3973d43f500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 20 Nov 2019 18:25:55 +0000 Subject: [PATCH] Code imported from .NETMF for use in nanoFramework (#2) --- .github_changelog_generator | 14 + .gitignore | 255 ++ CHANGELOG.md | 0 CODE_OF_CONDUCT.md | 3 + CONTRIBUTING.md | 3 + LICENSE | 201 ++ azure-pipelines.yml | 66 +- source/NuGet.Config | 7 + ...Framework.Graphics.DELIVERABLES.Wpf.nuspec | 35 + source/nanoFramework.Graphics.Wpf.nuspec | 38 + source/nanoFramework.Graphics.Wpf.sln | 27 + .../Core/Input/ButtonDevice.cs | 528 +++ .../Core/Input/ButtonEventArgs.cs | 71 + .../Core/Input/ButtonEventHandler.cs | 15 + .../Core/Input/ButtonState.cs | 36 + .../Core/Input/Buttons.cs | 126 + .../Core/Input/FocusChangedEventArgs.cs | 51 + .../Core/Input/FocusChangedEventHandler.cs | 15 + .../Core/Input/GenericDevice.cs | 132 + .../Core/Input/InputDevice.cs | 37 + .../Core/Input/InputEventArgs.cs | 55 + .../Core/Input/InputEventHandler.cs | 15 + .../Core/Input/InputManager.cs | 499 +++ .../Core/Input/InputProviderSite.cs | 117 + .../Core/Input/InputReport.cs | 82 + .../Core/Input/InputReportEventArgs.cs | 43 + .../Core/Input/InputReportEventHandler.cs | 16 + .../Core/Input/NotifyInputEventArgs.cs | 44 + .../Core/Input/PreProcessInputEventArgs.cs | 43 + .../Core/Input/PreProcessInputEventHandler.cs | 16 + .../Core/Input/ProcessInputEventArgs.cs | 116 + .../Core/Input/ProcessInputEventHandler.cs | 16 + .../Core/Input/RawButtonInputReport.cs | 76 + .../Core/Input/RawGenericInputReport.cs | 65 + .../Core/Input/RawTouchInputReport.cs | 103 + .../Core/Input/StagingAreaInputItem.cs | 84 + .../Core/Input/Touch.cs | 178 + .../Core/Input/TouchDevice.cs | 105 + .../Core/Presentation/Controls/Border.cs | 171 + .../Core/Presentation/Controls/Canvas.cs | 218 ++ .../Presentation/Controls/ContentControl.cs | 63 + .../Core/Presentation/Controls/Control.cs | 108 + .../Core/Presentation/Controls/DockPanel.cs | 232 ++ .../Core/Presentation/Controls/Image.cs | 90 + .../Core/Presentation/Controls/InkCanvas.cs | 192 + .../Core/Presentation/Controls/ListBox.cs | 300 ++ .../Core/Presentation/Controls/ListBoxItem.cs | 72 + .../Controls/ListBoxItemCollection.cs | 184 + .../Core/Presentation/Controls/Orientation.cs | 27 + .../Core/Presentation/Controls/Panel.cs | 56 + .../Controls/ScrollChangedEventArgs.cs | 53 + .../Controls/ScrollChangedEventHandler.cs | 17 + .../Presentation/Controls/ScrollViewer.cs | 383 ++ .../Presentation/Controls/ScrollingStyle.cs | 36 + .../Controls/SelectionChangedEventArgs.cs | 40 + .../Controls/SelectionChangedEventHandler.cs | 17 + .../Core/Presentation/Controls/StackPanel.cs | 148 + .../Core/Presentation/Controls/Text.cs | 264 ++ .../Core/Presentation/Controls/TextFlow.cs | 486 +++ .../Core/Presentation/Controls/TextRun.cs | 194 ++ .../Controls/TextRunCollection.cs | 205 ++ .../Core/Presentation/Controls/WrapPanel.cs | 265 ++ .../Core/Presentation/HorizontalAlignment.cs | 36 + .../Core/Presentation/LayoutManager.cs | 452 +++ .../Core/Presentation/Media/Brush.cs | 89 + .../Core/Presentation/Media/Color.cs | 628 ++++ .../Core/Presentation/Media/Constants.cs | 21 + .../Core/Presentation/Media/DrawingContext.cs | 558 +++ .../Core/Presentation/Media/ImageBrush.cs | 68 + .../Presentation/Media/LinearGradientBrush.cs | 112 + .../Core/Presentation/Media/MediaContext.cs | 275 ++ .../Core/Presentation/Media/Pen.cs | 46 + .../Presentation/Media/SolidColorBrush.cs | 349 ++ .../Core/Presentation/Media/Stretch.cs | 26 + .../Core/Presentation/Media/TextAlignment.cs | 31 + .../Core/Presentation/Media/TextTrimming.cs | 31 + .../Core/Presentation/PresentationSource.cs | 89 + .../Core/Presentation/Shapes/Ellipse.cs | 47 + .../Core/Presentation/Shapes/Line.cs | 96 + .../Core/Presentation/Shapes/Polygon.cs | 71 + .../Core/Presentation/Shapes/Rectangle.cs | 52 + .../Core/Presentation/Shapes/Shape.cs | 67 + .../Core/Presentation/SizeToContent.cs | 34 + .../Core/Presentation/UIElement.cs | 2116 +++++++++++ .../Core/Presentation/UIElementCollection.cs | 734 ++++ .../Core/Presentation/VerticalAlignment.cs | 38 + .../Core/Presentation/Visibility.cs | 31 + .../Core/Presentation/Window.cs | 314 ++ .../Core/Presentation/WindowManager.cs | 161 + .../Core/System/Application.cs | 1059 ++++++ .../Core/System/EventHandler.cs | 40 + .../Core/System/EventRoute.cs | 197 ++ .../Core/System/PropertyChangedEventArgs.cs | 50 + .../System/PropertyChangedEventHandler.cs | 16 + .../Core/System/RouteItem.cs | 90 + .../Core/System/RoutedEvent.cs | 130 + .../Core/System/RoutedEventArgs.cs | 271 ++ .../Core/System/RoutedEventHandlerInfo.cs | 129 + .../Core/System/RoutingStrategy.cs | 44 + .../Core/System/SystemMetrics.cs | 76 + .../Core/System/Threading/Dispatcher.cs | 626 ++++ .../Core/System/Threading/DispatcherFrame.cs | 82 + .../Core/System/Threading/DispatcherObject.cs | 110 + .../System/Threading/DispatcherOperation.cs | 327 ++ .../Threading/DispatcherOperationStatus.cs | 37 + .../Core/System/Threading/DispatcherTimer.cs | 231 ++ .../Core/System/WindowCollection.cs | 206 ++ .../Microsoft.SPOT.Graphics.xml | 3098 +++++++++++++++++ .../Native/Bitmap.cs | 536 +++ .../Native/ButtonEnum.cs | 433 +++ .../Native/DisplayControl.cs | 106 + .../nanoFramework.Graphics.Wpf/Native/Font.cs | 170 + .../Native/GenericEventEx.cs | 26 + .../nanoFramework.Graphics.Wpf/Native/Ink.cs | 40 + .../Native/Mathematics.cs | 44 + .../Native/TouchCollector.cs | 250 ++ .../Native/TouchInterface.cs | 277 ++ .../Native/TouchPanel.cs | 81 + .../Native/TouchScreen.cs | 267 ++ .../Native/temporary.cs | 31 + .../Properties/AssemblyInfo.cs | 15 + source/nanoFramework.Graphics.Wpf/app.config | 15 + source/nanoFramework.Graphics.Wpf/key.snk | Bin 0 -> 596 bytes .../nanoFramework.Graphics.Wpf.nfproj | 212 ++ .../nanoFramework.Graphics.Wpf.nfproj~HEAD | 230 ++ .../packages.config | 8 + source/readme.txt | 21 + source/version.json | 24 + template.vssettings | 74 + 129 files changed, 23392 insertions(+), 13 deletions(-) create mode 100644 .github_changelog_generator create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 source/NuGet.Config create mode 100644 source/nanoFramework.Graphics.DELIVERABLES.Wpf.nuspec create mode 100644 source/nanoFramework.Graphics.Wpf.nuspec create mode 100644 source/nanoFramework.Graphics.Wpf.sln create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/ButtonDevice.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/ButtonState.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/Buttons.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/GenericDevice.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputDevice.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputManager.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputProviderSite.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputReport.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/NotifyInputEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/RawButtonInputReport.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/RawGenericInputReport.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/RawTouchInputReport.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/StagingAreaInputItem.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/Touch.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Input/TouchDevice.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Border.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Canvas.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ContentControl.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Control.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/DockPanel.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Image.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/InkCanvas.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBox.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItem.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItemCollection.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Orientation.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Panel.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollViewer.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollingStyle.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/StackPanel.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Text.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextFlow.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRun.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRunCollection.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/WrapPanel.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/HorizontalAlignment.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/LayoutManager.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Brush.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Color.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Constants.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/DrawingContext.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/ImageBrush.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/LinearGradientBrush.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/MediaContext.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Pen.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/SolidColorBrush.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Stretch.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextAlignment.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextTrimming.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/PresentationSource.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Ellipse.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Line.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Polygon.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Rectangle.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Shape.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/SizeToContent.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElement.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElementCollection.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/VerticalAlignment.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Visibility.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/Window.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/Presentation/WindowManager.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Application.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/EventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/EventRoute.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventHandler.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/RouteItem.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/RoutedEvent.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventArgs.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventHandlerInfo.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/RoutingStrategy.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/SystemMetrics.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Threading/Dispatcher.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherFrame.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherObject.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperation.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperationStatus.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherTimer.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Core/System/WindowCollection.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Microsoft.SPOT.Graphics.xml create mode 100644 source/nanoFramework.Graphics.Wpf/Native/Bitmap.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/ButtonEnum.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/DisplayControl.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/Font.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/GenericEventEx.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/Ink.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/Mathematics.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/TouchCollector.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/TouchInterface.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/TouchPanel.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/TouchScreen.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Native/temporary.cs create mode 100644 source/nanoFramework.Graphics.Wpf/Properties/AssemblyInfo.cs create mode 100644 source/nanoFramework.Graphics.Wpf/app.config create mode 100644 source/nanoFramework.Graphics.Wpf/key.snk create mode 100644 source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj create mode 100644 source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj~HEAD create mode 100644 source/nanoFramework.Graphics.Wpf/packages.config create mode 100644 source/readme.txt create mode 100644 source/version.json create mode 100644 template.vssettings diff --git a/.github_changelog_generator b/.github_changelog_generator new file mode 100644 index 0000000..fc0b36a --- /dev/null +++ b/.github_changelog_generator @@ -0,0 +1,14 @@ +user=nanoframework +project=lib-nanoFramework.Graphics.Wpf +issues=true +add_issues_wo_labels=false +add_pr_wo_labels=false +add_issues_wo_labels=false +filter_issues_by_milestone=false +exclude_labels=Area: Config-and-Build,Area: Infrastructure-and-Organization,reverted +enhancement_labels=Type: enhancement +bug_labels=Type: bug +merge_prefix=**Documentation and other chores:** +unreleased_label=**Changes available only in 'Preview' NuGet packages:** +issue_line_labels=Breaking-Change,documentation +author=false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce3e1b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,255 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +#SoundCloud +*.sonarqube/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7feff0a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +Please refer to the contributor Code of Conduct at the Home repository [here](https://github.com/nanoframework/Home/blob/master/CODE_OF_CONDUCT.md. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5a6a6c1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to **nanoFramework** + +Please refer to the contribution guidelines at the Home repository [here](https://github.com/nanoframework/Home/blob/master/CONTRIBUTING.md). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index aa91291..ea8aa02 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,19 +1,59 @@ -# Starter pipeline -# Start with a minimal pipeline that you can customize to build and deploy your code. -# Add steps that build, run tests, deploy, and more: -# https://aka.ms/yaml - trigger: -- master + branches: + include: + - master + - develop + - release-/* + - refs/tags/* + paths: + exclude: + - CHANGELOG.md + - /*.md + - .gitignore + - appveyor.yml + # waiting for feature to become available + # tags: + # include: + # - v/* + +pr: + branches: + include: + - master + - develop + - release/* + autoCancel: true + +# add nf-tools repo to resources (for Azure Pipelines templates) +resources: + repositories: + - repository: templates + type: github + name: nanoframework/nf-tools + endpoint: nanoframework pool: - vmImage: 'ubuntu-latest' + vmImage: 'VS2017-Win2016' + +variables: + solution: '**/source/*.sln' + buildPlatform: 'Any CPU' + buildConfiguration: 'Release' + nugetPackageName: 'nanoFramework.Graphics.Wpf' + repoName: 'lib-nanoFramework.Graphics.Wpf' steps: -- script: echo Hello, world! - displayName: 'Run a one-line script' -- script: | - echo Add other tasks to build, test, and deploy your project. - echo See https://aka.ms/yaml - displayName: 'Run a multi-line script' +# step from template @ nf-tools repo +# all build, update and publish steps +- template: azure-pipelines-templates/class-lib-build.yml@templates + parameters: + sonarCloudProject: 'nanoframework_lib-nanoFramework.Graphics.Wpf' + +# step from template @ nf-tools repo +# report error +- template: azure-pipelines-templates/discord-webhook-task.yml@templates + parameters: + status: 'failure' + webhookUrl: '$(DiscordWebhook)' + message: '' diff --git a/source/NuGet.Config b/source/NuGet.Config new file mode 100644 index 0000000..ef0bacf --- /dev/null +++ b/source/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/source/nanoFramework.Graphics.DELIVERABLES.Wpf.nuspec b/source/nanoFramework.Graphics.DELIVERABLES.Wpf.nuspec new file mode 100644 index 0000000..c533256 --- /dev/null +++ b/source/nanoFramework.Graphics.DELIVERABLES.Wpf.nuspec @@ -0,0 +1,35 @@ + + + + nanoFramework.Graphics.Wpf.DELIVERABLES + $version$ + nanoFramework.Graphics.Wpf.WpfDELIVERABLES + nanoFramework project contributors + nanoFramework project contributors + false + + + false + https://github.com/nanoframework/lib-nanoFramework.Graphics.Wpf + https://secure.gravatar.com/avatar/97d0e092247f0716db6d4b47b7d1d1ad + Copyright (c) 2018 The nanoFramework project contributors + ** DON'T REFERENCE THIS PACKAGE ** Not meant for development. This package includes the deliverable artifacts of the nanoFramework.Graphics.Wpf assembly for nanoFramework. These are for testing purposes and for updating the native code base of the library. + nanoFramework.Graphics.Wpf.WpfDELIVERABLES is not meant for development. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf.nuspec b/source/nanoFramework.Graphics.Wpf.nuspec new file mode 100644 index 0000000..ca31e24 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf.nuspec @@ -0,0 +1,38 @@ + + + + nanoFramework.Graphics.Wpf + $version$ + nanoFramework.Graphics.Wpf + nanoFramework project contributors + nanoFramework project contributors + false + Apache-2.0 + + + false + https://github.com/nanoframework/lib-nanoFramework.Graphics.Wpf + https://secure.gravatar.com/avatar/97d0e092247f0716db6d4b47b7d1d1ad + + Copyright (c) 2019 The nanoFramework project contributors + This package includes the nanoFramework.Graphics.Wpf assembly for nanoFramework C# projects. +This package requires a target with nanoFramework.Graphics.Wpf v$nativeVersion$. + nanoFramework.Graphics.Wpf assembly for nanoFramework C# projects + nanoFramework C# csharp netnf nanoFramework.Graphics.Wpf + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf.sln b/source/nanoFramework.Graphics.Wpf.sln new file mode 100644 index 0000000..224432a --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29411.108 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.Graphics.Wpf", "nanoFramework.Graphics.Wpf\nanoFramework.Graphics.Wpf.nfproj", "{E30AE2AA-BED0-4AA8-AC22-04C274898894}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E30AE2AA-BED0-4AA8-AC22-04C274898894}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E30AE2AA-BED0-4AA8-AC22-04C274898894}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E30AE2AA-BED0-4AA8-AC22-04C274898894}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {E30AE2AA-BED0-4AA8-AC22-04C274898894}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E30AE2AA-BED0-4AA8-AC22-04C274898894}.Release|Any CPU.Build.0 = Release|Any CPU + {E30AE2AA-BED0-4AA8-AC22-04C274898894}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BB29E7B3-1F41-4CC3-89AE-511A089DD643} + EndGlobalSection +EndGlobal diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonDevice.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonDevice.cs new file mode 100644 index 0000000..9efa02f --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonDevice.cs @@ -0,0 +1,528 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +#define TRACK_BUTTON_STATE + +using nanoFramework.Presentation; +using nanoFramework.UI; +using System; + +namespace nanoFramework.UI.Input +{ + /// + /// The ButtonDevice class represents the button device to the + /// members of a context. + /// + public sealed class ButtonDevice : InputDevice + { + internal ButtonDevice(InputManager inputManager) + { + _inputManager = inputManager; + + _inputManager.InputDeviceEvents[(int)InputManager.InputDeviceType.Button].PreNotifyInput += new NotifyInputEventHandler(PreNotifyInput); + _inputManager.InputDeviceEvents[(int)InputManager.InputDeviceType.Button].PostProcessInput += new ProcessInputEventHandler(PostProcessInput); + + _isEnabledOrVisibleChangedEventHandler = new PropertyChangedEventHandler(OnIsEnabledOrVisibleChanged); + } + + /// + /// Returns the element that input from this device is sent to. + /// + public override UIElement Target + { + get + { + return _focus; + } + } + + /// + /// Device Type + /// + public override InputManager.InputDeviceType DeviceType + { + get + { + return InputManager.InputDeviceType.Button; + } + } + + /// + /// Focuses the button input on a particular element. + /// + /// + /// The element to focus the button pad on. + /// + /// Element focused to + public UIElement Focus(UIElement obj) + { + VerifyAccess(); + + bool forceToNullIfFailed = false; + + // Make sure that the element is enabled. This includes all parents. + bool enabled = true; + bool visible = true; + if (obj != null) + { + enabled = obj.IsEnabled; + visible = obj.IsVisible; + + if ((enabled && visible) && forceToNullIfFailed) + { + obj = null; + enabled = true; + visible = true; + } + } + + if ((enabled && visible) && obj != _focus) + { + // go ahead and change our internal sense of focus to the desired element. + ChangeFocus(obj, DateTime.UtcNow); + } + + return _focus; + } + + /// + /// Returns whether or not the specified button is down. + /// + public bool IsButtonDown(Button button) + { + return GetButtonState(button) == ButtonState.Down; + } + + /// + /// Returns whether or not the specified button is up. + /// + public bool IsButtonUp(Button button) + { + return GetButtonState(button) == ButtonState.None; + } + + /// + /// Returns whether or not the specified button is held. + /// + public bool IsButtonHeld(Button button) + { + return GetButtonState(button) == ButtonState.Held; + } + + /// + /// Returns the state of the specified button. + /// + public ButtonState GetButtonState(Button button) + { +#if TRACK_BUTTON_STATE + + if ((int)Button.LastSystemDefinedButton <= (int)button || (int)button <= 0) + throw new ArgumentOutOfRangeException("button", "invalid enum"); + + int index = (int)button / 4; + int state = (_buttonState[index] >> ((int)button % 4)) & 0x3; + + return (ButtonState)state; +#else + return ButtonState.None; +#endif + } + +#if TRACK_BUTTON_STATE + internal void SetButtonState(Button button, ButtonState state) + { + //If the PreNotifyInput event sent by the InputManager is always sent by the + //correct thread, this is redundant. Also, why is this function 'internal' + //when we only access it from inside this class? + VerifyAccess(); + + if ((int)Button.LastSystemDefinedButton <= (int)button || (int)button <= 0) + throw new ArgumentOutOfRangeException("button", "invalid enum"); + + int index = (int)button / 4; + int shift = ((int)button % 4); + + byte newState = _buttonState[index]; + + newState &= (byte)(~((byte)(0x3 << shift))); + newState |= (byte)((int)state << shift); + + _buttonState[index] = newState; + } + +#endif + + private void ChangeFocus(UIElement focus, DateTime timestamp) + { + if (focus != _focus) + { + // Update the critical pieces of data. + UIElement oldFocus = _focus; + _focus = focus; + _focusRootUIElement = focus != null ? focus.RootUIElement : null; + + // Adjust the handlers we use to track everything. + if (oldFocus != null) + { + oldFocus.IsEnabledChanged -= _isEnabledOrVisibleChangedEventHandler; + oldFocus.IsVisibleChanged -= _isEnabledOrVisibleChangedEventHandler; + } + + if (focus != null) + { + focus.IsEnabledChanged += _isEnabledOrVisibleChangedEventHandler; + focus.IsVisibleChanged += _isEnabledOrVisibleChangedEventHandler; + } + + // Send the LostFocus and GotFocus events. + if (oldFocus != null) + { + FocusChangedEventArgs lostFocus = new FocusChangedEventArgs(this, timestamp, oldFocus, focus); + lostFocus.RoutedEvent = Buttons.LostFocusEvent; + lostFocus.Source = oldFocus; + + _inputManager.ProcessInput(lostFocus); + } + + if (focus != null) + { + FocusChangedEventArgs gotFocus = new FocusChangedEventArgs(this, timestamp, oldFocus, focus); + gotFocus.RoutedEvent = Buttons.GotFocusEvent; + gotFocus.Source = focus; + + _inputManager.ProcessInput(gotFocus); + } + } + } + + private void OnIsEnabledOrVisibleChanged(object sender, PropertyChangedEventArgs e) + { + // The element with focus just became disabled or non-visible + // + // We can't leave focus on a disabled element, so move it. + // + // Will need to change this for watch, but this solution is for aux now. + + Focus(_focus.Parent); + } + + private void PreNotifyInput(object sender, NotifyInputEventArgs e) + { + RawButtonInputReport buttonInput = ExtractRawButtonInputReport(e, InputManager.PreviewInputReportEvent); + if (buttonInput != null) + { + CheckForDisconnectedFocus(); + /* + +REFACTOR -- + + the keyboard device is only active per app domain basis -- so like if your app domain doesn't have + focus your keyboard device is not going to give you the real state of the keyboard. + + When it gets focus, it needs to know about this somehow. We could use this keyboard action + type stuff to do so. Though this design really seem to be influenced a lot from living in + the windows world. + + Essentially the input stack is being used like a message pump to say, hey dude you can + use the keyboard now -- it's not real input, it's more or less a message. + + It could be interesting for elements to know about this -- since I think + they will probalby still have focus (or do they get a Got and Lost Focus when the keyboard activates -- I don't think so, + we need to know what we were focused on when the window gets focus again. + + So maybe elements want to stop some animation or something when input focus moves away from the activesource, and + start them again later. Could be interesting. +*/ + + if ((buttonInput.Actions & RawButtonActions.Activate) == RawButtonActions.Activate) + { + //System.Console.WriteLine("Initializing the button state."); + +#if TRACK_BUTTON_STATE + // Clear out our key state storage. + for (int i = 0; i < _buttonState.Length; i++) + { + _buttonState[i] = 0; + } + +#endif + // we are now active. + // we should track which source is active so we don't confuse deactivations. + _isActive = true; + } + + // Generally, we need to check against redundant actions. + // We never prevet the raw event from going through, but we + // will only generate the high-level events for non-redundant + // actions. We store the set of non-redundant actions in + // the dictionary of this event. + + // If the input is reporting a button down, the action is never + // considered redundant. + if ((buttonInput.Actions & RawButtonActions.ButtonDown) == RawButtonActions.ButtonDown) + { + RawButtonActions actions = GetNonRedundantActions(e); + actions |= RawButtonActions.ButtonDown; + e.StagingItem.SetData(_tagNonRedundantActions, actions); + + // Pass along the button that was pressed, and update our state. + e.StagingItem.SetData(_tagButton, buttonInput.Button); + +#if TRACK_BUTTON_STATE + ButtonState buttonState = GetButtonState(buttonInput.Button); + + if ((buttonState & ButtonState.Down) == ButtonState.Down) + { + buttonState = ButtonState.Down | ButtonState.Held; + } + else + { + buttonState |= ButtonState.Down; + } + + SetButtonState(buttonInput.Button, buttonState); +#endif + + // Tell the InputManager that the MostRecentDevice is us. + if (_inputManager != null && _inputManager.MostRecentInputDevice != this) + { + _inputManager.MostRecentInputDevice = (InputDevice)this; + } + } + + // need to detect redundant ups. + if ((buttonInput.Actions & RawButtonActions.ButtonUp) == RawButtonActions.ButtonUp) + { + RawButtonActions actions = GetNonRedundantActions(e); + actions |= RawButtonActions.ButtonUp; + e.StagingItem.SetData(_tagNonRedundantActions, actions); + + // Pass along the button that was pressed, and update our state. + e.StagingItem.SetData(_tagButton, buttonInput.Button); + +#if TRACK_BUTTON_STATE + ButtonState buttonState = GetButtonState(buttonInput.Button); + + if ((buttonState & ButtonState.Down) == ButtonState.Down) + { + buttonState &= (~ButtonState.Down) & (ButtonState.Down | ButtonState.Held); + } + else + { + buttonState |= ButtonState.Held; + } + + SetButtonState(buttonInput.Button, buttonState); +#endif + + // Tell the InputManager that the MostRecentDevice is us. + if (_inputManager != null && _inputManager.MostRecentInputDevice != this) + { + _inputManager.MostRecentInputDevice = (InputDevice)this; + } + } + } + + // On ButtonDown, we might need to set the Repeat flag + + if (e.StagingItem.Input.RoutedEvent == Buttons.PreviewButtonDownEvent) + { + CheckForDisconnectedFocus(); + + ButtonEventArgs args = (ButtonEventArgs)e.StagingItem.Input; + + // Is this the same as the previous button? + if (_previousButton == args.Button) + { + // Yes, this is a repeat (we got the buttondown for it twice, with no ButtonUp in between) + // what about chording? + args._isRepeat = true; + } + + // Otherwise, keep this button to check against next time. + else + { + _previousButton = args.Button; + args._isRepeat = false; + } + + } + + // On ButtonUp, we clear Repeat flag + else if (e.StagingItem.Input.RoutedEvent == Buttons.PreviewButtonUpEvent) + { + CheckForDisconnectedFocus(); + + ButtonEventArgs args = (ButtonEventArgs)e.StagingItem.Input; + args._isRepeat = false; + + // Clear _previousButton, so that down/up/down/up doesn't look like a repeat + _previousButton = Button.None; + + } + } + + private void PostProcessInput(object sender, ProcessInputEventArgs e) + { + // PreviewButtonDown --> ButtonDown + if (e.StagingItem.Input.RoutedEvent == Buttons.PreviewButtonDownEvent) + { + CheckForDisconnectedFocus(); + + if (!e.StagingItem.Input.Handled) + { + ButtonEventArgs previewButtonDown = (ButtonEventArgs)e.StagingItem.Input; + ButtonEventArgs buttonDown = new ButtonEventArgs(this, previewButtonDown.InputSource, previewButtonDown.Timestamp, previewButtonDown.Button); + + buttonDown._isRepeat = previewButtonDown.IsRepeat; + buttonDown.RoutedEvent = Buttons.ButtonDownEvent; + + e.PushInput(buttonDown, e.StagingItem); + } + } + + // PreviewButtonUp --> ButtonUp + if (e.StagingItem.Input.RoutedEvent == Buttons.PreviewButtonUpEvent) + { + CheckForDisconnectedFocus(); + + if (!e.StagingItem.Input.Handled) + { + ButtonEventArgs previewButtonUp = (ButtonEventArgs)e.StagingItem.Input; + + ButtonEventArgs buttonUp = new ButtonEventArgs(this, previewButtonUp.InputSource, previewButtonUp.Timestamp, previewButtonUp.Button); + + buttonUp.RoutedEvent = Buttons.ButtonUpEvent; + + e.PushInput(buttonUp, e.StagingItem); + } + } + + RawButtonInputReport buttonInput = ExtractRawButtonInputReport(e, InputManager.InputReportEvent); + if (buttonInput != null) + { + CheckForDisconnectedFocus(); + + if (!e.StagingItem.Input.Handled) + { + // In general, this is where we promote the non-redundant + // reported actions to our premier events. + RawButtonActions actions = GetNonRedundantActions(e); + + // Raw --> PreviewButtonDown + if ((actions & RawButtonActions.ButtonDown) == RawButtonActions.ButtonDown) + { + Button button = (Button)e.StagingItem.GetData(_tagButton); + if (button != Button.None) + { + ButtonEventArgs previewButtonDown = new ButtonEventArgs(this, buttonInput.InputSource, buttonInput.Timestamp, button); + previewButtonDown.RoutedEvent = Buttons.PreviewButtonDownEvent; + e.PushInput(previewButtonDown, e.StagingItem); + } + } + + // Raw --> PreviewButtonUp + if ((actions & RawButtonActions.ButtonUp) == RawButtonActions.ButtonUp) + { + Button button = (Button)e.StagingItem.GetData(_tagButton); + if (button != Button.None) + { + ButtonEventArgs previewButtonUp = new ButtonEventArgs(this, buttonInput.InputSource, buttonInput.Timestamp, button); + previewButtonUp.RoutedEvent = Buttons.PreviewButtonUpEvent; + e.PushInput(previewButtonUp, e.StagingItem); + } + } + } + + // Deactivate + if ((buttonInput.Actions & RawButtonActions.Deactivate) == RawButtonActions.Deactivate) + { + if (_isActive) + { + _isActive = false; + + // Even if handled, a button deactivate results in a lost focus. + ChangeFocus(null, e.StagingItem.Input.Timestamp); + } + } + } + } + + private RawButtonActions GetNonRedundantActions(NotifyInputEventArgs e) + { + RawButtonActions actions; + + // The CLR throws a null-ref exception if it tries to unbox a + // null. So we have to special case that. + object o = e.StagingItem.GetData(_tagNonRedundantActions); + if (o != null) + { + actions = (RawButtonActions)o; + } + else + { + actions = new RawButtonActions(); + } + + return actions; + } + + // at the moment we don't have a good way of detecting when an + // element gets deleted from the tree (logical or visual). The + // best we can do right now is clear out the focus if we detect + // that the tree containing the focus was disconnected. + private bool CheckForDisconnectedFocus() + { + bool wasDisconnected = false; + + if (_focus != null && _focus.RootUIElement != _focusRootUIElement) + { + wasDisconnected = true; + + // need to remove this for the watch, placed here for aux now. + Focus(_focusRootUIElement); + } + + return wasDisconnected; + } + + private RawButtonInputReport ExtractRawButtonInputReport(NotifyInputEventArgs e, RoutedEvent Event) + { + RawButtonInputReport buttonInput = null; + + InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; + if (input != null) + { + if (input.Report is RawButtonInputReport && input.RoutedEvent == Event) + { + buttonInput = (RawButtonInputReport)input.Report; + } + } + + return buttonInput; + } + + private InputManager _inputManager; + private bool _isActive; + + private UIElement _focus; + private UIElement _focusRootUIElement; + private Button _previousButton; + + private PropertyChangedEventHandler _isEnabledOrVisibleChangedEventHandler; + +#if TRACK_BUTTON_STATE + // Device state we track + private byte[] _buttonState = new byte[(int)Button.LastSystemDefinedButton / 4]; +#endif + + // Data tags for information we pass around the staging area. + private object _tagNonRedundantActions = new object(); + private object _tagButton = new object(); + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventArgs.cs new file mode 100644 index 0000000..62059f2 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventArgs.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) 2017 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; +using nanoFramework.UI; + +namespace nanoFramework.UI.Input +{ + /// + /// The ButtonEventArgs class contains information about button states. + /// + /// + public class ButtonEventArgs : InputEventArgs + { + /// + /// Constructs an instance of the ButtonEventArgs class. + /// + /// + /// The button device associated with this event. + /// + /// + /// Presentation Source + /// + /// + /// The time when the input occured. (machine time) + /// + /// + /// The button referenced by the event. + /// + public ButtonEventArgs(ButtonDevice buttonDevice, PresentationSource inputSource, DateTime timestamp, Button button) + : base(buttonDevice, timestamp) + { + InputSource = inputSource; + Button = button; + } + + /// + /// The Button referenced by the event. + /// + public readonly Button Button; + + /// + /// The state of the button referenced by the event. + /// + public ButtonState ButtonState + { + get { return ((ButtonDevice)this.Device).GetButtonState(Button); } + } + + /// + /// The source for this button + /// + public readonly PresentationSource InputSource; + + /// + /// Whether the button pressed is a repeated button or not. + /// + public bool IsRepeat + { + get { return _isRepeat; } + } + + internal bool _isRepeat; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventHandler.cs new file mode 100644 index 0000000..ad61a2f --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonEventHandler.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// The delegate to use for handlers that receive ButtonEventArgs. + /// + public delegate void ButtonEventHandler(object sender, ButtonEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonState.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonState.cs new file mode 100644 index 0000000..d9e6558 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/ButtonState.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.UI.Input +{ + + /// + /// The ButtonState enumeration describes the state that buttons + /// can be in. + /// + [Flags] + public enum ButtonState : byte + { + /// + /// No state (same as up). + /// + None = 0, + + /// + /// The button is down. + /// + Down = 1, + + /// + /// The button is held + /// + Held = 2 + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/Buttons.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/Buttons.cs new file mode 100644 index 0000000..dff5f4c --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/Buttons.cs @@ -0,0 +1,126 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using nanoFramework.UI; + +namespace nanoFramework.UI.Input +{ + + // REFACTOR -- this could potentially be integrated with ButtonDevice, though + // this can give us a nice friendly class name (maybe ButtonPad or something instead), + // so developer code looks real nice and all. + // However, all these static wrappers are a buncha unneeded indirection if you're + // looking perf-wise. + + /// + /// The Button class represents the button device to the + /// members of a context. + /// + /// + /// The static members of this class simply delegate to the primary + /// button device of the calling thread's input manager. + /// + public sealed class Buttons + { + /// + /// PreviewButtonDown + /// + public static readonly RoutedEvent PreviewButtonDownEvent = new RoutedEvent("PreviewButtonDown", RoutingStrategy.Tunnel, typeof(ButtonEventHandler)); + + /// + /// PreviewButtonUp + /// + public static readonly RoutedEvent PreviewButtonUpEvent = new RoutedEvent("PreviewButtonUp", RoutingStrategy.Tunnel, typeof(ButtonEventHandler)); + + /// + /// ButtonDown + /// + public static readonly RoutedEvent ButtonDownEvent = new RoutedEvent("ButtonDown", RoutingStrategy.Bubble, typeof(ButtonEventHandler)); + + /// + /// ButtonUp + /// + public static readonly RoutedEvent ButtonUpEvent = new RoutedEvent("ButtonUp", RoutingStrategy.Bubble, typeof(ButtonEventHandler)); + + /// + /// GotFocus + /// + public static readonly RoutedEvent GotFocusEvent = new RoutedEvent("GotFocus", RoutingStrategy.Bubble, typeof(FocusChangedEventHandler)); + + /// + /// LostFocus + /// + public static readonly RoutedEvent LostFocusEvent = new RoutedEvent("LostFocus", RoutingStrategy.Bubble, typeof(FocusChangedEventHandler)); + + /// + /// Returns the element that the button is focused on. + /// + public static UIElement FocusedElement + { + get + { + return PrimaryDevice.Target; + } + } + + /// + /// Focuses the button on a particular element. + /// + /// + /// The element to focus the button on. + /// + public static UIElement Focus(UIElement element) + { + return PrimaryDevice.Focus(element); + } + + /// + /// Returns whether or not the specified button is down. + /// + public static bool IsButtonDown(Button button) + { + return PrimaryDevice.IsButtonDown(button); + } + + /// + /// Returns whether or not the specified button is up. + /// + public static bool IsButtonUp(Button button) + { + return PrimaryDevice.IsButtonUp(button); + } + + /// + /// Returns whether or not the specified button is held. + /// + public static bool IsButtonHeld(Button button) + { + return PrimaryDevice.IsButtonHeld(button); + } + + /// + /// Returns the state of the specified button. + /// + public static ButtonState GetButtonState(Button button) + { + return PrimaryDevice.GetButtonState(button); + } + + /// + /// The primary button device. + /// + public static ButtonDevice PrimaryDevice + { + get + { + return InputManager.CurrentInputManager._buttonDevice; + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventArgs.cs new file mode 100644 index 0000000..72f98e7 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventArgs.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; + +namespace nanoFramework.UI.Input +{ + /// + /// The FocusChangedEventArgs class contains information about focus states + /// + public class FocusChangedEventArgs : InputEventArgs + { + /// + /// Constructs an instance of the FocusChangedEventArgs class. + /// + /// + /// The logical button device associated with this event. + /// + /// + /// The time when the input occured. + /// + /// + /// The element that previously had focus. + /// + /// + /// The element that now has focus. + /// + public FocusChangedEventArgs(ButtonDevice buttonDevice, DateTime timestamp, UIElement oldFocus, UIElement newFocus) + : base(buttonDevice, timestamp) + { + OldFocus = oldFocus; + NewFocus = newFocus; + } + + /// + /// The element that previously had focus. + /// + public readonly UIElement OldFocus; + + /// + /// The element that now has focus. + /// + public readonly UIElement NewFocus; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventHandler.cs new file mode 100644 index 0000000..0b7b151 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/FocusChangedEventHandler.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// The delegate to use for handlers that receive FocusChangedEventArgs. + /// + public delegate void FocusChangedEventHandler(object sender, FocusChangedEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/GenericDevice.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/GenericDevice.cs new file mode 100644 index 0000000..265ab8d --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/GenericDevice.cs @@ -0,0 +1,132 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI.Input +{ + /// + /// Generic Event handler + /// + /// + /// + public delegate void GenericEventHandler(object sender, GenericEventArgs e); + + /// + /// Event Args + /// + public class GenericEventArgs : InputEventArgs + { + /// + /// Generic Event Args + /// + /// + /// + public GenericEventArgs(InputDevice inputDevice, GenericEventEx genericEvent) + : base(inputDevice, genericEvent.Time) + { + InternalEvent = genericEvent; + } + + /// + /// GenericEventEx + /// + public readonly GenericEventEx InternalEvent; + } + + /// + /// Generic Events + /// + public sealed class GenericEvents + { + /// + /// Generic Standard Events + /// + public static readonly RoutedEvent GenericStandardEvent = new RoutedEvent("GenericStandardEvent", RoutingStrategy.Tunnel, typeof(GenericEventArgs)); + } + + /// + /// The GenericDevice class represents the Generic device to the + /// members of a context. + /// + public sealed class GenericDevice : InputDevice + { + internal GenericDevice(InputManager inputManager) + { + _inputManager = inputManager; + + _inputManager.InputDeviceEvents[(int)InputManager.InputDeviceType.Generic].PostProcessInput += new ProcessInputEventHandler(PostProcessInput); + } + + private UIElement _focus = null; + + /// + /// + /// + public override UIElement Target + { + get + { + VerifyAccess(); + + return _focus; + } + } + + /// + /// SetTarget + /// + /// + public void SetTarget(UIElement target) + { + _focus = target; + } + + /// + /// Input Manager + /// + public override InputManager.InputDeviceType DeviceType + { + get + { + return InputManager.InputDeviceType.Generic; + } + } + + private void PostProcessInput(object sender, ProcessInputEventArgs e) + { + InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; + if (input != null && input.RoutedEvent == InputManager.InputReportEvent) + { + RawGenericInputReport report = input.Report as RawGenericInputReport; + + if (report != null) + { + if (!e.StagingItem.Input.Handled) + { + GenericEventEx ge = (GenericEventEx)report.InternalEvent; + GenericEventArgs args = new GenericEventArgs( + this, + report.InternalEvent); + + args.RoutedEvent = GenericEvents.GenericStandardEvent; + if (report.Target != null) + { + args.Source = report.Target; + } + + e.PushInput(args, e.StagingItem); + } + } + } + } + + private InputManager _inputManager; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputDevice.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputDevice.cs new file mode 100644 index 0000000..5fe34a0 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputDevice.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using nanoFramework.UI; +using nanoFramework.UI.Threading; + +namespace nanoFramework.UI.Input +{ + /// + /// Provides the base class for all input devices. + /// + public abstract class InputDevice : DispatcherObject + { + /// + /// Constructs an instance of the InputDevice class. + /// + protected InputDevice() + { + } + + /// + /// Returns the element that input from this device is sent to. + /// + public abstract UIElement Target { get; } + + /// + /// Input Device Type + /// + public abstract InputManager.InputDeviceType DeviceType { get; } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputEventArgs.cs new file mode 100644 index 0000000..dd2af1c --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputEventArgs.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; +using System; + +namespace nanoFramework.UI.Input +{ + /// + /// The InputEventArgs class represents a type of RoutedEventArgs that + /// are relevant to all input events. + /// + + public class InputEventArgs : RoutedEventArgs + { + /// + /// Initializes a new instance of the InputEventArgs class. + /// + /// + /// The input device to associate with this event. + /// + /// + /// The time when the input occured. + /// + public InputEventArgs(InputDevice inputDevice, DateTime timestamp) + { + /* inputDevice parameter being null is valid */ + /* timestamp parameter is valuetype, need not be checked */ + + _inputDevice = inputDevice; + Timestamp = timestamp; + } + + /// + /// Read-only access to the input device that initiated this + /// event. + /// + public InputDevice Device + { + get { return _inputDevice; } + } + + /// + /// Read-only access to the input timestamp. + /// + public readonly DateTime Timestamp; + + internal InputDevice _inputDevice; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputEventHandler.cs new file mode 100644 index 0000000..2eb5ae8 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputEventHandler.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// The delegate to use for handlers that receive InputEventArgs. + /// + public delegate void InputEventHandler(object sender, InputEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputManager.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputManager.cs new file mode 100644 index 0000000..b6daff2 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputManager.cs @@ -0,0 +1,499 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; +using System.Collections; +using nanoFramework.UI.Threading; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI.Input +{ + /// + /// The InputManager class is responsible for coordinating all of the + /// input system in nanoFramework. + /// + /// The input manager exists per Dispatcher + /// + public sealed class InputManager : DispatcherObject + { + + /// + /// A routed event indicating that an input report arrived. + /// + public static readonly RoutedEvent PreviewInputReportEvent = new RoutedEvent("PreviewInputReport", RoutingStrategy.Tunnel, typeof(InputReportEventHandler)); + + /// + /// A routed event indicating that an input report arrived. + /// + public static readonly RoutedEvent InputReportEvent = new RoutedEvent("InputReport", RoutingStrategy.Bubble, typeof(InputReportEventHandler)); + + /// + /// Return the input manager associated with the current context. + /// + /// + /// This class will not be available in internet zone. + /// + public static InputManager CurrentInputManager + { + get + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + if (dispatcher == null) throw new InvalidOperationException("no dispatcher"); + + if (dispatcher._inputManager == null) + { + lock (typeof(GlobalLock)) + { + if (dispatcher._inputManager == null) + { + dispatcher._inputManager = new InputManager(); + } + } + } + + return dispatcher._inputManager; + } + } + + private InputManager() + { + _stagingArea = new Queue(); + _currentStagingStack = new Stack(); + _frameStagingArea = new ArrayList(); + InputDeviceEvents = new DeviceEvents[(int)InputDeviceType.Last]; + + for (int i = 0; i < (int)InputDeviceType.Last; i++) + { + InputDeviceEvents[i] = new DeviceEvents(); + } + + _continueProcessingStagingAreaCallback = new DispatcherOperationCallback(ProcessStagingArea); + _buttonDevice = new ButtonDevice(this); + _touchDevice = new TouchDevice(this); + _genericDevice = new GenericDevice(this); + } + + /// + /// Button Device + /// + public ButtonDevice ButtonDevice + { + get + { + return _buttonDevice; + } + } + + /// + /// Touch Device + /// + public TouchDevice TouchDevice + { + get + { + return _touchDevice; + } + } + + /// + /// GenericDevice + /// + public GenericDevice GenericDevice + { + get + { + return _genericDevice; + } + } + + /// + /// Registers an input provider with the input manager. + /// + /// + /// The input provider to register. + /// + public InputProviderSite RegisterInputProvider(object inputProvider) + { + VerifyAccess(); + + // Create a site for this provider, and keep track of it. + InputProviderSite site = new InputProviderSite(this, inputProvider); + + int idx = _inputProviders.IndexOf(inputProvider); + if (idx < 0) + { + _inputProviders.Add(inputProvider); + _inputProviderSites.Add(site); + } + else + { + // NOTE -- should we dispose the old one? + _inputProviders[idx] = inputProvider; + _inputProviderSites[idx] = site; + } + + return site; + } + + internal void UnregisterInputProvider(object inputProvider) + { + int i = _inputProviders.IndexOf(inputProvider); + if (i >= 0) + { + _inputProviders.RemoveAt(i); + _inputProviderSites.RemoveAt(i); + } + } + + /// + /// Returns a collection of input providers registered with the input manager. + /// + public ICollection InputProviders + { + get + { + VerifyAccess(); + + return _inputProviders; + } + } + + /// + /// The MostRecentInputDevice represents the last input device to + /// report an "interesting" user action. What exactly constitutes + /// such an action is up to each device to implement. + /// + public InputDevice MostRecentInputDevice + { + get + { //If GenericInputDevice/TouchDevice use VerifyAccess in SetTarget, this verify can be removed. + VerifyAccess(); return _mostRecentInputDevice; + } + set { VerifyAccess(); _mostRecentInputDevice = value; } + } + + /// + /// Synchronously processes the specified input. + /// + /// + /// The specified input is processed by all of the filters and + /// monitors, and is finally dispatched to the appropriate + /// element as an input event. + /// + /// + /// Whether or not any event generated as a consequence of this + /// event was handled. + /// + public bool ProcessInput(InputEventArgs input) + { + VerifyAccess(); + + if (input == null) + { + throw new ArgumentNullException("input"); + } + + // Push a marker indicating the portion of the staging area + // that needs to be processed. + Stack stk = new Stack(); + + // Push the input to be processed onto the staging area. + stk.Push(new StagingAreaInputItem(input, null)); + + _stagingArea.Enqueue(stk); + + // Post a work item to continue processing the staging area + // in case someone pushes a dispatcher frame in the middle + // of input processing. + DispatcherFrame frame = Dispatcher.CurrentFrame; + if (frame != null) + { + if (!_frameStagingArea.Contains(frame)) + { + _frameStagingArea.Add(frame); + Dispatcher.BeginInvoke(_continueProcessingStagingAreaCallback, frame); + } + } + + return true; + } + + private object ProcessStagingArea(object frame) + { + bool handled = false; + + // NOTE -- avalon caches the XXXEventArgs. In our system, the costs are different, + // so it is probably cheaper for us to just create them, since everything gets created + // on the heap anyways, and IL is expensive. we should reconsider this if + // its a performance impact. + + // Because we can be reentered, we can't just enumerate over the + // staging area - that could throw an exception if the queue + // changes underneath us. Instead, just loop until we find a + // frame marker or until the staging area is empty. + + try + { + while (_stagingArea.Count > 0) + { + _currentStagingStack = _stagingArea.Dequeue() as Stack; + + do + { + StagingAreaInputItem item = (StagingAreaInputItem)_currentStagingStack.Pop(); + + // Pre-Process the input. This could modify the staging + // area, and it could cancel the processing of this + // input event. + // + + bool fCanceled = false; + + int devType = (int)item.Input._inputDevice.DeviceType; + + if (InputDeviceEvents[devType]._preProcessInput != null) + { + PreProcessInputEventArgs preProcessInputEventArgs; + InputDeviceEvents[devType]._preProcessInput(this, preProcessInputEventArgs = new PreProcessInputEventArgs(item)); + + fCanceled = preProcessInputEventArgs._canceled; + } + + if (!fCanceled) + { + // Pre-Notify the input. + // + if (InputDeviceEvents[devType]._preNotifyInput != null) + { + InputDeviceEvents[devType]._preNotifyInput(this, new NotifyInputEventArgs(item)); + } + + // Raise the input event being processed. + InputEventArgs input = item.Input; + + // Some input events are explicitly associated with + // an element. Those that are not instead are associated with + // the target of the input device for this event. + UIElement eventSource = input._source as UIElement; + + if (eventSource == null && input._inputDevice != null) + { + eventSource = input._inputDevice.Target; + } + + if (eventSource != null) + { + eventSource.RaiseEvent(input); + } + + // Post-Notify the input. + // + if (InputDeviceEvents[devType]._postNotifyInput != null) + { + InputDeviceEvents[devType]._postNotifyInput(this, new NotifyInputEventArgs(item)); + } + + // Post-Process the input. This could modify the staging + // area. + if (InputDeviceEvents[devType]._postProcessInput != null) + { + InputDeviceEvents[devType]._postProcessInput(this, new ProcessInputEventArgs(item)); + } + + // PreviewInputReport --> InputReport + if (item.Input._routedEvent == InputManager.PreviewInputReportEvent) + { + if (!item.Input.Handled) + { + InputReportEventArgs previewInputReport = (InputReportEventArgs)item.Input; + InputReportEventArgs inputReport = new InputReportEventArgs(previewInputReport.Device, previewInputReport.Report); + inputReport.RoutedEvent = InputManager.InputReportEvent; + + _currentStagingStack.Push(new StagingAreaInputItem(inputReport, item)); + } + } + + if (input.Handled) + { + handled = true; + } + } + } while (_currentStagingStack.Count > 0); + } + } + finally + { + // It is possible that we can be re-entered by a nested + // dispatcher frame. Continue processing the staging + // area if we need to. + if (_stagingArea.Count > 0) + { + // Before we actually start to drain the staging area, we need + // to post a work item to process more input. This enables us + // to process more input if we enter a nested pump. + Dispatcher.BeginInvoke(_continueProcessingStagingAreaCallback, Dispatcher.CurrentFrame); + } + + _frameStagingArea.Remove(frame); + } + + return handled; + } + + private DispatcherOperationCallback _continueProcessingStagingAreaCallback; + private ArrayList _frameStagingArea; + + + /// + /// Input Device Type + /// + public enum InputDeviceType : int + { + /// + /// Button + /// + + Button = 0, + + /// + /// Touch + /// + + Touch, + + /// + /// Generic + /// + + Generic, + + /// + /// Last + /// + + Last, + + } + + /// + /// Device Events + /// + public class DeviceEvents : DispatcherObject + { + /// Subscribe for all input before it is processed + public event PreProcessInputEventHandler PreProcessInput + { + add + { + VerifyAccess(); + + // Add the handlers in reverse order so that handlers that + // users add are invoked before handlers in the system. + + _preProcessInput = (PreProcessInputEventHandler)WeakDelegate.Combine(value, _preProcessInput); + } + + remove + { + VerifyAccess(); + + _preProcessInput = (PreProcessInputEventHandler)WeakDelegate.Remove(_preProcessInput, value); + } + } + + /// Subscribe for all input before it is notified + public event NotifyInputEventHandler PreNotifyInput + { + add + { + VerifyAccess(); + + // Add the handlers in reverse order so that handlers that + // users add are invoked before handlers in the system. + + _preNotifyInput = (NotifyInputEventHandler)WeakDelegate.Combine(value, _preNotifyInput); + } + + remove + { + VerifyAccess(); + _preNotifyInput = (NotifyInputEventHandler)WeakDelegate.Remove(_preNotifyInput, value); + } + + } + + /// Subscribe to all input after it is notified + public event NotifyInputEventHandler PostNotifyInput + { + add + { + VerifyAccess(); + + // Add the handlers in reverse order so that handlers that + // users add are invoked before handlers in the system. + + _postNotifyInput = (NotifyInputEventHandler)WeakDelegate.Combine(value, _postNotifyInput); + } + + remove + { + VerifyAccess(); + + _postNotifyInput = (NotifyInputEventHandler)WeakDelegate.Remove(_postNotifyInput, value); + } + } + + /// subscribe to all input after it is processed + public event ProcessInputEventHandler PostProcessInput + { + add + { + VerifyAccess(); + + // Add the handlers in reverse order so that handlers that + // users add are invoked before handlers in the system. + + _postProcessInput = (ProcessInputEventHandler)WeakDelegate.Combine(value, _postProcessInput); + } + + remove + { + VerifyAccess(); + + _postProcessInput = (ProcessInputEventHandler)WeakDelegate.Remove(_postProcessInput, value); + } + } + + internal PreProcessInputEventHandler _preProcessInput; + internal NotifyInputEventHandler _preNotifyInput; + internal NotifyInputEventHandler _postNotifyInput; + internal ProcessInputEventHandler _postProcessInput; + + } + + /// + /// + /// + public DeviceEvents[] InputDeviceEvents; + + private ArrayList _inputProviders = new ArrayList(); + private ArrayList _inputProviderSites = new ArrayList(); + + internal Stack _currentStagingStack; + internal Queue _stagingArea; + private InputDevice _mostRecentInputDevice; + + internal ButtonDevice _buttonDevice; + internal TouchDevice _touchDevice; + internal GenericDevice _genericDevice; + + private class GlobalLock { }; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputProviderSite.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputProviderSite.cs new file mode 100644 index 0000000..b42dc10 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputProviderSite.cs @@ -0,0 +1,117 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.UI.Input +{ + + /// + /// The object which input providers use to report input to the input manager. + /// + public class InputProviderSite : IDisposable + { + internal InputProviderSite(InputManager inputManager, object inputProvider) + { + if (inputManager == null) + { + throw new ArgumentNullException("inputManager"); + } + + _inputManager = inputManager; + _inputProvider = inputProvider; + } + + /// + /// Returns the input manager that this site is attached to. + /// + public InputManager InputManager + { + get + { + return _inputManager; + } + } + + /// + /// Unregisters this input provider. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + _isDisposed = true; + + if (_inputManager != null && _inputProvider != null) + { + _inputManager.UnregisterInputProvider(_inputProvider); + } + + _inputManager = null; + _inputProvider = null; + } + + } + + /// + /// Returns true if we are disposed. + /// + public bool IsDisposed + { + get + { + return _isDisposed; + } + } + + /// + /// Reports input to the input manager. + /// + /// + /// Whether or not any event generated as a consequence of this + /// event was handled. + /// + // do we really need this? Make the "providers" call InputManager.ProcessInput themselves. + // we currently need to map back to providers for other reasons. + public bool ReportInput(InputDevice device, InputReport inputReport) + { + if (_isDisposed) + { + throw new InvalidOperationException(); + } + + bool handled = false; + + InputReportEventArgs input = new InputReportEventArgs(device, inputReport); + input.RoutedEvent = InputManager.PreviewInputReportEvent; + + if (_inputManager != null) + { + handled = _inputManager.ProcessInput(input); + } + + return handled; + } + + private bool _isDisposed; + + private InputManager _inputManager; + + private object _inputProvider; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputReport.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputReport.cs new file mode 100644 index 0000000..c01e8cc --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputReport.cs @@ -0,0 +1,82 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; + +namespace nanoFramework.UI.Input +{ + /// + /// The InputReport is an abstract base class for all input that is + /// reported to the InputManager. + /// + /// + /// It is important to note that the InputReport class only contains + /// blittable types. This is required so that the report can be + /// marshalled across application domains. + /// + public abstract class InputReport + { + /// + /// Constructs an instance of the InputReport class. + /// + /// + /// The type of input that is being reported. + /// + /// + /// The time when the input occured. + /// + protected InputReport(PresentationSource inputSource, DateTime timestamp) + { + /* We need to allow a null presentation source for now, since the root input system doesn't have a presentationsource. + if (inputSource == null) + throw new ArgumentNullException("inputSource"); + */ + + InputSource = inputSource; + Timestamp = timestamp; + } + + /// + /// Read-only access to the type of input source that reported input. + /// + public readonly PresentationSource InputSource; + + /// + /// Read-only access to the time when the input occured. + /// + public readonly DateTime Timestamp; + } + + /// + /// report arguments + /// + public class InputReportArgs + { + /// + /// + /// + /// + /// + public InputReportArgs(object dev, object report) + { + Device = (InputDevice)dev; + Report = (InputReport)report; + } + + /// + /// + /// + public readonly InputDevice Device; + /// + /// + /// + public readonly InputReport Report; + } + +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventArgs.cs new file mode 100644 index 0000000..a97c464 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventArgs.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.UI.Input +{ + /// + /// The InputReportEventArgs class contains information about an input + /// report that is being processed. + /// + public class InputReportEventArgs : InputEventArgs + { + /// + /// Initializes a new instance of the InputReportEventArgs class. + /// + /// + /// The input device to associate this input with. + /// + /// + /// The input report being processed. + /// + public InputReportEventArgs(InputDevice inputDevice, + InputReport report) + : base(inputDevice, ((report != null) ? report.Timestamp : DateTime.MinValue)) + { + if (report == null) + throw new ArgumentNullException("report"); + + Report = report; + } + + /// + /// Read-only access to the input report being processed. + /// + public readonly InputReport Report; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventHandler.cs new file mode 100644 index 0000000..53b20e1 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/InputReportEventHandler.cs @@ -0,0 +1,16 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// The delegate to use for handlers that receive InputReportEventArgs + /// + public delegate void InputReportEventHandler(object sender, InputReportEventArgs e); + +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/NotifyInputEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/NotifyInputEventArgs.cs new file mode 100644 index 0000000..31db976 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/NotifyInputEventArgs.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI.Input +{ + /// + /// Provides information about an input event being processed by the + /// input manager. + /// + /// + /// An instance of this class, or a derived class, is passed to the + /// handlers of the following events: + /// + /// + /// + public class NotifyInputEventArgs : EventArgs + { + // Only we can make these. + internal NotifyInputEventArgs(StagingAreaInputItem input) + { + StagingItem = input; + } + + /// + /// The staging area input item being processed by the input + /// manager. + /// + public readonly StagingAreaInputItem StagingItem; + } + + /// + /// Delegate type for handles of events that use + /// . + /// + public delegate void NotifyInputEventHandler(object sender, NotifyInputEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventArgs.cs new file mode 100644 index 0000000..5a111f9 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventArgs.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// Allows the handler to cancel the processing of an input event. + /// + /// + /// An instance of this class is passed to the handlers of the + /// following events: + /// cref="InputManager.PreProcessInput" + /// + public sealed class PreProcessInputEventArgs : ProcessInputEventArgs + { + // Only we can make these. Note that we cache and reuse instances. + internal PreProcessInputEventArgs(StagingAreaInputItem input) + : base(input) + { + _canceled = false; + } + + /// + /// Cancels the processing of the input event. + /// + public void Cancel() + { + _canceled = true; + } + + /// + /// Whether or not the input event processing was canceled. + /// + public bool Canceled { get { return _canceled; } } + + internal bool _canceled; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventHandler.cs new file mode 100644 index 0000000..d02ac8b --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/PreProcessInputEventHandler.cs @@ -0,0 +1,16 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// Delegate type for handles of events that use + /// . + /// + public delegate void PreProcessInputEventHandler(object sender, PreProcessInputEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventArgs.cs new file mode 100644 index 0000000..09a4e60 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventArgs.cs @@ -0,0 +1,116 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System.Collections; + +namespace nanoFramework.UI.Input +{ + + /* REFACTOR + + Consider if we can wipe this wrapper, and just allow access to the input manager.. + this enforces a contract when processing input, which is nice, but I hate wrappers. + */ + + /// + /// Provides access to the input manager's staging area. + /// + /// + /// An instance of this class, or a derived class, is passed to the + /// handlers of the following events: + /// cref="InputManager.PreProcessInput" + /// cref="InputManager.PostProcessInput" + /// + /// + public class ProcessInputEventArgs : NotifyInputEventArgs + { + // Only we can make these. + internal ProcessInputEventArgs(StagingAreaInputItem input) + : base(input) + { + } + + /// + /// Pushes an input event onto the top of the staging area. + /// + /// + /// The input event to place on the staging area. This may not + /// be null, and may not already exist in the staging area. + /// + /// + /// An existing staging area item to promote the state from. + /// + /// + /// The staging area input item that wraps the specified input. + /// + public StagingAreaInputItem PushInput(InputEventArgs input, + StagingAreaInputItem promote) // Note: this should be a bool, and always use the InputItem available on these args. + { + StagingAreaInputItem item = new StagingAreaInputItem(input, promote); + + InputManager.CurrentInputManager._currentStagingStack.Push(item); + + return item; + } + + /// + /// Pushes an input event onto the top of the staging area. + /// + /// + /// The input event to place on the staging area. This may not + /// be null, and may not already exist in the staging area. + /// + /// + /// The specified staging area input item. + /// + public StagingAreaInputItem PushInput(StagingAreaInputItem input) + { + InputManager.CurrentInputManager._currentStagingStack.Push(input); + + return input; + } + + /// + /// Pops off the input event on the top of the staging area. + /// + /// + /// The input event that was on the top of the staging area. + /// This can be null, if the staging area was empty. + /// + public StagingAreaInputItem PopInput() + { + Stack stagingArea = InputManager.CurrentInputManager._currentStagingStack; + + if (stagingArea.Count > 0) + { + return (StagingAreaInputItem)stagingArea.Pop(); + } + + return null; + } + + /// + /// Returns the input event on the top of the staging area. + /// + /// + /// The input event that is on the top of the staging area. + /// This can be null, if the staging area is empty. + /// + public StagingAreaInputItem PeekInput() + { + Stack stagingArea = InputManager.CurrentInputManager._currentStagingStack; + + if (stagingArea.Count > 0) + { + return (StagingAreaInputItem)stagingArea.Peek(); + } + + return null; + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventHandler.cs new file mode 100644 index 0000000..e241336 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/ProcessInputEventHandler.cs @@ -0,0 +1,16 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Input +{ + /// + /// Delegate type for handles of events that use + /// . + /// + public delegate void ProcessInputEventHandler(object sender, ProcessInputEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/RawButtonInputReport.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/RawButtonInputReport.cs new file mode 100644 index 0000000..71dceb4 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/RawButtonInputReport.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; + +namespace nanoFramework.UI.Input +{ + /// + /// The RawButtonInputReport class encapsulates the raw input + /// provided from a keyboard. + /// + /// + /// It is important to note that the InputReport class only contains + /// blittable types. This is required so that the report can be + /// marshalled across application domains. + /// + public class RawButtonInputReport : InputReport + { + /// + /// Constructs an instance of the RawKeyboardInputReport class. + /// + /// + /// + /// + /// + public RawButtonInputReport(PresentationSource inputSource, DateTime timestamp, Button button, RawButtonActions actions) + : base(inputSource, timestamp) + { + Button = button; + Actions = actions; + } + + /// + /// Read-only access to the button reported. + /// + public readonly Button Button; + + /// + /// Read-only access to the action reported. + /// + public readonly RawButtonActions Actions; + } + + // REFACTOR -- this goes in a separate CS file. + /// + /// + /// + public enum RawButtonActions + { + /// + /// Button Down + /// + ButtonDown = 1, + + /// + /// Button Up + /// + ButtonUp = 2, + + /// + /// Activate + /// + Activate = 4, + + /// + /// Deactivate + /// + Deactivate = 8, + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/RawGenericInputReport.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/RawGenericInputReport.cs new file mode 100644 index 0000000..10ee293 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/RawGenericInputReport.cs @@ -0,0 +1,65 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI.Input +{ + /// + /// The RawGenericInputReport class encapsulates the raw input + /// provided from a keyboard. + /// + /// + /// It is important to note that the InputReport class only contains + /// blittable types. This is required so that the report can be + /// marshalled across application domains. + /// + public class RawGenericInputReport : InputReport + { + /// + /// Constructs an instance of the RawKeyboardInputReport class. + /// + /// + /// source of the input + /// + /// + /// Generic event + /// + public RawGenericInputReport(PresentationSource inputSource, GenericEventEx genericEvent) + : base(inputSource, genericEvent.Time) + { + InternalEvent = genericEvent; + Target = null; + } + + /// + /// + /// + /// + /// + /// + public RawGenericInputReport(PresentationSource inputSource, + GenericEventEx genericEvent, UIElement destTarget) + : base(inputSource, genericEvent.Time) + { + InternalEvent = genericEvent; + Target = destTarget; + } + + /// + /// + /// + public readonly UIElement Target; + + /// + /// + /// + public readonly GenericEventEx InternalEvent; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/RawTouchInputReport.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/RawTouchInputReport.cs new file mode 100644 index 0000000..eab2558 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/RawTouchInputReport.cs @@ -0,0 +1,103 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; +using nanoFramework.Touch; + +namespace nanoFramework.UI.Input +{ + /// + /// The RawTouchInputReport class encapsulates the raw input + /// provided from a multitouch source. + /// + /// + /// It is important to note that the InputReport class only contains + /// blittable types. This is required so that the report can be + /// marshalled across application domains. + /// +public class RawTouchInputReport : InputReport + { + /// + /// Constructs an instance of the RawKeyboardInputReport class. + /// + /// + /// + /// + /// + public RawTouchInputReport(PresentationSource inputSource, DateTime timestamp, byte eventMessage, TouchInput[] touches) + : base(inputSource, timestamp) + { + EventMessage = eventMessage; + Touches = touches; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public RawTouchInputReport(PresentationSource inputSource, + DateTime timestamp, byte eventMessage, TouchInput[] touches, UIElement destTarget) + : base(inputSource, timestamp) + { + EventMessage = eventMessage; + Touches = touches; + Target = destTarget; + } + + /// + /// + /// + public readonly UIElement Target; + + /// + /// + /// + public readonly byte EventMessage; + + /// + /// + /// + public readonly TouchInput[] Touches; + } + + /// + /// + /// + public enum RawTouchActions + { + /// + /// Touch Down + /// + TouchDown = 0x01, + + /// + /// Touch Up + /// + TouchUp = 0x02, + + /// + /// Activate + /// + Activate = 0x04, + + /// + /// Deactivate + /// + Deactivate = 0x08, + + /// + /// Touch Move + /// + TouchMove = 0x10, + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/StagingAreaInputItem.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/StagingAreaInputItem.cs new file mode 100644 index 0000000..43c2ccf --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/StagingAreaInputItem.cs @@ -0,0 +1,84 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System.Collections; + +namespace nanoFramework.UI.Input +{ + /// + /// This class encapsulates an input event while it is being + /// processed by the input manager. + /// + /// + /// This class just provides the dictionary-based storage for + /// all of the listeners of the various input manager events. + /// + public class StagingAreaInputItem + { + internal StagingAreaInputItem(InputEventArgs input, StagingAreaInputItem promote) + { + Input = input; + + if (promote != null && promote._table != null) + { + // REFACTOR -- need a hashtable! + + _table = (Hashtable)promote._table.Clone(); + } + } + + /// + /// Returns the input event. + /// + public readonly InputEventArgs Input; + + /// + /// Provides storage for arbitrary data needed during the + /// processing of this input event. + /// + /// + /// An arbitrary key for the data. This cannot be null. + /// + /// + /// The data previously set for this key, or null. + /// + public object GetData(object key) + { + if (_table == null) + { + return null; + } + else + { + return _table[key]; + } + } + + /// + /// Provides storage for arbitrary data needed during the + /// processing of this input event. + /// + /// + /// An arbitrary key for the data. This cannot be null. + /// + /// + /// The data to set for this key. This can be null. + /// + public void SetData(object key, object value) + { + if (_table == null) + { + _table = new Hashtable(); + } + + _table[key] = value; + } + + Hashtable _table; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/Touch.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/Touch.cs new file mode 100644 index 0000000..c789f03 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/Touch.cs @@ -0,0 +1,178 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using nanoFramework.UI; +using System; +using nanoFramework.Touch; + +namespace nanoFramework.UI.Input +{ + /// + /// + /// + /// + /// + public delegate void TouchEventHandler(object sender, TouchEventArgs e); + + /// + /// + /// + public enum CaptureMode + { + /// + /// None + /// + None, + + /// + /// Element + /// + Element, + + /// + /// SubTree + /// + SubTree, + } + + /// + /// + /// + public static class TouchCapture + { + /// + /// + /// + /// + /// + public static bool Capture(UIElement element) + { + return Capture(element, CaptureMode.Element); + } + + /// + /// + /// + /// + /// + /// + public static bool Capture(UIElement element, CaptureMode mode) + { + if (mode != CaptureMode.None) + { + if (element == null) + { + throw new ArgumentException(); + } + + // Make sure the element is attached + // to the MainWindow subtree. + if (!IsMainWindowChild(element)) + { + throw new ArgumentException(); + } + + if (mode == CaptureMode.SubTree) + { + throw new NotImplementedException(); + } + + if (mode == CaptureMode.Element) + { + _captureElement = element; + } + } + else + { + _captureElement = null; + } + + return true; + } + + /// + /// + /// + public static UIElement Captured + { + get + { + return _captureElement; + } + } + + private static bool IsMainWindowChild(UIElement element) + { + UIElement mainWindow = Application.Current.MainWindow; + while (element != null) + { + if (element == mainWindow) + return true; + + element = element.Parent; + } + + return false; + } + + private static UIElement _captureElement = null; + } + + /// + /// + /// + public sealed class TouchEvents + { + // Fields + /// + /// + /// + public static readonly RoutedEvent TouchDownEvent = new RoutedEvent("TouchDownEvent", RoutingStrategy.Tunnel, typeof(TouchEventArgs)); + /// + /// + /// + public static readonly RoutedEvent TouchMoveEvent = new RoutedEvent("TouchMoveEvent", RoutingStrategy.Tunnel, typeof(TouchEventArgs)); + /// + /// + /// + public static readonly RoutedEvent TouchUpEvent = new RoutedEvent("TouchUpEvent", RoutingStrategy.Tunnel, typeof(TouchEventArgs)); + } + + /// + /// + /// + public class TouchEventArgs : InputEventArgs + { + /// + /// + /// + public TouchInput[] Touches; + + /// + /// + /// + /// + /// + /// + public TouchEventArgs(InputDevice inputDevice, DateTime timestamp, TouchInput[] touches) + : base(inputDevice, timestamp) + { + Touches = touches; + } + + /// + public void GetPosition(UIElement relativeTo, int touchIndex, out int x, out int y) + { + x = Touches[touchIndex].X; + y = Touches[touchIndex].Y; + + relativeTo.PointToClient(ref x, ref y); + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Input/TouchDevice.cs b/source/nanoFramework.Graphics.Wpf/Core/Input/TouchDevice.cs new file mode 100644 index 0000000..216d493 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Input/TouchDevice.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using nanoFramework.UI; +using System; +using nanoFramework.Touch; + +namespace nanoFramework.UI.Input +{ + /// + /// The TouchDevice class represents the stylus/touch device to the + /// members of a context. + /// + public sealed class TouchDevice : InputDevice + { + internal TouchDevice(InputManager inputManager) + { + _inputManager = inputManager; + + _inputManager.InputDeviceEvents[(int)InputManager.InputDeviceType.Touch].PostProcessInput += new ProcessInputEventHandler(PostProcessInput); + } + + /// + /// + /// + public override UIElement Target + { + get + { + return _focus; + } + } + + /// + /// + /// + public override InputManager.InputDeviceType DeviceType + { + get + { + return InputManager.InputDeviceType.Touch; + } + } + + /// + /// + /// + /// + public void SetTarget(UIElement target) + { + _focus = target; + } + + private void PostProcessInput(object sender, ProcessInputEventArgs e) + { + if (!e.StagingItem.Input.Handled) + { + RoutedEvent routedEvent = e.StagingItem.Input.RoutedEvent; + if (routedEvent == InputManager.InputReportEvent) + { + InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; + if (input != null) + { + RawTouchInputReport report = input.Report as RawTouchInputReport; + if (report != null) + { + TouchEventArgs args = new TouchEventArgs( + this, + report.Timestamp, + report.Touches); + + UIElement target = report.Target; + if (report.EventMessage == (byte)TouchMessages.Down) + { + args.RoutedEvent = TouchEvents.TouchDownEvent; + } + else if (report.EventMessage == (byte)TouchMessages.Up) + { + args.RoutedEvent = TouchEvents.TouchUpEvent; + } + else if (report.EventMessage == (byte)TouchMessages.Move) + { + args.RoutedEvent = TouchEvents.TouchMoveEvent; + } + else + throw new Exception("Unknown touch event."); + + args.Source = (target == null ? _focus : target); + e.PushInput(args, e.StagingItem); + } + } + } + } + } + + private InputManager _inputManager; + private UIElement _focus; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Border.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Border.cs new file mode 100644 index 0000000..a46766e --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Border.cs @@ -0,0 +1,171 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using System; + +namespace nanoFramework.Presentation.Controls +{ +/// +/// +/// + public class Border : ContentControl + { + /// + /// + /// + public Border() + { + _borderBrush = new SolidColorBrush(Color.Black); + + _borderLeft = _borderTop = _borderRight = _borderBottom = 1; + } + + /// + /// + /// + public Brush BorderBrush + { + get + { + VerifyAccess(); + + return _borderBrush; + } + + set + { + VerifyAccess(); + + _borderBrush = value; + Invalidate(); + } + } + + /// + /// + /// + /// + /// + /// + /// + public void GetBorderThickness(out int left, out int top, out int right, out int bottom) + { + left = _borderLeft; + top = _borderTop; + right = _borderRight; + bottom = _borderBottom; + } + + /// + /// + /// + /// + public void SetBorderThickness(int length) + { + // no need to verify access here as the next call will do it + SetBorderThickness(length, length, length, length); + } + + /// + /// + /// + /// + /// + /// + /// + public void SetBorderThickness(int left, int top, int right, int bottom) + { + VerifyAccess(); + + // Negative values are not valid (same behavior as desktop WPF). + if ((left < 0) || (right < 0) || (top < 0) || (bottom < 0)) + { + string errorMessage = "'" + left.ToString() + "," + top.ToString() + "," + right.ToString() + "," + bottom.ToString() + "' is not a valid value 'BorderThickness'"; + + throw new ArgumentException(errorMessage); + } + + _borderLeft = left; + _borderTop = top; + _borderRight = right; + _borderBottom = bottom; + InvalidateMeasure(); + } + + /// + /// + /// + /// + /// + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + UIElement child = Child; + if (child != null) + { + child.Arrange(_borderLeft, + _borderTop, + arrangeWidth - _borderLeft - _borderRight, + arrangeHeight - _borderTop - _borderBottom); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + UIElement child = Child; + if (child != null) + { + int horizontalBorder = _borderLeft + _borderRight; + int verticalBorder = _borderTop + _borderBottom; + + child.Measure(availableWidth - horizontalBorder, availableHeight - verticalBorder); + + child.GetDesiredSize(out desiredWidth, out desiredHeight); + desiredWidth += horizontalBorder; + desiredHeight += verticalBorder; + } + else + { + desiredWidth = desiredHeight = 0; + } + } + + /// + /// + /// + /// + public override void OnRender(DrawingContext dc) + { + int width = _renderWidth; + int height = _renderHeight; + + // Border + // + dc.DrawRectangle(_borderBrush, null, 0, 0, width, height); + + // Background + // + if (_background != null) + { + dc.DrawRectangle(_background, null, _borderLeft, _borderTop, + width - _borderLeft - _borderRight, + height - _borderTop - _borderBottom); + } + } + + private Brush _borderBrush; + private int _borderLeft, _borderTop, _borderRight, _borderBottom; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Canvas.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Canvas.cs new file mode 100644 index 0000000..04bfa39 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Canvas.cs @@ -0,0 +1,218 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + /// + public class Canvas : Panel + { + /// + /// + /// + public Canvas() + { + } + + private const int Edge_Left = 0x1; + private const int Edge_Right = 0x2; + private const int Edge_Top = 0x4; + private const int Edge_Bottom = 0x8; + private const int Edge_LeftRight = Edge_Left | Edge_Right; + private const int Edge_TopBottom = Edge_Top | Edge_Bottom; + + private static int GetAnchorValue(UIElement e, int edge) + { + UIElement.Pair anchorInfo = e._anchorInfo; + if (anchorInfo != null) + { + if ((anchorInfo._status & edge) != 0) + { + return ((edge & Edge_LeftRight) != 0) ? anchorInfo._first : anchorInfo._second; + } + } + + return 0; + } + + private static void SetAnchorValue(UIElement e, int edge, int val) + { + e.VerifyAccess(); + + UIElement.Pair anchorInfo = e._anchorInfo; + if (anchorInfo == null) + { + anchorInfo = new UIElement.Pair(); + e._anchorInfo = anchorInfo; + } + + if ((edge & Edge_LeftRight) != 0) + { + anchorInfo._first = val; + anchorInfo._status &= ~Edge_LeftRight; + } + else + { + anchorInfo._second = val; + anchorInfo._status &= ~Edge_TopBottom; + } + + anchorInfo._status |= edge; + + if (e.Parent != null) + { + e.Parent.InvalidateArrange(); + } + } + + /// + /// + /// + /// + /// + public static int GetBottom(UIElement e) + { + return GetAnchorValue(e, Edge_Bottom); + } + + /// + /// + /// + /// + /// + public static void SetBottom(UIElement e, int bottom) + { + SetAnchorValue(e, Edge_Bottom, bottom); + } + + /// + /// + /// + /// + /// + public static int GetLeft(UIElement e) + { + return GetAnchorValue(e, Edge_Left); + } + + /// + /// + /// + /// + /// + public static void SetLeft(UIElement e, int left) + { + SetAnchorValue(e, Edge_Left, left); + } + + /// + /// + /// + /// + /// + public static int GetRight(UIElement e) + { + return GetAnchorValue(e, Edge_Right); + } + + /// + /// + /// + /// + /// + public static void SetRight(UIElement e, int right) + { + SetAnchorValue(e, Edge_Right, right); + } + + /// + /// + /// + /// + /// + public static int GetTop(UIElement e) + { + return GetAnchorValue(e, Edge_Top); + } + + /// + /// + /// + /// + /// + public static void SetTop(UIElement e, int top) + { + SetAnchorValue(e, Edge_Top, top); + } + + /// + /// + /// + /// + /// + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + VerifyAccess(); + + UIElementCollection children = _logicalChildren; + if (children != null) + { + int count = children.Count; + for (int i = 0; i < count; i++) + { + UIElement child = children[i]; + + int childWidth, childHeight; + child.GetDesiredSize(out childWidth, out childHeight); + + UIElement.Pair anchorInfo = child._anchorInfo; + if (anchorInfo != null) + { + int status = anchorInfo._status; + child.Arrange( + ((status & Edge_Right) != 0) ? arrangeWidth - childWidth - anchorInfo._first : anchorInfo._first, + ((status & Edge_Bottom) != 0) ? arrangeHeight - childHeight - anchorInfo._second : anchorInfo._second, + childWidth, + childHeight); + } + else + { + child.Arrange(0, 0, childWidth, childHeight); + } + } + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + UIElementCollection children = _logicalChildren; + if (children != null) + { + for (int i = 0; i < children.Count; i++) + { + children[i].Measure(Media.Constants.MaxExtent, Media.Constants.MaxExtent); + } + } + + desiredWidth = 0; + desiredHeight = 0; + } + + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ContentControl.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ContentControl.cs new file mode 100644 index 0000000..93f2b29 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ContentControl.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public abstract class ContentControl : Control + { + /// + /// + /// + public UIElement Child + { + get + { + if (LogicalChildren.Count > 0) + { + return _logicalChildren[0]; + } + else + { + return null; + } + } + + set + { + VerifyAccess(); + + LogicalChildren.Clear(); + LogicalChildren.Add(value); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + UIElement child = this.Child; + if (child != null) + { + child.Measure(availableWidth, availableHeight); + child.GetDesiredSize(out desiredWidth, out desiredHeight); + } + else + { + desiredWidth = desiredHeight = 0; + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Control.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Control.cs new file mode 100644 index 0000000..9f0be42 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Control.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using nanoFramework.UI; + + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class Control : UIElement + { + /// + /// + /// + public Brush Background + { + get + { + VerifyAccess(); + + return _background; + } + + set + { + VerifyAccess(); + + _background = value; + Invalidate(); + } + } + + /// + /// + /// + public Font Font + { + get + { + return _font; + } + + set + { + VerifyAccess(); + + _font = value; + InvalidateMeasure(); + } + } + + /// + /// + /// + public Brush Foreground + { + get + { + VerifyAccess(); + + return _foreground; + } + + set + { + VerifyAccess(); + + _foreground = value; + Invalidate(); + } + } + + /// + /// + /// + /// + public override void OnRender(DrawingContext dc) + { + if (_background != null) + { + dc.DrawRectangle(_background, null, 0, 0, _renderWidth, _renderHeight); + } + } + + /// + /// + /// + protected internal Brush _background = null; + + /// + /// + /// + protected internal Brush _foreground = new SolidColorBrush(Color.Black); + + /// + /// + /// + protected internal Font _font; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/DockPanel.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/DockPanel.cs new file mode 100644 index 0000000..c1e07c1 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/DockPanel.cs @@ -0,0 +1,232 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// Dock - Enum which describes how to position and stretch the child of a DockPanel. + /// + /// + public enum Dock + { + /// + /// Position this child at the left of the remaining space. + /// + Left, + + /// + /// Position this child at the top of the remaining space. + /// + Top, + + /// + /// Position this child at the right of the remaining space. + /// + Right, + + /// + /// Position this child at the bottom of the remaining space. + /// + Bottom + } + + /// + /// DockPanel is used to size and position children inward from the edges of available space. + /// + /// A enum (see and ) + /// determines on which size a child is placed. Children are stacked in order from these edges until + /// there is no more space; this happens when previous children have consumed all available space, or a child + /// with Dock set to Fill is encountered. + /// + public class DockPanel : Panel + { + private static ArrayList DockPropertiesKeys = new ArrayList(); + private static ArrayList DockPropertiesValues = new ArrayList(); + + /// + /// DockPanel computes a position and final size for each of its children based upon their enum and sizing properties. + /// + /// Width that DockPanel will assume to position children. + /// Height that DockPanel will assume to position children. + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + int totalChildrenCount = Children.Count; + int nonFillChildrenCount = totalChildrenCount - (LastChildFill ? 1 : 0); + + int accumulatedLeft = 0; + int accumulatedTop = 0; + int accumulatedRight = 0; + int accumulatedBottom = 0; + + for (int i = 0; i < totalChildrenCount; i++) + { + UIElement child = Children[i]; + if (child == null) continue; + + int desiredWidth, desiredHeight; + child.GetDesiredSize(out desiredWidth, out desiredHeight); + + int finalX = accumulatedLeft, finalY = accumulatedTop; + int finalWidth = Mathematics.Max(0, arrangeWidth - accumulatedLeft - accumulatedRight); + int finalHeight = Mathematics.Max(0, arrangeHeight - accumulatedTop - accumulatedBottom); + + if (i < nonFillChildrenCount) + switch (DockPanel.GetDock(child)) + { + case Dock.Left: + accumulatedLeft += desiredWidth; + finalWidth = desiredWidth; + break; + + case Dock.Top: + accumulatedTop += desiredHeight; + finalHeight = desiredHeight; + break; + + case Dock.Right: + accumulatedRight += desiredWidth; + finalX = Mathematics.Max(0, arrangeWidth - accumulatedRight); + finalWidth = desiredWidth; + break; + + case Dock.Bottom: + accumulatedBottom += desiredHeight; + finalY = Mathematics.Max(0, arrangeHeight - accumulatedBottom); + finalHeight = desiredHeight; + break; + } + + child.Arrange(finalX, finalY, finalWidth, finalHeight); + } + } + + /// + /// Updates DesiredSize of the DockPanel. + /// Called by parent UIElement. + /// This is the first pass of layout. + /// + /// An "upper limit" that the return value should not exceed. + /// An "upper limit" that the return value should not exceed. + /// The Panel's desired width. + /// The Panel's desired height. + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + int count = Children.Count; + int accumulatedWidth = 0; + int accumulatedHeight = 0; + + desiredWidth = 0; + desiredHeight = 0; + + for (int i = 0; i < count; i++) + { + UIElement child = Children[i]; + + if (child == null) continue; + + int childAvailableWidth = Mathematics.Max(0, availableWidth - accumulatedWidth); + int childAvailableHeight = Mathematics.Max(0, availableHeight - accumulatedHeight); + child.Measure(childAvailableWidth, childAvailableHeight); + + int childDesiredWidth; + int childDesiredHeight; + child.GetDesiredSize(out childDesiredWidth, out childDesiredHeight); + + switch (DockPanel.GetDock(child)) + { + case Dock.Left: + case Dock.Right: + desiredHeight = Mathematics.Max(desiredHeight, accumulatedHeight + childDesiredHeight); + accumulatedWidth += childDesiredWidth; + break; + case Dock.Top: + case Dock.Bottom: + desiredWidth = Mathematics.Max(desiredWidth, accumulatedWidth + childDesiredWidth); + accumulatedHeight += childDesiredHeight; + break; + } + } + + desiredWidth = Mathematics.Max(desiredWidth, accumulatedWidth); + desiredHeight = Mathematics.Max(desiredHeight, accumulatedHeight); + } + + /// + /// Reads the pseudo-attached property Dock from the given element. + /// + /// UIElement from which to read the pseudo-attached property. + /// The property's value. + public static Dock GetDock(UIElement element) + { + if (element == null) + throw new ArgumentNullException("element"); + + int index = DockPropertiesKeys.IndexOf(element); + if (index != -1) + return (Dock)DockPropertiesValues[index]; + + return Dock.Left; + } + + /// + /// Writes the pseudo-attached property Dock to the given element. + /// + /// UIElement to which to write the attached property. + /// The property value to set. + public static void SetDock(UIElement element, Dock dock) + { + if (element == null) + throw new ArgumentNullException("element"); + + if (!IsValidDock(dock)) + throw new ArgumentException(null, "dock"); + + int index = DockPropertiesKeys.IndexOf(element); + if (index == -1) + { + index = + DockPropertiesKeys.Add(element); + DockPropertiesValues.Add(dock); + } + + DockPropertiesValues[index] = dock; + } + + internal static bool IsValidDock(object o) + { + Dock dock = (Dock)o; + + return (dock == Dock.Left + || dock == Dock.Top + || dock == Dock.Right + || dock == Dock.Bottom); + } + + private bool _lastChildFill = true; + /// + /// + /// + public bool LastChildFill + { + get + { + return _lastChildFill; + } + set + { + if (value != _lastChildFill) + { + _lastChildFill = value; + InvalidateMeasure(); + } + } + } + } +} \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Image.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Image.cs new file mode 100644 index 0000000..d656735 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Image.cs @@ -0,0 +1,90 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using nanoFramework.UI; + + +namespace nanoFramework.Presentation.Controls +{ + /// + /// Summary description for Image. + /// + public class Image : UIElement + { +/// +/// +/// + public Image() + { + } + + /// + /// + /// + /// + public Image(Bitmap bmp) + : this() + { + _bitmap = bmp; + } + + /// + /// + /// + public Bitmap Bitmap + { + get + { + VerifyAccess(); + + return _bitmap; + } + + set + { + VerifyAccess(); + + _bitmap = value; + InvalidateMeasure(); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + desiredWidth = desiredHeight = 0; + if (_bitmap != null) + { + desiredWidth = _bitmap.Width; + desiredHeight = _bitmap.Height; + } + } + + /// + /// + /// + /// + public override void OnRender(DrawingContext dc) + { + Bitmap bmp = _bitmap; + if (bmp != null) + { + dc.DrawImage(_bitmap, 0, 0); + } + } + + private Bitmap _bitmap; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/InkCanvas.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/InkCanvas.cs new file mode 100644 index 0000000..30d5559 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/InkCanvas.cs @@ -0,0 +1,192 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI.Input; +using nanoFramework.Presentation; +using nanoFramework.Presentation.Media; +using System; +using nanoFramework.UI; +using nanoFramework.Touch; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class DrawingAttributes + { + /// + /// + /// + public Color Color = Color.Black; + } + + /// + /// Note: InkCanvas control is not movable at runtime. This requires complex logic, with + /// no customer scenario at this moment. + /// + public class InkCanvas : UIElement + { + /// + /// + /// + /// + /// + /// + /// + public InkCanvas(int left, int top, int width, int height) + : this(left, top, width, height, 1) + { + } + /// + /// + /// + /// + /// + /// + /// + /// + public InkCanvas(int left, int top, int width, int height, int borderWidth) + { + Init(left, top, width, height, borderWidth); + Canvas.SetLeft(this, left); + Canvas.SetTop(this, top); + HorizontalAlignment = HorizontalAlignment.Left; + VerticalAlignment = VerticalAlignment.Top; + } + + /// + /// + /// + ~InkCanvas() + { + } + + /// + /// + /// + /// + protected override void OnTouchDown(TouchEventArgs e) + { + int x, y, w, h; + + GetLayoutOffset(out x, out y); + GetRenderSize(out w, out h); + + x += _left; + y += _top; + + TouchCapture.Capture(this); + Ink.SetInkRegion(0, x, y, x + w, y + h, _borderWidth, (int)_defaultDrawingAttributes.Color, 1, _bitmap); + } + + /// + /// + /// + /// + protected override void OnTouchUp(TouchEventArgs e) + { + Ink.ResetInkRegion(); + Invalidate(); + } + /// + /// + /// + /// + public override void OnRender(nanoFramework.Presentation.Media.DrawingContext dc) + { + if (_bitmap != null) + { + dc.DrawImage(_bitmap, 0, 0); + } + } + + /// + /// + /// + public void Clear() + { + _bitmap.DrawRectangle(Color.Black, _borderWidth, 0, 0, _width, _height, 0, 0, Color.White, 0, 0, Color.White, 0, 0, Bitmap.OpacityOpaque); + Invalidate(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + protected virtual void Init(int left, int top, int width, int height, int borderWidth) + { + _width = width; + _height = height; + _left = left; + _top = top; + _borderWidth = borderWidth; + + int x1 = _left; + int y1 = _top; + + _bitmap = new Bitmap(_width, _height); + _bitmap.DrawRectangle(Color.Black, _borderWidth, 0, 0, _width, _height, 0, 0, Color.White, 0, 0, Color.White, 0, 0, Bitmap.OpacityOpaque); + + if ((x1 < 0) || ((x1 + _width) > SystemMetrics.ScreenWidth) || + (y1 < 0) || ((y1 + _height) > SystemMetrics.ScreenHeight)) + { + throw new ArgumentException("screenlimit"); + } + } + /// + /// + /// + public DrawingAttributes DefaultDrawingAttributes + { + get + { + return _defaultDrawingAttributes; + } + + set + { + _defaultDrawingAttributes = value; + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + desiredWidth = (availableWidth > _width) ? _width : availableWidth; + desiredHeight = (availableHeight > _height) ? _height : availableHeight; + } + + /// + /// + /// + protected DrawingAttributes _defaultDrawingAttributes = new DrawingAttributes(); + + /// + /// + /// + protected Bitmap _bitmap = null; + + + private int _borderWidth; + private int _width; + private int _height; + private int _top; + private int _left; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBox.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBox.cs new file mode 100644 index 0000000..f67ba18 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBox.cs @@ -0,0 +1,300 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.UI.Input; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class ListBox : ContentControl + { + /// + /// + /// + public ListBox() + { + _panel = new StackPanel(); + _scrollViewer = new ScrollViewer(); + _scrollViewer.Child = _panel; + this.LogicalChildren.Add(_scrollViewer); + } + + /// + /// + /// + public ListBoxItemCollection Items + { + get + { + VerifyAccess(); + + if (_items == null) + { + _items = new ListBoxItemCollection(this, _panel.Children); + } + + return _items; + } + } + + /// + /// + /// + public event SelectionChangedEventHandler SelectionChanged + { + add + { + VerifyAccess(); + _selectionChanged += value; + } + + remove + { + VerifyAccess(); + _selectionChanged -= value; + } + } + + /// + /// + /// + public int SelectedIndex + { + get + { + return _selectedIndex; + } + + set + { + VerifyAccess(); + + if (_selectedIndex != value) + { + if (value < -1) + { + throw new ArgumentOutOfRangeException("SelectedIndex"); + } + + ListBoxItem item = (_items != null && value >= 0 && value < _items.Count) ? _items[value] : null; + + if (item != null && !item.IsSelectable) + { + throw new InvalidOperationException("Item is not selectable"); + } + + ListBoxItem previousItem = SelectedItem; + if (previousItem != null) + { + previousItem.OnIsSelectedChanged(false); + } + + SelectionChangedEventArgs args = new SelectionChangedEventArgs(_selectedIndex, value); + _selectedIndex = value; + + if (item != null) + { + item.OnIsSelectedChanged(true); + } + + if (_selectionChanged != null) + { + _selectionChanged(this, args); + } + } + } + } + + /// + /// + /// + public ListBoxItem SelectedItem + { + get + { + if (_items != null && _selectedIndex >= 0 && _selectedIndex < _items.Count) + { + return _items[_selectedIndex]; + } + + return null; + } + + set + { + VerifyAccess(); + + int index = Items.IndexOf(value); + if (index != -1) + { + SelectedIndex = index; + } + } + } + + /// + /// + /// + /// + public void ScrollIntoView(ListBoxItem item) + { + VerifyAccess(); + + if (!Items.Contains(item)) return; + + int panelX, panelY; + _panel.GetLayoutOffset(out panelX, out panelY); + + int x, y; + item.GetLayoutOffset(out x, out y); + + int top = y + panelY; + int bottom = top + item._renderHeight; + + // Make sure bottom of item is in view + // + if (bottom > _scrollViewer._renderHeight) + { + _scrollViewer.VerticalOffset -= (_scrollViewer._renderHeight - bottom); + } + + // Make sure top of item is in view + // + if (top < 0) + { + _scrollViewer.VerticalOffset += top; + } + } + + /// + /// + /// + /// + protected override void OnButtonDown(ButtonEventArgs e) + { + if (e.Button == Button.VK_DOWN && _selectedIndex < Items.Count - 1) + { + int newIndex = _selectedIndex + 1; + while (newIndex < Items.Count && !Items[newIndex].IsSelectable) newIndex++; + + if (newIndex < Items.Count) + { + SelectedIndex = newIndex; + ScrollIntoView(SelectedItem); + e.Handled = true; + } + } + else if (e.Button == Button.VK_UP && _selectedIndex > 0) + { + int newIndex = _selectedIndex - 1; + while (newIndex >= 0 && !Items[newIndex].IsSelectable) newIndex--; + + if (newIndex >= 0) + { + SelectedIndex = newIndex; + ScrollIntoView(SelectedItem); + e.Handled = true; + } + } + } + + // + // Scrolling events re-exposed from the ScrollViewer + // + + /// + /// Event handler if the scroll changes. + /// + public event ScrollChangedEventHandler ScrollChanged + { + add { _scrollViewer.ScrollChanged += value; } + remove { _scrollViewer.ScrollChanged -= value; } + } + + /// + /// Horizontal offset of the scroll. + /// + public int HorizontalOffset + { + get + { + return _scrollViewer.HorizontalOffset; + } + + set + { + _scrollViewer.HorizontalOffset = value; + } + } + + /// + /// Vertical offset of the scroll. + /// + public int VerticalOffset + { + get + { + return _scrollViewer.VerticalOffset; + } + + set + { + _scrollViewer.VerticalOffset = value; + } + } + + /// + /// Extent height of the scroll area. + /// + public int ExtentHeight + { + get + { + return _scrollViewer.ExtentHeight; + } + } + + /// + /// Extent width of the scroll area. + /// + public int ExtentWidth + { + get + { + return _scrollViewer.ExtentWidth; + } + } + + /// + /// The scrolling style. + /// + public ScrollingStyle ScrollingStyle + { + get + { + return _scrollViewer.ScrollingStyle; + } + + set + { + _scrollViewer.ScrollingStyle = value; + } + } + + internal ScrollViewer _scrollViewer; + internal StackPanel _panel; + private int _selectedIndex = -1; + private SelectionChangedEventHandler _selectionChanged; + + private ListBoxItemCollection _items; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItem.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItem.cs new file mode 100644 index 0000000..4378a99 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItem.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class ListBoxItem : ContentControl + { + /// + /// + /// + public bool IsSelected + { + get + { + return (_listBox != null && _listBox.SelectedItem == this); + } + } + + /// + /// + /// + public bool IsSelectable + { + get + { + return _isSelectable; + } + + set + { + VerifyAccess(); + + if (_isSelectable != value) + { + _isSelectable = value; + if (!value && IsSelected) + { + _listBox.SelectedIndex = -1; + } + } + } + } + + /// + /// + /// + /// + protected internal virtual void OnIsSelectedChanged(bool isSelected) + { + } + + internal void SetListBox(ListBox listbox) + { + this._listBox = listbox; + if (IsSelected && !IsSelectable) + { + _listBox.SelectedIndex = -1; + } + } + + private bool _isSelectable = true; + private ListBox _listBox; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItemCollection.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItemCollection.cs new file mode 100644 index 0000000..f3b0433 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ListBoxItemCollection.cs @@ -0,0 +1,184 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class ListBoxItemCollection : ICollection + { + UIElementCollection _items; + + /// + /// + /// + /// + /// + public ListBoxItemCollection(ListBox listBox, UIElementCollection items) + { + _listBox = listBox; + _items = items; + } + + /// + /// + /// + /// + /// + public int Add(ListBoxItem item) + { + int pos = _items.Add(item); + item.SetListBox(_listBox); + return pos; + } + + /// + /// + /// + /// + /// + public int Add(UIElement element) + { + ListBoxItem item = new ListBoxItem(); + item.Child = element; + return Add(item); + } + + /// + /// + /// + public void Clear() + { + _items.Clear(); + } + + + /// + /// + /// + /// + /// + public bool Contains(ListBoxItem item) + { + return _items.Contains(item); + } + + /// + /// + /// + /// + /// + public ListBoxItem this[int index] + { + get { return (ListBoxItem)_items[index]; } + set { _items[index] = value; value.SetListBox(_listBox); } + } + + /// + /// + /// + /// + /// + public int IndexOf(ListBoxItem item) + { + return _items.IndexOf(item); + } + + /// + /// + /// + /// + /// + public void Insert(int index, ListBoxItem item) + { + _items.Insert(index, item); + item.SetListBox(_listBox); + } + + /// + /// + /// + /// + public void Remove(ListBoxItem item) + { + _items.Remove(item); + item.SetListBox(null); + } + + /// + /// + /// + /// + public void RemoveAt(int index) + { + if (index >= 0 && index < _items.Count) + { + this[index].SetListBox(null); + } + + _items.RemoveAt(index); + } + + #region ICollection Members + + /// + /// + /// + /// + /// + public void CopyTo(Array array, int index) + { + _items.CopyTo(array, index); + } + + /// + /// + /// + public int Count + { + get { return _items.Count; } + } + + /// + /// + /// + public bool IsSynchronized + { + get { return _items.IsSynchronized; } + } + + /// + /// + /// + public object SyncRoot + { + get { return _items.SyncRoot; } + } + + #endregion + + #region IEnumerable Members + + /// + /// + /// + /// + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_items).GetEnumerator(); + } + + #endregion + + private ListBox _listBox; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Orientation.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Orientation.cs new file mode 100644 index 0000000..5a97678 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Orientation.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Controls + +{ + /// + /// + /// + public enum Orientation + { + /// + /// + /// + Horizontal, + + /// + /// + /// + Vertical + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Panel.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Panel.cs new file mode 100644 index 0000000..ccd30d8 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Panel.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class Panel : UIElement + { + /// + /// + /// + public UIElementCollection Children + { + get + { + return LogicalChildren; + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + desiredWidth = desiredHeight = 0; + UIElementCollection children = _logicalChildren; + if (children != null) + { + for (int i = 0; i < children.Count; i++) + { + UIElement child = children[i]; + child.Measure(availableWidth, availableHeight); + int childDesiredWidth, childDesiredHeight; + child.GetDesiredSize(out childDesiredWidth, out childDesiredHeight); + desiredWidth = Mathematics.Max(desiredWidth, childDesiredWidth); + desiredHeight = Mathematics.Max(desiredHeight, childDesiredHeight); + desiredWidth = Mathematics.Max(desiredWidth, childDesiredWidth); + desiredHeight = Mathematics.Max(desiredHeight, childDesiredHeight); + } + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventArgs.cs new file mode 100644 index 0000000..cbede61 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventArgs.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class ScrollChangedEventArgs : EventArgs + { + /// + /// + /// + public readonly int HorizontalChange; + /// + /// + /// + public readonly int HorizontalOffset; + + /// + /// + /// + public readonly int VerticalChange; + /// + /// + /// + public readonly int VerticalOffset; + + /// + /// + /// + /// + /// + /// + /// + public ScrollChangedEventArgs(int offsetX, int offsetY, int offsetChangeX, int offsetChangeY) + { + HorizontalOffset = offsetX; + HorizontalChange = offsetChangeX; + + VerticalOffset = offsetY; + VerticalChange = offsetChangeY; + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventHandler.cs new file mode 100644 index 0000000..47b2831 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollChangedEventHandler.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Controls +{ +/// +/// +/// +/// +/// + public delegate void ScrollChangedEventHandler(object sender, ScrollChangedEventArgs args); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollViewer.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollViewer.cs new file mode 100644 index 0000000..68f5958 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollViewer.cs @@ -0,0 +1,383 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Diagnostics; +using nanoFramework.UI.Input; +using nanoFramework.UI; + + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class ScrollViewer : ContentControl + { + /// + /// + /// + public ScrollViewer() + { + this.HorizontalAlignment = HorizontalAlignment.Left; + this.VerticalAlignment = VerticalAlignment.Stretch; + } + /// + /// + /// + public event ScrollChangedEventHandler ScrollChanged + { + add + { + VerifyAccess(); + + _scrollChanged += value; + } + + remove + { + VerifyAccess(); + + _scrollChanged -= value; + } + } + + /// + /// + /// + public int HorizontalOffset + { + get + { + return _horizontalOffset; + } + + set + { + VerifyAccess(); + + if (value < 0) + { + value = 0; + } + else if ((_flags & Flags.NeverArranged) == 0 && value > _scrollableWidth) + { + value = _scrollableWidth; + } + + if (_horizontalOffset != value) + { + _horizontalOffset = value; + InvalidateArrange(); + } + } + } + + /// + /// + /// + public int VerticalOffset + { + get + { + return _verticalOffset; + } + + set + { + VerifyAccess(); + + if (value < 0) + { + value = 0; + } + else if ((_flags & Flags.NeverArranged) == 0 && value > _scrollableHeight) + { + value = _scrollableHeight; + } + + if (_verticalOffset != value) + { + _verticalOffset = value; + InvalidateArrange(); + } + } + } + + /// + /// + /// + public int ExtentHeight + { + get + { + return _extentHeight; + } + } + + /// + /// + /// + public int ExtentWidth + { + get + { + return _extentWidth; + } + } + + /// + /// + /// + public int LineWidth + { + get + { + return _lineWidth; + } + + set + { + VerifyAccess(); + + if (value < 0) + { + throw new System.ArgumentOutOfRangeException("LineWidth"); + } + + _lineWidth = value; + } + } + + /// + /// + /// + public int LineHeight + { + get + { + return _lineHeight; + } + + set + { + VerifyAccess(); + + if (value < 0) + { + throw new System.ArgumentOutOfRangeException("LineHeight"); + } + + _lineHeight = value; + } + } + + /// + /// + /// + public ScrollingStyle ScrollingStyle + { + get + { + return _scrollingStyle; + } + + set + { + VerifyAccess(); + + if (value < ScrollingStyle.First || value > ScrollingStyle.Last) + { + throw new ArgumentOutOfRangeException("ScrollingStyle", "Invalid Enum"); + } + + _scrollingStyle = value; + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + UIElement child = this.Child; + if (child != null && child.Visibility != Visibility.Collapsed) + { + child.Measure((HorizontalAlignment == HorizontalAlignment.Stretch) ? Media.Constants.MaxExtent : availableWidth, (VerticalAlignment == VerticalAlignment.Stretch) ? Media.Constants.MaxExtent : availableHeight); + child.GetDesiredSize(out desiredWidth, out desiredHeight); + _extentHeight = child._unclippedHeight; + _extentWidth = child._unclippedWidth; + } + else + { + desiredWidth = desiredHeight = 0; + _extentHeight = _extentWidth = 0; + } + } + + /// + /// + /// + /// + /// + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + UIElement child = this.Child; + if (child != null) + { + // Clip scroll-offset if necessary + // + _scrollableWidth = Mathematics.Max(0, ExtentWidth - arrangeWidth); + _scrollableHeight = Mathematics.Max(0, ExtentHeight - arrangeHeight); + _horizontalOffset = Mathematics.Min(_horizontalOffset, _scrollableWidth); + _verticalOffset = Mathematics.Min(_verticalOffset, _scrollableHeight); + + //Debug.Assert(_horizontalOffset >= 0); + //Debug.Assert(_verticalOffset >= 0); + + child.Arrange(-_horizontalOffset, + -_verticalOffset, + Mathematics.Max(arrangeWidth, ExtentWidth), + Mathematics.Max(arrangeHeight, ExtentHeight)); + } + else + { + _horizontalOffset = _verticalOffset = 0; + } + + InvalidateScrollInfo(); + } + + /// + /// + /// + public void LineDown() + { + VerticalOffset += _lineHeight; + } + + /// + /// + /// + public void LineLeft() + { + HorizontalOffset -= _lineWidth; + } + + /// + /// + /// + public void LineRight() + { + HorizontalOffset += _lineWidth; + } + + /// + /// + /// + public void LineUp() + { + VerticalOffset -= _lineHeight; + } + + /// + /// + /// + public void PageDown() + { + VerticalOffset += ActualHeight; + } + + /// + /// + /// + public void PageLeft() + { + HorizontalOffset -= ActualWidth; + } + + /// + /// + /// + public void PageRight() + { + HorizontalOffset += ActualWidth; + } + + /// + /// + /// + public void PageUp() + { + VerticalOffset -= ActualHeight; + } + + private void InvalidateScrollInfo() + { + if (_scrollChanged != null) + { + int deltaX = _horizontalOffset - _previousHorizontalOffset; + int deltaY = _verticalOffset - _previousVerticalOffset; + _scrollChanged(this, new ScrollChangedEventArgs(_horizontalOffset, _verticalOffset, deltaX, deltaY)); + } + + _previousHorizontalOffset = _horizontalOffset; + _previousVerticalOffset = _verticalOffset; + } + + /// + /// + /// + /// + protected override void OnButtonDown(ButtonEventArgs e) + { + switch (e.Button) + { + case Button.VK_UP: + if (_scrollingStyle == ScrollingStyle.LineByLine) LineUp(); else PageUp(); + break; + case Button.VK_DOWN: + if (_scrollingStyle == ScrollingStyle.LineByLine) LineDown(); else PageDown(); + break; + case Button.VK_LEFT: + if (_scrollingStyle == ScrollingStyle.LineByLine) LineLeft(); else PageLeft(); + break; + case Button.VK_RIGHT: + if (_scrollingStyle == ScrollingStyle.LineByLine) LineRight(); else PageRight(); + break; + default: + return; + } + + if (_previousHorizontalOffset != _horizontalOffset || _previousVerticalOffset != _verticalOffset) + { + e.Handled = true; + } + } + + private int _previousHorizontalOffset; + private int _previousVerticalOffset; + private int _horizontalOffset; + private int _verticalOffset; + private int _extentWidth; + private int _extentHeight; + private int _scrollableWidth; + private int _scrollableHeight; + + private int _lineHeight = 1; + private int _lineWidth = 1; + + private ScrollingStyle _scrollingStyle = ScrollingStyle.LineByLine; + + private ScrollChangedEventHandler _scrollChanged; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollingStyle.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollingStyle.cs new file mode 100644 index 0000000..2693209 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/ScrollingStyle.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public enum ScrollingStyle + { + /// + /// + /// + First, + + /// + /// + /// + LineByLine = First, + + /// + /// + /// + PageByPage, + + /// + /// + /// + Last = PageByPage + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventArgs.cs new file mode 100644 index 0000000..0646a57 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventArgs.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class SelectionChangedEventArgs : EventArgs + { + /// + /// + /// + public readonly int PreviousSelectedIndex; + + /// + /// + /// + public readonly int SelectedIndex; + + /// + /// + /// + /// + /// + public SelectionChangedEventArgs(int previousIndex, int newIndex) + { + PreviousSelectedIndex = previousIndex; + SelectedIndex = newIndex; + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventHandler.cs new file mode 100644 index 0000000..6c661f7 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/SelectionChangedEventHandler.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + /// + /// + public delegate void SelectionChangedEventHandler(object sender, SelectionChangedEventArgs args); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/StackPanel.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/StackPanel.cs new file mode 100644 index 0000000..11be2f1 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/StackPanel.cs @@ -0,0 +1,148 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class StackPanel : Panel + { + /// + /// + /// + public StackPanel() + : this(Orientation.Vertical) + { + } + + /// + /// + /// + /// + public StackPanel(Orientation orientation) + { + this.Orientation = orientation; + } + + /// + /// + /// + public Orientation Orientation + { + get + { + return _orientation; + } + + set + { + VerifyAccess(); + + _orientation = value; + InvalidateMeasure(); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + desiredWidth = 0; + desiredHeight = 0; + + bool fHorizontal = (Orientation == Orientation.Horizontal); + + // Iterate through children. + // + int nChildren = Children.Count; + for (int i = 0; i < nChildren; i++) + { + UIElement child = Children[i]; + + if (child.Visibility != Visibility.Collapsed) + { + // Measure the child. + // - according to Avalon specs, the stack panel should not constrain + // a child's measure in the direction of the stack + // + if (fHorizontal) + { + child.Measure(Media.Constants.MaxExtent, availableHeight); + } + else + { + child.Measure(availableWidth, Media.Constants.MaxExtent); + } + + // Accumulate child size. + // + int childDesiredWidth, childDesiredHeight; + child.GetDesiredSize(out childDesiredWidth, out childDesiredHeight); + + if (fHorizontal) + { + desiredWidth += childDesiredWidth; + desiredHeight = Mathematics.Max(desiredHeight, childDesiredHeight); + } + else + { + desiredWidth = Mathematics.Max(desiredWidth, childDesiredWidth); + desiredHeight += childDesiredHeight; + } + } + } + } + + /// + /// + /// + /// + /// + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + bool fHorizontal = (Orientation == Orientation.Horizontal); + int previousChildSize = 0; + int childPosition = 0; + + // Arrange and Position Children. + // + int nChildren = Children.Count; + for (int i = 0; i < nChildren; ++i) + { + UIElement child = Children[i]; + if (child.Visibility != Visibility.Collapsed) + { + childPosition += previousChildSize; + int childDesiredWidth, childDesiredHeight; + child.GetDesiredSize(out childDesiredWidth, out childDesiredHeight); + + if (fHorizontal) + { + previousChildSize = childDesiredWidth; + child.Arrange(childPosition, 0, previousChildSize, Mathematics.Max(arrangeHeight, childDesiredHeight)); + } + else + { + previousChildSize = childDesiredHeight; + child.Arrange(0, childPosition, Mathematics.Max(arrangeWidth, childDesiredWidth), previousChildSize); + } + } + } + } + + private Orientation _orientation; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Text.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Text.cs new file mode 100644 index 0000000..a83cd0d --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/Text.cs @@ -0,0 +1,264 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using System; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class Text : UIElement + { + /// + /// + /// + public Text() + : this(null, null) + { + } + + /// + /// + /// + /// + public Text(string content) + : this(null, content) + { + } + + /// + /// + /// + /// + /// + public Text(Font font, string content) + { + _text = content; + _font = font; + _foreColor = Color.Black; + } + + /// + /// + /// + public Font Font + { + get + { + return _font; + } + + set + { + VerifyAccess(); + + _font = value; + InvalidateMeasure(); + } + } + + /// + /// + /// + public Color ForeColor + { + get + { + return _foreColor; + } + + set + { + VerifyAccess(); + + _foreColor = value; + Invalidate(); + } + } + + /// + /// + /// + public string TextContent + { + get + { + return _text; + } + + set + { + VerifyAccess(); + + if (_text != value) + { + _text = value; + InvalidateMeasure(); + } + } + } + + /// + /// + /// + public TextTrimming Trimming + { + get + { + return _trimming; + } + + set + { + VerifyAccess(); + + _trimming = value; + Invalidate(); + } + } + + /// + /// + /// + public TextAlignment TextAlignment + { + get + { + return _alignment; + } + + set + { + VerifyAccess(); + + _alignment = value; + Invalidate(); + } + } + + /// + /// + /// + public int LineHeight + { + //Don't support IgnoreDescent, etc... + get + { + return (_font != null) ? (_font.Height + _font.ExternalLeading) : 0; + } + } + + /// + /// + /// + public bool TextWrap + { + get + { + return _textWrap; + } + + set + { + VerifyAccess(); + + _textWrap = value; + InvalidateMeasure(); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + if (_font != null && _text != null && _text.Length > 0) + { + uint flags = Bitmap.DT_IgnoreHeight | Bitmap.DT_WordWrap; + + switch (_alignment) + { + case TextAlignment.Left: + flags |= Bitmap.DT_AlignmentLeft; + break; + case TextAlignment.Right: + flags |= Bitmap.DT_AlignmentRight; + break; + case TextAlignment.Center: + flags |= Bitmap.DT_AlignmentCenter; + break; + default: + throw new NotSupportedException(); + } + + switch (_trimming) + { + case TextTrimming.CharacterEllipsis: + flags |= Bitmap.DT_TrimmingCharacterEllipsis; + break; + case TextTrimming.WordEllipsis: + flags |= Bitmap.DT_TrimmingWordEllipsis; + break; + } + + _font.ComputeTextInRect(_text, out desiredWidth, out desiredHeight, 0, 0, availableWidth, 0, flags); + + if (_textWrap == false) desiredHeight = _font.Height; + } + else + { + desiredWidth = 0; + desiredHeight = (_font != null) ? _font.Height : 0; + } + } + + /// + /// + /// + /// + public override void OnRender(DrawingContext dc) + { + if (_font != null && _text != null) + { + int height = _textWrap ? _renderHeight : _font.Height; + + string txt = _text; + dc.DrawText(ref txt, _font, _foreColor, 0, 0, _renderWidth, height, _alignment, _trimming); + } + } + +#if NANOCLR_TRACE + public override string ToString() + { + return base.ToString() + " [" + this.TextContent + "]"; + } + +#endif + + /// + /// + /// + protected Font _font; + private Color _foreColor; + + /// + /// + /// + protected string _text; + private bool _textWrap; + private TextTrimming _trimming = TextTrimming.WordEllipsis; + private TextAlignment _alignment = TextAlignment.Left; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextFlow.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextFlow.cs new file mode 100644 index 0000000..94c3328 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextFlow.cs @@ -0,0 +1,486 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using nanoFramework; +using System; +using System.Collections; +using System.Diagnostics; +using nanoFramework.UI.Input; +using nanoFramework.Touch; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class TextFlow : UIElement + { + /// + /// + /// + public TextRunCollection TextRuns; + + internal class TextLine + { + public const int DefaultLineHeight = 10; + + public TextRun[] Runs; + public int Baseline; + public int Height; + private int _width; + + public TextLine(ArrayList runs, int height, int baseline) + { + Runs = (TextRun[])runs.ToArray(typeof(TextRun)); + this.Baseline = baseline; + this.Height = height; + } + + // Empty line with specified height + public TextLine(int height) + { + Runs = new TextRun[0]; + this.Height = height; + } + + public int Width + { + get + { + if (_width == 0) + { + int lineWidth = 0; + int width, height; + for (int i = Runs.Length - 1; i >= 0; i--) + { + Runs[i].GetSize(out width, out height); + lineWidth += width; + } + + _width = lineWidth; + } + + return _width; + } + } + } + + internal ArrayList _lineCache; + internal TextAlignment _alignment = TextAlignment.Left; + internal int _currentLine; + + internal ScrollingStyle _scrollingStyle = ScrollingStyle.LineByLine; + + /// + /// + /// + public TextFlow() + { + TextRuns = new TextRunCollection(this); + } + + /// + /// + /// + public ScrollingStyle ScrollingStyle + { + get + { + return _scrollingStyle; + } + + set + { + VerifyAccess(); + + if (value < ScrollingStyle.First || value > ScrollingStyle.Last) + { + throw new ArgumentOutOfRangeException("ScrollingStyle", "Invalid Enum"); + } + + _scrollingStyle = value; + } + } + + /// + /// + /// + public TextAlignment TextAlignment + { + get + { + return _alignment; + } + + set + { + VerifyAccess(); + + _alignment = value; + Invalidate(); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + desiredWidth = availableWidth; + desiredHeight = availableHeight; + + if (availableWidth > 0) + { + _lineCache = SplitLines(availableWidth); + + // Compute total desired height + // + int totalHeight = 0; + for (int lineNumber = _lineCache.Count - 1; lineNumber >= 0; --lineNumber) + { + totalHeight += ((TextLine)_lineCache[lineNumber]).Height; + } + + desiredHeight = totalHeight; + } + } + + internal bool LineScroll(bool up) + { + if (_lineCache == null) return false; + + if (up && _currentLine > 0) + { + _currentLine--; + Invalidate(); + return true; + } + else if (!up && _currentLine < _lineCache.Count - 1) + { + _currentLine++; + Invalidate(); + return true; + } + + return false; + } + + internal bool PageScroll(bool up) + { + if (_lineCache == null) return false; + + int lineNumber = _currentLine; + int nLines = _lineCache.Count; + int pageHeight = _renderHeight; + int heightOfLines = 0; + + if (up) + { + // Determine first line of previous page + // + while (lineNumber > 0) + { + lineNumber--; + TextLine line = (TextLine)_lineCache[lineNumber]; + heightOfLines += line.Height; + if (heightOfLines > pageHeight) + { + lineNumber++; + break; + } + } + } + else + { + // Determine first line of next page + // + while (lineNumber < nLines) + { + TextLine line = (TextLine)_lineCache[lineNumber]; + heightOfLines += line.Height; + if (heightOfLines > pageHeight) + { + break; + } + + lineNumber++; + } + + if (lineNumber == nLines) lineNumber = nLines - 1; + } + + if (_currentLine != lineNumber) + { + _currentLine = lineNumber; + Invalidate(); + return true; + } + else + { + return false; + } + } + + // Given an available width, takes the TextRuns and arranges them into + // separate lines, breaking where possible at whitespace. + // + internal ArrayList SplitLines(int availableWidth) + { + //Debug.Assert(availableWidth > 0); + + int lineWidth = 0; + + ArrayList remainingRuns = new ArrayList(); + for (int i = 0; i < TextRuns.Count; i++) + { + remainingRuns.Add(TextRuns[i]); + } + + ArrayList lineCache = new ArrayList(); + ArrayList runsOnCurrentLine = new ArrayList(); + + while (remainingRuns.Count > 0) + { + bool newLine = false; + + TextRun run = (TextRun)remainingRuns[0]; + remainingRuns.RemoveAt(0); + + if (run.IsEndOfLine) + { + newLine = true; + } + else + { + // Add run to end of current line + // + int runWidth, runHeight; + run.GetSize(out runWidth, out runHeight); + lineWidth += runWidth; + runsOnCurrentLine.Add(run); + + // If the line length now extends beyond the available width, attempt to break the line + // + if (lineWidth > availableWidth) + { + bool onlyRunOnCurrentLine = (runsOnCurrentLine.Count == 1); + + if (run.Text.Length > 1) + { + runsOnCurrentLine.Remove(run); + + TextRun run1, run2; + if (run.Break(runWidth - (lineWidth - availableWidth), out run1, out run2, onlyRunOnCurrentLine)) + { + // Break and put overflow on next line + // + if (run1 != null) + { + runsOnCurrentLine.Add(run1); + } + + if (run2 != null) + { + remainingRuns.Insert(0, run2); + } + } + else if (!onlyRunOnCurrentLine) + { + // No break found - put it on its own line + // + remainingRuns.Insert(0, run); + } + } + else // run.Text.Length == 1 + { + if (!onlyRunOnCurrentLine) + { + runsOnCurrentLine.Remove(run); + remainingRuns.Insert(0, run); + } + } + + newLine = true; + } + + if (lineWidth >= availableWidth || remainingRuns.Count == 0) + { + newLine = true; + } + } + + // If we're done with this line, add it to the list + // + if (newLine) + { + int lineHeight = 0; + int baseLine = 0; + int nRuns = runsOnCurrentLine.Count; + if (nRuns > 0) + { + // Compute line height & baseline + for (int i = 0; i < nRuns; i++) + { + Font font = ((TextRun)runsOnCurrentLine[i]).Font; + int h = font.Height + font.ExternalLeading; + if (h > lineHeight) + { + lineHeight = h; + baseLine = font.Ascent; + } + } + + // Add line to cache + lineCache.Add(new TextLine(runsOnCurrentLine, lineHeight, baseLine)); + } + else + { + // Empty line. Just borrow the height from the previous line, if any + lineHeight = (lineCache.Count) > 0 ? + ((TextLine)lineCache[lineCache.Count - 1]).Height : + TextLine.DefaultLineHeight; + lineCache.Add(new TextLine(lineHeight)); + } + + // Move onto next line + // + runsOnCurrentLine.Clear(); + lineWidth = 0; + } + } + + return lineCache; + } + + /// + /// + /// + /// + protected override void OnButtonDown(ButtonEventArgs e) + { + if (e.Button == Button.VK_UP || e.Button == Button.VK_DOWN) + { + bool isUp = (e.Button == Button.VK_UP); + switch (_scrollingStyle) + { + case ScrollingStyle.PageByPage: + e.Handled = PageScroll(isUp); + break; + case ScrollingStyle.LineByLine: + e.Handled = LineScroll(isUp); + break; + default: + //Debug.Assert(false, "Unknown ScrollingStyle"); + break; + } + } + } + + /// + /// + /// + /// + public override void OnRender(Media.DrawingContext dc) + { + if (_lineCache == null || _lineCache.Count == 0) + { + return; + } + + int nLines = _lineCache.Count; + int top = 0; + + int width, height; + GetRenderSize(out width, out height); + + // Draw each line of Text + // + int lineNumber = _currentLine; + while (lineNumber < nLines) + { + TextLine line = (TextLine)_lineCache[lineNumber]; + if (top + line.Height > height) + { + break; + } + + TextRun[] runs = line.Runs; + + int x; + switch (_alignment) + { + case TextAlignment.Left: + x = 0; + break; + + case TextAlignment.Center: + x = (width - line.Width) >> 1; // >> 1 is the same as div by 2 + break; + + case TextAlignment.Right: + x = width - line.Width; + break; + + default: + throw new NotSupportedException(); + } + + for (int i = 0; i < runs.Length; i++) + { + TextRun run = runs[i]; + int w, h; + run.GetSize(out w, out h); + int y = top + line.Baseline - run.Font.Ascent; + dc.DrawText(run.Text, run.Font, run.ForeColor, x, y); + x += w; + } + + top += line.Height; + lineNumber++; + } + } + + /// + /// + /// + public int TopLine + { + get + { + return _currentLine; + } + + set + { + VerifyAccess(); + + Object temp = _lineCache[value]; // Easy way to make sure _lineCache is valid and value is within range + + _currentLine = value; + Invalidate(); + } + } + + /// + /// + /// + public int LineCount + { + get + { + return _lineCache.Count; // if _lineCache is null, it'll throw a NullReferenceException + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRun.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRun.cs new file mode 100644 index 0000000..ecd4829 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRun.cs @@ -0,0 +1,194 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using System; +using System.Diagnostics; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// + /// + public class TextRun + { + /// + /// + /// + public readonly string Text; + /// + /// + /// + public readonly Font Font; + /// + /// + /// + public readonly Color ForeColor; + + internal bool IsEndOfLine; + + /// + /// + /// + protected int _width; + /// + /// + /// + protected int _height; + + private TextRun() + { + } + + /// + /// + /// + /// + /// + /// + public TextRun(string text, Font font, Color foreColor) + { + if (text == null || text.Length == 0) + { + throw new ArgumentNullException("Text must be non-null and non-empty"); + } + + if (font == null) + { + throw new ArgumentNullException("font must be non-null"); + } + + this.Text = text; + this.Font = font; + this.ForeColor = foreColor; + } + + /// + /// + /// + public static TextRun EndOfLine + { + get + { + TextRun eol = new TextRun(); + eol.IsEndOfLine = true; + return eol; + } + } + + private int EmergencyBreak(int width) + { + int index = Text.Length; + int w, h; + do + { + Font.ComputeExtent(Text.Substring(0, --index), out w, out h); + } + + while (w >= width && index > 1); + + return index; + } + + internal bool Break(int availableWidth, out TextRun run1, out TextRun run2, bool emergencyBreak) + { + //Debug.Assert(availableWidth > 0); + //Debug.Assert(availableWidth < _width); + //Debug.Assert(Text.Length > 1); + + int leftBreak = -1; + int rightBreak = -1; + int w, h; + + // Try to find a candidate position for breaking + // + bool foundBreak = false; + while (!foundBreak) + { + // Try adding a word + // + int indexOfNextSpace = Text.IndexOf(' ', leftBreak + 1); + + foundBreak = (indexOfNextSpace == -1); + + if (!foundBreak) + { + Font.ComputeExtent(Text.Substring(0, indexOfNextSpace), out w, out h); + foundBreak = (w >= availableWidth); + if (w == availableWidth) + { + leftBreak = indexOfNextSpace; + } + } + + if (foundBreak) + { + if (leftBreak >= 0) + { + rightBreak = leftBreak + 1; + } + else if (emergencyBreak) + { + leftBreak = EmergencyBreak(availableWidth); + rightBreak = leftBreak; + } + else + { + run1 = run2 = null; + return false; + } + } + else + { + leftBreak = indexOfNextSpace; + } + } + + string first = Text.Substring(0, leftBreak).TrimEnd(' '); + + // Split the text run + // + run1 = null; + if (first.Length > 0) + { + run1 = new TextRun(first, this.Font, this.ForeColor); + } + + run2 = null; + if (rightBreak < Text.Length) + { + String run2String = Text.Substring(rightBreak).TrimStart(' '); + + // if run2 is all spaces (length == 0 after trim), we'll leave run2 as null + if (run2String.Length > 0) + { + run2 = new TextRun(run2String, this.Font, this.ForeColor); + } + } + + return true; + } + + /// + /// + /// + /// + /// + public void GetSize(out int width, out int height) + { + if (_width == 0) + { + Font.ComputeExtent(Text, out _width, out _height); + } + + width = _width; + height = _height; + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRunCollection.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRunCollection.cs new file mode 100644 index 0000000..7fab309 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/TextRunCollection.cs @@ -0,0 +1,205 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using System; +using System.Collections; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + + /// + /// + /// +public class TextRunCollection : ICollection +{ + private TextFlow _textFlow; + private ArrayList _textRuns; + + internal TextRunCollection(TextFlow textFlow) + { + this._textFlow = textFlow; + _textRuns = new ArrayList(); + } + + /// + /// + /// + public int Count + { + get + { + return _textRuns.Count; + } + } + + /// + /// + /// + /// + /// + /// + /// + public int Add(string text, Font font, Color foreColor) + { + return Add(new TextRun(text, font, foreColor)); + } + + /// + /// + /// + /// + /// + public int Add(TextRun textRun) + { + if (textRun == null) + { + throw new ArgumentNullException("textRun"); + } + + int result = _textRuns.Add(textRun); + _textFlow.InvalidateMeasure(); + return result; + } + + /// + /// + /// + public void Clear() + { + _textRuns.Clear(); + _textFlow.InvalidateMeasure(); + } + + /// + /// + /// + /// + /// + public bool Contains(TextRun run) + { + return _textRuns.Contains(run); + } + + /// + /// + /// + /// + /// + public int IndexOf(TextRun run) + { + return _textRuns.IndexOf(run); + } + + /// + /// + /// + /// + /// + public void Insert(int index, TextRun run) + { + _textRuns.Insert(index, run); + _textFlow.InvalidateMeasure(); + } + + /// + /// + /// + /// + public void Remove(TextRun run) + { + _textRuns.Remove(run); + _textFlow.InvalidateMeasure(); + } + + /// + /// + /// + /// + public void RemoveAt(int index) + { + if (index < 0 || index >= _textRuns.Count) + { + throw new ArgumentOutOfRangeException("index"); + } + + _textRuns.RemoveAt(index); + + _textFlow.InvalidateMeasure(); + } + + /// + /// + /// + /// + /// + public TextRun this[int index] + { + get + { + return (TextRun)_textRuns[index]; + } + + set + { + _textRuns[index] = value; + _textFlow.InvalidateMeasure(); + } + } + + #region ICollection Members + + /// + /// + /// + public bool IsSynchronized + { + get + { + return false; + } + } + + /// + /// + /// + /// + /// + public void CopyTo(Array array, int index) + { + _textRuns.CopyTo(array, index); + } + + /// + /// + /// + public object SyncRoot + { + get + { + return null; + } + } + + #endregion + + #region IEnumerable Members + + /// + /// + /// + /// + public IEnumerator GetEnumerator() + { + return _textRuns.GetEnumerator(); + } + + #endregion +} +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/WrapPanel.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/WrapPanel.cs new file mode 100644 index 0000000..8bfd77a --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Controls/WrapPanel.cs @@ -0,0 +1,265 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Controls +{ + /// + /// WrapPanel is used to place child UIElements at sequential positions from left to the right + /// and then "wrap" the lines of children from top to the bottom. + /// + /// All children get the layout partition of size ItemWidth x ItemHeight. + /// + public class WrapPanel : Panel + { + private struct UVSize + { + internal int U; + internal int V; + private Orientation _orientation; + + internal UVSize(Orientation orientation, int width, int height) + { + U = V = 0; + _orientation = orientation; + Width = width; + Height = height; + } + + internal UVSize(Orientation orientation) + { + U = V = 0; + _orientation = orientation; + } + + internal int Width + { + get { return (_orientation == Orientation.Horizontal ? U : V); } + set { if (_orientation == Orientation.Horizontal) U = value; else V = value; } + } + internal int Height + { + get { return (_orientation == Orientation.Horizontal ? V : U); } + set { if (_orientation == Orientation.Horizontal) V = value; else U = value; } + } + } + + /// + /// + /// + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + UVSize arrangeSize = new UVSize(_orientation, arrangeWidth, arrangeHeight); + UVSize currentLineSize = new UVSize(_orientation); + int accumulatedV = 0; + + bool itemWidthSet = _itemWidth != 0; + bool itemHeightSet = _itemHeight != 0; + bool useSetU = _orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet; + int itemSetU = _orientation == Orientation.Horizontal ? _itemWidth : _itemHeight; + + int firstInLineIndex = 0; + int count = Children.Count; + for (int i = 0; i < count; i++) + { + UIElement child = Children[i]; + if (child == null) continue; + + int desiredWidth, desiredHeight; + child.GetDesiredSize(out desiredWidth, out desiredHeight); + + UVSize childSize = new UVSize(_orientation, + itemWidthSet ? _itemWidth : desiredWidth, + itemHeightSet ? _itemHeight : desiredHeight); + + // does not fit on line + if (currentLineSize.U + childSize.U > arrangeSize.U) + { + // arrange previous line + ArrangeLine(accumulatedV, currentLineSize.V, firstInLineIndex, i /* exclusive */, useSetU, itemSetU); + accumulatedV += currentLineSize.V; + + // this child is on new line + currentLineSize = childSize; + + // child is bigger than available size + if (childSize.U > arrangeSize.U) + { + ArrangeLine(accumulatedV, childSize.V, i, i + 1, useSetU, itemSetU); + i++; // order of parameters evaluation is not guaranted + + // this is the only child on line + accumulatedV += childSize.V; + currentLineSize = new UVSize(_orientation); + } + + firstInLineIndex = i; + } + else + { + currentLineSize.U += childSize.U; + currentLineSize.V = Mathematics.Max(childSize.V, currentLineSize.V); + } + } + + if (firstInLineIndex < count) + ArrangeLine(accumulatedV, currentLineSize.V, firstInLineIndex, count, useSetU, itemSetU); + } + private void ArrangeLine(int v, int lineV, int indexStart, int indexEnd, bool useSetU, int itemSetU) + { + int u = 0; + bool isHorizontal = _orientation == Orientation.Horizontal; + + for (int i = indexStart; i < indexEnd; i++) + { + UIElement child = Children[i]; + if (child == null) continue; + + UVSize childSize = new UVSize(_orientation); + if (isHorizontal) child.GetDesiredSize(out childSize.U, out childSize.V); + else child.GetDesiredSize(out childSize.V, out childSize.U); + + int layoutSlotU = useSetU ? itemSetU : childSize.U; + + if (isHorizontal) child.Arrange(u, v, layoutSlotU, lineV); + else child.Arrange(v, u, lineV, layoutSlotU); + + u += layoutSlotU; + } + } + + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + UVSize desiredSize = new UVSize(_orientation); + UVSize availableSize = new UVSize(_orientation, availableWidth, availableHeight); + UVSize currentLineSize = new UVSize(_orientation); + + bool itemWidthSet = _itemWidth != 0; + bool itemHeightSet = _itemHeight != 0; + + int availableChildWidth = itemWidthSet ? _itemWidth : availableWidth; + int availableChildHeight = itemHeightSet ? _itemHeight : availableHeight; + + int count = Children.Count; + for (int i = 0; i < count; i++) + { + UIElement child = Children[i]; + if (child == null) continue; + + child.Measure(availableChildWidth, availableChildHeight); + + int desiredChildWidth, desiredChildHeight; + child.GetDesiredSize(out desiredChildWidth, out desiredChildHeight); + + UVSize childSize = new UVSize(_orientation, + itemWidthSet ? _itemWidth : desiredChildWidth, + itemHeightSet ? _itemHeight : desiredChildHeight); + + if (currentLineSize.U + childSize.U > availableSize.U) + { + desiredSize.U = Mathematics.Max(currentLineSize.U, desiredSize.U); + desiredSize.V += currentLineSize.V; + + currentLineSize = childSize; + if (childSize.U > availableSize.U) + { + desiredSize.U = Mathematics.Max(childSize.U, desiredSize.U); + desiredSize.V = childSize.V; + currentLineSize = new UVSize(_orientation); + } + } + else + { + currentLineSize.U += childSize.U; + currentLineSize.V = Mathematics.Max(childSize.V, currentLineSize.V); + } + } + + desiredWidth = Mathematics.Max(currentLineSize.U, desiredSize.U); + desiredHeight = desiredSize.V + currentLineSize.V; + } + + private int _itemWidth = 0; + /// + /// The ItemWidth and ItemHeight properties specify the size of all items in the WrapPanel. + /// Note that children of WrapPanel may have their own Width/Height properties set - the ItemWidth/ItemHeight + /// specifies the size of "layout partition" reserved by WrapPanel for the child. + /// If this property is not set (equal to 0) - the size of layout + /// partition is equal to DesiredSize of the child element. + /// + public int ItemWidth + { + get { return _itemWidth; } + set + { + if (_itemWidth != value) + { + if (!IsWidthHeightValid(value)) + throw new ArgumentOutOfRangeException("value"); + + _itemWidth = value; + InvalidateMeasure(); + } + } + } + + private int _itemHeight = 0; + /// + /// The ItemWidth and ItemHeight properties specify the size of all items in the WrapPanel. + /// Note that children of WrapPanel may have their own Width/Height properties set - the ItemWidth/ItemHeight + /// specifies the size of "layout partition" reserved by WrapPanel for the child. + /// If this property is not set (equal to 0) - the size of layout + /// partition is equal to DesiredSize of the child element. + /// + public int ItemHeight + { + get { return _itemHeight; } + set + { + if (_itemHeight != value) + { + if (!IsWidthHeightValid(value)) + throw new ArgumentOutOfRangeException("value"); + + _itemHeight = value; + InvalidateMeasure(); + } + } + } + + private static bool IsWidthHeightValid(object value) + { + int v = (int)value; + return v >= 0; + } + + private Orientation _orientation = Orientation.Horizontal; + /// + /// Specifies dimension of children positioning in absence of wrapping. + /// Wrapping occurs in orthogonal direction. For example, if Orientation is Horizontal, + /// the items try to form horizontal rows first and if needed are wrapped and form vertical stack of rows. + /// If Orientation is Vertical, items first positioned in a vertical column, and if there is + /// not enough space - wrapping creates additional columns in horizontal dimension. + /// + public Orientation Orientation + { + get { return _orientation; } + set + { + if (value != _orientation) + { + _orientation = value; + InvalidateMeasure(); + } + } + } + } +} \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/HorizontalAlignment.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/HorizontalAlignment.cs new file mode 100644 index 0000000..7add36d --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/HorizontalAlignment.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation +{ + /// + /// + /// + public enum HorizontalAlignment + { + /// + /// + /// + Left, + + /// + /// + /// + Center, + + /// + /// + /// + Right, + + /// + /// + /// + Stretch + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/LayoutManager.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/LayoutManager.cs new file mode 100644 index 0000000..b50863c --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/LayoutManager.cs @@ -0,0 +1,452 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using nanoFramework.UI; +using System; +using System.Collections; +using nanoFramework.UI.Threading; + +namespace nanoFramework.Presentation +{ + // LayoutManager is responsible for all layout operations. It maintains the Arrange, + // Invalidate and Measure queues. + // + internal class LayoutManager : DispatcherObject + { + TimeSpan ts = new TimeSpan(new DateTime().Ticks); + public class LayoutQueue + { + public LayoutQueue(LayoutManager layoutManager) + { + _layoutManager = layoutManager; + _elements = new ArrayList(); + } + + public bool IsEmpty + { + get + { + return _elements.Count == 0; + } + } + + public void Add(UIElement e) + { + if (!_elements.Contains(e)) + { + RemoveOrphans(e); + _elements.Add(e); + } + + _layoutManager.NeedsRecalc(); + } + + public UIElement GetTopMost() + { + UIElement found = null; + int treeLevel = int.MaxValue; + + int count = _elements.Count; + for (int index = 0; index < count; index++) + { + UIElement currentElement = (UIElement)_elements[index]; + UIElement parent = currentElement._parent; + + int cnt = 0; + while (parent != null && cnt < treeLevel) + { + cnt++; + parent = parent._parent; + } + + if (cnt < treeLevel) + { + treeLevel = cnt; + found = currentElement; + } + } + + return found; + } + + public void Remove(UIElement e) + { + _elements.Remove(e); + } + + public void RemoveOrphans(UIElement parent) + { + int count = _elements.Count; + for (int index = count - 1; index >= 0; index--) + { + UIElement child = (UIElement)_elements[index]; + if (child._parent == parent) + { + _elements.RemoveAt(index); + } + } + } + + private LayoutManager _layoutManager; + + private ArrayList _elements; + } + + private class SingletonLock + { + } + + private LayoutManager() + { + // This constructor exists to prevent instantiation of a LayoutManager by any + // means other than through LayoutManager.CurrentLayoutManager. + _updateLayoutBackground = new DispatcherOperationCallback(UpdateLayoutBackground); + _updateCallback = new DispatcherOperationCallback(UpdateLayoutCallback); + } + + // posts a layout update + private void NeedsRecalc() + { + if (!_layoutRequestPosted && !_isUpdating) + { + _layoutRequestPosted = true; + MediaContext.From(Dispatcher).BeginInvokeOnRender(_updateCallback, this); + } + } + + private object UpdateLayoutBackground(object arg) + { + this.NeedsRecalc(); + return null; + } + + private object UpdateLayoutCallback(object arg) + { + this.UpdateLayout(); + return null; + } + + public LayoutQueue ArrangeQueue + { + get + { + if (_arrangeQueue == null) + { + lock (typeof(SingletonLock)) + { + if (_arrangeQueue == null) + { + _arrangeQueue = new LayoutQueue(this); + } + } + } + + return _arrangeQueue; + } + } + + // Returns the LayoutManager singleton + // + public static LayoutManager CurrentLayoutManager + { + get + { + return LayoutManager.From(Dispatcher.CurrentDispatcher); + } + } + + public static LayoutManager From(Dispatcher dispatcher) + { + if (dispatcher == null) throw new ArgumentException(); + + if (dispatcher._layoutManager == null) + { + lock (typeof(SingletonLock)) + { + if (dispatcher._layoutManager == null) + { + dispatcher._layoutManager = new LayoutManager(); + } + } + } + + return dispatcher._layoutManager; + } + + public LayoutQueue MeasureQueue + { + get + { + if (_measureQueue == null) + { + lock (typeof(SingletonLock)) + { + if (_measureQueue == null) + { + _measureQueue = new LayoutQueue(this); + } + } + } + + return _measureQueue; + } + } + + public void UpdateLayout() + { + VerifyAccess(); + + //make UpdateLayout to be a NOP if called during UpdateLayout. + if (_isUpdating) return; + + _isUpdating = true; + + WindowManager.Instance.Invalidate(); + + LayoutQueue measureQueue = MeasureQueue; + LayoutQueue arrangeQueue = ArrangeQueue; + + int cnt = 0; + bool gotException = true; + UIElement currentElement = null; + + //NOTE: + // + //There are a bunch of checks here that break out of and re-queue layout if + //it looks like things are taking too long or we have somehow gotten into an + //infinite loop. In the nanoCLR we will probably have better ways of + //dealing with a bad app through app domain separation, but keeping this + //robustness can't hurt. In a single app domain scenario, it could + //give the opportunity to get out to the system if something is misbehaving, + //we like this kind of reliability in embedded systems. + // + + try + { + invalidateTreeIfRecovering(); + + while ((!MeasureQueue.IsEmpty) || (!ArrangeQueue.IsEmpty)) + { + if (++cnt > 153) + { + //loop detected. Lets re-queue and let input/user to correct the situation. + // + Dispatcher.BeginInvoke(_updateLayoutBackground, this); + currentElement = null; + gotException = false; + return; + } + + //loop for Measure + //We limit the number of loops here by time - normally, all layout + //calculations should be done by this time, this limit is here for + //emergency, "infinite loop" scenarios - yielding in this case will + //provide user with ability to continue to interact with the app, even though + //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one + + int loopCounter = 0; + TimeSpan loopStartTime = TimeSpan.Zero; + + while (true) + { + if (++loopCounter > 153) + { + loopCounter = 0; + if (LimitExecution(ref loopStartTime)) + { + currentElement = null; + gotException = false; + return; + } + } + + currentElement = measureQueue.GetTopMost(); + + if (currentElement == null) break; //exit if no more Measure candidates + + currentElement.Measure( + currentElement._previousAvailableWidth, + currentElement._previousAvailableHeight + ); + + measureQueue.RemoveOrphans(currentElement); + } + + //loop for Arrange + //if Arrange dirtied the tree go clean it again + + //We limit the number of loops here by time - normally, all layout + //calculations should be done by this time, this limit is here for + //emergency, "infinite loop" scenarios - yielding in this case will + //provide user with ability to continue to interact with the app, even though + //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one + loopCounter = 0; + loopStartTime = TimeSpan.Zero; + + while (true) + { + if (++loopCounter > 153) + { + loopCounter = 0; + if (LimitExecution(ref loopStartTime)) + { + currentElement = null; + gotException = false; + return; + } + } + + currentElement = arrangeQueue.GetTopMost(); + + if (currentElement == null) break; //exit if no more Arrange candidates + + int arrangeX, arrangeY, arrangeWidth, arrangeHeight; + + getProperArrangeRect(currentElement, out arrangeX, out arrangeY, out arrangeWidth, out arrangeHeight); + + currentElement.Arrange(arrangeX, arrangeY, arrangeWidth, arrangeHeight); + arrangeQueue.RemoveOrphans(currentElement); + } + + /* REFACTOR -- do we need Layout events and Size changed events? + + //let LayoutUpdated handlers to call UpdateLayout + //note that it means we can get reentrancy into UpdateLayout past this point, + //if any of event handlers call UpdateLayout sync. Need to protect from reentrancy + //in the firing methods below. + + fireSizeChangedEvents(); + if ((!MeasureQueue.IsEmpty) || (!ArrangeQueue.IsEmpty)) continue; + fireLayoutUpdateEvent(); + */ + } + + currentElement = null; + gotException = false; + } + finally + { + _isUpdating = false; + _layoutRequestPosted = false; + + if (gotException) + { + //set indicator + _gotException = true; + _forceLayoutElement = currentElement; + + //make attempt to request the subsequent layout calc + Dispatcher.BeginInvoke(_updateLayoutBackground, this); + } + } + } + + // + // ensures we don't spend all day doing layout, and + // give the system the chance to do something else. + private bool LimitExecution(ref TimeSpan loopStartTime) + { + TimeSpan tsNow = new TimeSpan(DateTime.UtcNow.Ticks); + if (loopStartTime.Ticks == 0) + { + // from Netmf -- loopStartTime = Microsoft.SPOT.Hardware.Utility.GetMachineTime(); + loopStartTime = tsNow; + } + else + { + // from Netmf -- if ((Microsoft.SPOT.Hardware.Utility.GetMachineTime() - loopStartTime).Ticks > 153 * 2 * TimeSpan.TicksPerMillisecond) // 153*2 = magic*science + if ((tsNow - loopStartTime).Ticks > 153 * 2 * TimeSpan.TicksPerMillisecond) // 153*2 = magic*science + { + //loop detected. Lets go over to background to let input work. + Dispatcher.BeginInvoke(_updateLayoutBackground, this); + return true; + } + } + + return false; + } + + private void getProperArrangeRect(UIElement element, out int x, out int y, out int width, out int height) + { + x = element._finalX; + y = element._finalY; + width = element._finalWidth; + height = element._finalHeight; + + // ELements without a parent (top level) get Arrange at DesiredSize + // if they were measured "to content" (as Constants.MaxExtent indicates). + // If we arrange the element that is temporarily disconnected + // so it is not a top-level one, the assumption is that it will be + // layout-invalidated and/or recomputed by the parent when reconnected. + if (element.Parent == null) + { + int desiredWidth, desiredHeight; + x = y = 0; + element.GetDesiredSize(out desiredWidth, out desiredHeight); + + if (element._previousAvailableWidth == Media.Constants.MaxExtent) + width = desiredWidth; + + if (element._previousAvailableHeight == Media.Constants.MaxExtent) + height = desiredHeight; + } + } + + private void invalidateTreeIfRecovering() + { + if ((_forceLayoutElement != null) || _gotException) + { + if (_forceLayoutElement != null) + { + UIElement e = _forceLayoutElement.RootUIElement; + + markTreeDirtyHelper(e); + MeasureQueue.Add(e); + } + + _forceLayoutElement = null; + _gotException = false; + } + } + + private void markTreeDirtyHelper(UIElement e) + { + //now walk down and mark all UIElements dirty + if (e != null) + { + e._flags |= (UIElement.Flags.InvalidMeasure | UIElement.Flags.InvalidArrange); + + UIElementCollection uiec = e._logicalChildren; + + if (uiec != null) + { + for (int i = uiec.Count; i-- > 0; ) + { + markTreeDirtyHelper(uiec[i]); + } + } + } + } + + private bool _isUpdating; + private bool _gotException; //true if UpdateLayout exited with exception + private bool _layoutRequestPosted; + + private UIElement _forceLayoutElement; //set in extreme situations, forces the update of the whole tree containing the element + + // measure & arrange queues. + private LayoutQueue _arrangeQueue; + private LayoutQueue _measureQueue; + + private DispatcherOperationCallback _updateLayoutBackground; + private DispatcherOperationCallback _updateCallback; + + } + +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Brush.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Brush.cs new file mode 100644 index 0000000..0348b7a --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Brush.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public abstract class Brush + { + private ushort _opacity = Bitmap.OpacityOpaque; + + /// + /// + /// + public ushort Opacity + { + get + { + return _opacity; + } + set + { + // clip values + if (value > Bitmap.OpacityOpaque) value = Bitmap.OpacityOpaque; + + _opacity = value; + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected internal abstract void RenderRectangle(Bitmap bmp, Pen outline, int x, int y, int width, int height); + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected internal virtual void RenderEllipse(Bitmap bmp, Pen outline, int x, int y, int xRadius, int yRadius) + { + throw new NotSupportedException("RenderEllipse is not supported with this brush."); + } + + /// + /// + /// + /// + /// + /// + protected internal virtual void RenderPolygon(Bitmap bmp, Pen outline, int[] pts) + { + throw new NotSupportedException("RenderPolygon is not supported with this brush."); + } + } + + /// + /// + /// + public enum BrushMappingMode + { + /// + /// + /// + Absolute, + + /// + /// + /// + RelativeToBoundingBox + } +} diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Color.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Color.cs new file mode 100644 index 0000000..1dfa119 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Color.cs @@ -0,0 +1,628 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public static class ColorUtility + { + /// + /// + /// + /// + /// + /// + /// + public static Color ColorFromRGB(byte r, byte g, byte b) + { + return (Color)((b << 16) | (g << 8) | r); + } + + /// + /// + /// + /// + /// + public static byte GetRValue(Color color) + { + return (byte)((uint)color & 0xff); + } + + /// + /// + /// + /// + /// + public static byte GetGValue(Color color) + { + return (byte)(((uint)color >> 8) & 0xff); + } + + /// + /// + /// + /// + /// + public static byte GetBValue(Color color) + { + return (byte)(((uint)color >> 16) & 0xff); + } + } + + /// + /// + /// + public enum Color : uint + { + /// + /// + /// + AliceBlue = 0xF0F8FF, + /// + /// + /// + AntiqueWhite = 0xFAEBD7, + /// + /// + /// + Aqua = 0x00FFFF, + /// + /// + /// + Aquamarine = 0x7FFFD4, + /// + /// + /// + Azure = 0xF0FFFF, + /// + /// + /// + Beige = 0xF5F5DC, + /// + /// + /// + Bisque = 0xFFE4C4, + /// + /// + /// + Black = 0x000000, + /// + /// + /// + BlanchedAlmond = 0xFFEBCD, + /// + /// + /// + Blue = 0x0000FF, + /// + /// + /// + BlueViolet = 0x8A2BE2, + /// + /// + /// + Brown = 0xA52A2A, + /// + /// + /// + BurlyWood = 0xDEB887, + /// + /// + /// + CadetBlue = 0x5F9EA0, + /// + /// + /// + Chartreuse = 0x7FFF00, + /// + /// + /// + Chocolate = 0xD2691E, + /// + /// + /// + Coral = 0xFF7F50, + /// + /// + /// + CornflowerBlue = 0x6495ED, + /// + /// + /// + Cornsilk =0xFFF8DC , + /// + /// + /// + Crimson = 0xDC143C, + /// + /// + /// + Cyan = 0x00FFFF, + /// + /// + /// + DarkBlue = 0x00008B, + /// + /// + /// + DarkCyan = 0x008B8B, + /// + /// + /// + DarkGoldenrod = 0xB8860B, + /// + /// + /// + DarkGray = 0xA9A9A9, + /// + /// + /// + DarkGreen = 0x006400, + /// + /// + /// + DarkKhaki = 0xBDB76B, + /// + /// + /// + DarkMagenta = 0x8B008B, + /// + /// + /// + DarkOliveGreen = 0x556B2F, + /// + /// + /// + DarkOrange = 0xFF8C00, + /// + /// + /// + DarkOrchid = 0x9932CC, + /// + /// + /// + DarkRed = 0x8B0000, + /// + /// + /// + DarkSalmon = 0xE9967A, + /// + /// + /// + DarkSeaGreen = 0x8FBC8F, + /// + /// + /// + DarkSlateBlue = 0x483D8B, + /// + /// + /// + DarkSlateGray = 0x2F4F4F, + /// + /// + /// + DarkTurquoise = 0x00CED1, + /// + /// + /// + DarkViolet = 0x9400D3, + /// + /// + /// + DeepPink = 0xFF1493, + /// + /// + /// + DeepSkyBlue = 0x00BFFF, + /// + /// + /// + DimGray = 0x696969, + /// + /// + /// + DodgerBlue = 0x1E90FF, + /// + /// + /// + Firebrick = 0xB22222, + /// + /// + /// + FloralWhite = 0xFFFAF0, + /// + /// + /// + ForestGreen = 0x228B22, + /// + /// + /// + Fuchsia = 0xFF00FF, + /// + /// + /// + Gainsboro = 0xDCDCDC, + /// + /// + /// + GhostWhite = 0xF8F8FF, + /// + /// + /// + Gold = 0xFFD700, + /// + /// + /// + Goldenrod = 0xDAA520, + /// + /// + /// + Gray = 0x808080, + /// + /// + /// + Green = 0x008000, + /// + /// + /// + GreenYellow = 0xADFF2F, + /// + /// + /// + Honeydew = 0xF0FFF0, + /// + /// + /// + HotPink = 0xFF69B4, + /// + /// + /// + IndianRed = 0xCD5C5C, + /// + /// + /// + Indigo = 0x4B0082, + /// + /// + /// + Ivory = 0xFFFFF0, + /// + /// + /// + Khaki = 0xF0E68C, + /// + /// + /// + Lavender = 0xE6E6FA, + /// + /// + /// + LavenderBlush = 0xFFF0F5, + /// + /// + /// + LawnGreen = 0x7CFC00, + /// + /// + /// + LemonChiffon = 0xFFFACD, + /// + /// + /// + LightBlue = 0xADD8E6, + /// + /// + /// + LightCoral = 0xF08080, + /// + /// + /// + LightCyan = 0xE0FFFF, + /// + /// + /// + LightGoldenrodYellow = 0xFAFAD2, + /// + /// + /// + LightGray = 0xD3D3D3, + /// + /// + /// + LightGreen = 0x90EE90, + /// + /// + /// + LightPink = 0xFFB6C1, + /// + /// + /// + LightSalmon = 0xFFA07A, + /// + /// + /// + LightSeaGreen = 0x20B2AA, + /// + /// + /// + LightSkyBlue = 0x87CEFA, + /// + /// + /// + LightSlateGray = 0x778899, + /// + /// + /// + LightSteelBlue = 0xB0C4DE, + /// + /// + /// + LightYellow = 0xFFFFE0, + /// + /// + /// + ime = 0x00FF00, + /// + /// + /// + LimeGreen = 0x32CD32, + /// + /// + /// + Linen = 0xFAF0E6, + /// + /// + /// + Magenta = 0xFF00FF, + /// + /// + /// + Maroon = 0x800000, + /// + /// + /// + MediumAquamarine = 0x66CDAA, + /// + /// + /// + MediumBlue = 0x0000CD, + /// + /// + /// + MediumOrchid = 0xBA55D3, + /// + /// + /// + MediumPurple = 0x9370DB, + /// + /// + /// + MediumSeaGreen = 0x3CB371, + /// + /// + /// + MediumSlateBlue = 0x7B68EE, + /// + /// + /// + MediumSpringGreen = 0x00FA9A, + /// + /// + /// + MediumTurquoise = 0x48D1CC, + /// + /// + /// + MediumVioletRed = 0xC71585, + /// + /// + /// + MidnightBlue = 0x191970, + /// + /// + /// + MintCream = 0xF5FFFA, + /// + /// + /// + MistyRose = 0xFFE4E1, + /// + /// + /// + Moccasin = 0xFFE4B5, + /// + /// + /// + NavajoWhite = 0xFFDEAD, + /// + /// + /// + Navy = 0x000080, + /// + /// + /// + OldLace = 0xFDF5E6, + /// + /// + /// + Olive = 0x808000, + /// + /// + /// + OliveDrab = 0x6B8E23, + /// + /// + /// + Orange = 0xFFA500, + /// + /// + /// + OrangeRed = 0xFF4500, + /// + /// + /// + Orchid = 0xDA70D6, + /// + /// + /// + PaleGoldenrod = 0xEEE8AA, + /// + /// + /// + PaleGreen = 0x98FB98, + /// + /// + /// + PaleTurquoise = 0xAFEEEE, + /// + /// + /// + PaleVioletRed = 0xDB7093, + /// + /// + /// + PapayaWhip = 0xFFEFD5, + /// + /// + /// + PeachPuff = 0xFFDAB9, + /// + /// + /// + Peru = 0xCD853F, + /// + /// + /// + Pink = 0xFFC0CB, + /// + /// + /// + Plum = 0xDDA0DD, + /// + /// + /// + PowderBlue = 0xB0E0E6, + /// + /// + /// + Purple = 0x800080, + /// + /// + /// + Red = 0xFF0000, + /// + /// + /// + RosyBrown = 0xBC8F8F, + /// + /// + /// + RoyalBlue = 0x4169E1, + /// + /// + /// + SaddleBrown = 0x8B4513, + /// + /// + /// + Salmon = 0xFA8072, + /// + /// + /// + SandyBrown = 0xF4A460, + /// + /// + /// + SeaGreen = 0x2E8B57, + /// + /// + /// + SeaShell = 0xFFF5EE, + /// + /// + /// + Sienna = 0xA0522D, + /// + /// + /// + Silver = 0xC0C0C0, + /// + /// + /// + SkyBlue = 0x87CEEB, + /// + /// + /// + SlateBlue = 0x6A5ACD, + /// + /// + /// + SlateGray = 0x708090, + /// + /// + /// + Snow = 0xFFFAFA, + /// + /// + /// + SpringGreen = 0x00FF7F, + /// + /// + /// + SteelBlue = 0x4682B4, + /// + /// + /// + Tan = 0xD2B48C, + /// + /// + /// + Teal = 0x008080, + /// + /// + /// + Thistle = 0xD8BFD8, + /// + /// + /// + Tomato = 0xFF6347, + /// + /// + /// + Transparent = 0x00FFFFFF, + /// + /// + /// + Turquoise = 0x40E0D0, + /// + /// + /// + Violet = 0xEE82EE, + /// + /// + /// + Wheat = 0xF5DEB3, + /// + /// + /// + White = 0xFFFFFF, + /// + /// + /// + WhiteSmoke = 0xF5F5F5, + /// + /// + /// + Yellow = 0xFFFF00, + /// + /// + /// + YellowGreen = 0x9ACD32 + } +} + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Constants.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Constants.cs new file mode 100644 index 0000000..6b9a3d2 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Constants.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public static class Constants + { + /// + /// + /// + public const int MaxExtent = 0x7ffff; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/DrawingContext.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/DrawingContext.cs new file mode 100644 index 0000000..107a704 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/DrawingContext.cs @@ -0,0 +1,558 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; +using System; +using System.Collections; +using nanoFramework.UI.Threading; + +namespace nanoFramework.Presentation.Media +{ + /// + /// Drawing Context. + /// + public class DrawingContext : DispatcherObject,IDisposable + { + /// + /// + /// + /// + public DrawingContext(Bitmap bmp) + { + this._bitmap = bmp; + } + + /// + /// + /// + /// + /// + public DrawingContext(int width, int height) + { + this._bitmap = new Bitmap(width, height); + } + + /// + /// + /// + /// + /// + public void Translate(int dx, int dy) + { + VerifyAccess(); + + _x += dx; + _y += dy; + } + + /// + /// + /// + /// + /// + public void GetTranslation(out int x, out int y) + { + VerifyAccess(); + + x = _x; + y = _y; + } + + /// + /// + /// + public Bitmap Bitmap + { + get + { + VerifyAccess(); + + return _bitmap; + } + } + + /// + /// + /// + public void Clear() + { + VerifyAccess(); + + _bitmap.Clear(); + } + + internal void Close() + { + _bitmap = null; + } + + /// + /// + /// + /// + /// + /// + public void DrawPolygon(Brush brush, Pen pen, int[] pts) + { + VerifyAccess(); + + brush.RenderPolygon(_bitmap, pen, pts); + + int nPts = pts.Length / 2; + + for (int i = 0; i < nPts - 1; i++) + { + DrawLine(pen, pts[i * 2], pts[i * 2 + 1], pts[i * 2 + 2], pts[i * 2 + 3]); + } + + if (nPts > 2) + { + DrawLine(pen, pts[nPts * 2 - 2], pts[nPts * 2 - 1], pts[0], pts[1]); + } + } + + /// + /// + /// + /// + /// + /// + public void SetPixel(Color color, int x, int y) + { + VerifyAccess(); + + _bitmap.SetPixel(_x + x, _y + y, color); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void DrawLine(Pen pen, int x0, int y0, int x1, int y1) + { + VerifyAccess(); + + if (pen != null) + { + _bitmap.DrawLine(pen.Color, pen.Thickness, _x + x0, _y + y0, _x + x1, _y + y1); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void DrawEllipse(Brush brush, Pen pen, int x, int y, int xRadius, int yRadius) + { + VerifyAccess(); + + // Fill + // + if (brush != null) + { + brush.RenderEllipse(_bitmap, pen, _x + x, _y + y, xRadius, yRadius); + } + + // Pen + else if (pen != null && pen.Thickness > 0) + { + _bitmap.DrawEllipse(pen.Color, pen.Thickness, _x + x, _y + y, xRadius, yRadius, + (Color)0x0, 0, 0, (Color)0x0, 0, 0, 0); + } + + } + + /// + /// + /// + /// + /// + /// + public void DrawImage(Bitmap source, int x, int y) + { + VerifyAccess(); + + _bitmap.DrawImage(_x + x, _y + y, source, 0, 0, source.Width, source.Height); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void DrawImage(Bitmap source, int destinationX, int destinationY, int sourceX, int sourceY, int sourceWidth, int sourceHeight) + { + VerifyAccess(); + + _bitmap.DrawImage(_x + destinationX, _y + destinationY, source, sourceX, sourceY, sourceWidth, sourceHeight); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void BlendImage(Bitmap source, int destinationX, int destinationY, int sourceX, int sourceY, int sourceWidth, int sourceHeight, ushort opacity) + { + VerifyAccess(); + + _bitmap.DrawImage( _x + destinationX, _y + destinationY, source, sourceX, sourceY, sourceWidth, sourceHeight, opacity ); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void RotateImage( int angle, int destinationX, int destinationY, Bitmap bitmap, int sourceX, int sourceY, int sourceWidth, int sourceHeight, ushort opacity ) + { + VerifyAccess(); + + _bitmap.RotateImage( angle, _x + destinationX, _y + destinationY, bitmap, sourceX, sourceY, sourceWidth, sourceHeight, opacity ); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void StretchImage(int xDst, int yDst, int widthDst, int heightDst, Bitmap bitmap, int xSrc, int ySrc, int widthSrc, int heightSrc, ushort opacity) + { + VerifyAccess(); + + _bitmap.StretchImage( _x + xDst, _y + yDst, widthDst, heightDst, bitmap, xSrc, ySrc, widthSrc, heightSrc, opacity ); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void TileImage(int xDst, int yDst, Bitmap bitmap, int width, int height, ushort opacity) + { + VerifyAccess(); + + _bitmap.TileImage( _x + xDst, _y + yDst, bitmap, width, height, opacity ); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void Scale9Image(int xDst, int yDst, int widthDst, int heightDst, Bitmap bitmap, int leftBorder, int topBorder, int rightBorder, int bottomBorder, ushort opacity) + { + VerifyAccess(); + + _bitmap.Scale9Image( _x + xDst, _y + yDst, widthDst, heightDst, bitmap, leftBorder, topBorder, rightBorder, bottomBorder, opacity ); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void DrawText(string text, Font font, Color color, int x, int y) + { + VerifyAccess(); + + _bitmap.DrawText(text, font, color, _x + x, _y + y); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool DrawText(ref string text, Font font, Color color, int x, int y, int width, int height, + TextAlignment alignment, TextTrimming trimming) + { + VerifyAccess(); + + uint flags = Bitmap.DT_WordWrap; + + // Text alignment + switch (alignment) + { + case TextAlignment.Left: + //flags |= Bitmap.DT_AlignmentLeft; + break; + case TextAlignment.Center: + flags |= Bitmap.DT_AlignmentCenter; + break; + case TextAlignment.Right: + flags |= Bitmap.DT_AlignmentRight; + break; + default: + throw new NotSupportedException(); + } + + // Trimming + switch (trimming) + { + case TextTrimming.CharacterEllipsis: + flags |= Bitmap.DT_TrimmingCharacterEllipsis; + break; + case TextTrimming.WordEllipsis: + flags |= Bitmap.DT_TrimmingWordEllipsis; + break; + } + + int xRelStart = 0; + int yRelStart = 0; + return _bitmap.DrawTextInRect(ref text, ref xRelStart, ref yRelStart, _x + x, _y + y, + width, height, flags, color, font); + } + + /// + /// + /// + /// + /// + /// + /// + public void GetClippingRectangle(out int x, out int y, out int width, out int height) + { + if (_clippingRectangles.Count == 0) + { + x = 0; + y = 0; + width = _bitmap.Width - _x; + height = _bitmap.Height - _y; + } + else + { + ClipRectangle rect = (ClipRectangle)_clippingRectangles.Peek(); + x = rect.X - _x; + y = rect.Y - _y; + width = rect.Width; + height = rect.Height; + } + } + + /// + /// + /// + /// + /// + /// + /// + public void PushClippingRectangle(int x, int y, int width, int height) + { + VerifyAccess(); + + if(width < 0 || height < 0) + { + throw new ArgumentException(); + } + + ClipRectangle rect = new ClipRectangle( _x + x, _y + y, width, height ); + + if (_clippingRectangles.Count > 0) + { + // Intersect with the existing clip bounds + ClipRectangle previousRect = (ClipRectangle)_clippingRectangles.Peek(); + //need to evaluate performance differences of inlining Min & Max( + int x1 = Mathematics.Max(rect.X, previousRect.X); + int x2 = Mathematics.Min(rect.X + rect.Width, previousRect.X + previousRect.Width); + int y1 = Mathematics.Max(rect.Y, previousRect.Y); + int y2 = Mathematics.Min(rect.Y + rect.Height, previousRect.Y + previousRect.Height); + + rect.X = x1; + rect.Y = y1; + rect.Width = x2 - x1; + rect.Height = y2 - y1; + } + + _clippingRectangles.Push(rect); + + _bitmap.SetClippingRectangle(rect.X, rect.Y, rect.Width, rect.Height); + EmptyClipRect = (rect.Width <= 0 || rect.Height <= 0); + } + + /// + /// + /// + public void PopClippingRectangle() + { + VerifyAccess(); + + int n = _clippingRectangles.Count; + + if (n > 0) + { + _clippingRectangles.Pop(); + + ClipRectangle rect; + + if (n == 1) // in this case, at this point the stack is empty + { + rect = new ClipRectangle(0, 0, _bitmap.Width, _bitmap.Height); + } + else + { + rect = (ClipRectangle)_clippingRectangles.Peek(); + } + + _bitmap.SetClippingRectangle(rect.X, rect.Y, rect.Width, rect.Height); + + EmptyClipRect = (rect.Width == 0 && rect.Height == 0); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void DrawRectangle(Brush brush, Pen pen, int x, int y, int width, int height) + { + VerifyAccess(); + + // Fill + // + if (brush != null) + { + brush.RenderRectangle(_bitmap, pen, _x + x, _y + y, width, height); + } + + // Pen + else if (pen != null && pen.Thickness > 0) + { + _bitmap.DrawRectangle(pen.Color, pen.Thickness, _x + x, _y + y, width, height, 0, 0, + (Color)0, 0, 0, (Color)0, 0, 0, 0); + } + } + + /// + /// + /// + public int Width + { + get + { + return _bitmap.Width; + } + } + + /// + /// + /// + public int Height + { + get + { + return _bitmap.Height; + } + } + + private class ClipRectangle + { + public ClipRectangle(int x, int y, int width, int height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + + public int X; + public int Y; + public int Width; + public int Height; + } + + internal bool EmptyClipRect = false; + + private Bitmap _bitmap; + internal int _x; + internal int _y; + private Stack _clippingRectangles = new Stack(); + + /// + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + _bitmap = null; + } + + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/ImageBrush.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/ImageBrush.cs new file mode 100644 index 0000000..2386c46 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/ImageBrush.cs @@ -0,0 +1,68 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public sealed class ImageBrush : Brush + { + /// + /// + /// + public Bitmap BitmapSource; + + /// + /// + /// + public Stretch Stretch = Stretch.Fill; + + /// + /// + /// + /// + public ImageBrush(Bitmap bmp) + { + BitmapSource = bmp; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected internal override void RenderRectangle(Bitmap bmp, Pen pen, int x, int y, int width, int height) + { + if (Stretch == Stretch.None) + { + bmp.DrawImage(x, y, BitmapSource, 0, 0, BitmapSource.Width, BitmapSource.Height, Opacity); + } + else if (width == BitmapSource.Width && height == BitmapSource.Height) + { + bmp.DrawImage(x, y, BitmapSource, 0, 0, width, height, Opacity); + } + else + { + bmp.StretchImage(x, y, BitmapSource, width, height, Opacity); + } + + if (pen != null && pen.Thickness > 0) + { + bmp.DrawRectangle(pen.Color, pen.Thickness, x, y, width, height, 0, 0, + (Color)0, 0, 0, (Color)0, 0, 0, 0); + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/LinearGradientBrush.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/LinearGradientBrush.cs new file mode 100644 index 0000000..6ec8722 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/LinearGradientBrush.cs @@ -0,0 +1,112 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public sealed class LinearGradientBrush : Brush + { + /// + /// + /// + public Color StartColor; + /// + /// + /// + public Color EndColor; + + /// + /// + /// + public BrushMappingMode MappingMode = BrushMappingMode.RelativeToBoundingBox; + + /// + /// + /// + public int StartX, StartY; + + /// + /// + /// + public int EndX, EndY; + + /// + /// + /// + public const int RelativeBoundingBoxSize = 1000; + + /// + /// + /// + /// + /// + public LinearGradientBrush(Color startColor, Color endColor) + : this(startColor, endColor, 0, 0, RelativeBoundingBoxSize, RelativeBoundingBoxSize) + { } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public LinearGradientBrush(Color startColor, Color endColor, int startX, int startY, int endX, int endY) + { + StartColor = startColor; + EndColor = endColor; + StartX = startX; + StartY = startY; + EndX = endX; + EndY = endY; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected internal override void RenderRectangle(Bitmap bmp, Pen pen, int x, int y, int width, int height) + { + Color outlineColor = (pen != null) ? pen.Color : (Color)0x0; + ushort outlineThickness = (pen != null) ? pen.Thickness : (ushort)0; + + int x1, y1; + int x2, y2; + + switch (MappingMode) + { + case BrushMappingMode.RelativeToBoundingBox: + x1 = x + (int)((long)(width - 1) * StartX / RelativeBoundingBoxSize); + y1 = y + (int)((long)(height - 1) * StartY / RelativeBoundingBoxSize); + x2 = x + (int)((long)(width - 1) * EndX / RelativeBoundingBoxSize); + y2 = y + (int)((long)(height - 1) * EndY / RelativeBoundingBoxSize); + break; + default: //case BrushMappingMode.Absolute: + x1 = StartX; + y1 = StartY; + x2 = EndX; + y2 = EndY; + break; + } + + bmp.DrawRectangle(outlineColor, outlineThickness, x, y, width, height, 0, 0, + StartColor, x1, y1, EndColor, x2, y2, Opacity); + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/MediaContext.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/MediaContext.cs new file mode 100644 index 0000000..b3fc291 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/MediaContext.cs @@ -0,0 +1,275 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI; +using System; +using System.Collections; +using System.Diagnostics; +using nanoFramework.UI.Threading; + +namespace nanoFramework.Presentation.Media +{ + /// + /// The MediaContext class controls the rendering + /// + internal class MediaContext : DispatcherObject , IDisposable + { + /// + /// The MediaContext lives in the Dispatcher and is the MediaSystem's class that keeps + /// per Dispatcher state. + /// + internal MediaContext() + { + _renderMessage = new DispatcherOperationCallback(RenderMessageHandler); + + // we have one render target, the window manager + _target = WindowManager.Instance; + _screen = new Bitmap(SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight); + } + + /// + /// Gets the MediaContext from the context passed in as argument. + /// + internal static MediaContext From(Dispatcher dispatcher) + { + //Debug.Assert(dispatcher != null, "Dispatcher required"); + + MediaContext cm = dispatcher._mediaContext; + + if (cm == null) + { + lock (typeof(GlobalLock)) + { + cm = dispatcher._mediaContext; + + if (cm == null) + { + cm = new MediaContext(); + dispatcher._mediaContext = cm; + } + } + } + + return cm; + } + + private class InvokeOnRenderCallback + { + private DispatcherOperationCallback _callback; + private object _arg; + + public InvokeOnRenderCallback( + DispatcherOperationCallback callback, + object arg) + { + _callback = callback; + _arg = arg; + } + + public void DoWork() + { + _callback(_arg); + } + } + + internal void BeginInvokeOnRender(DispatcherOperationCallback callback, object arg) + { + //Debug.Assert(callback != null); + + // While technically it could be OK for the arg to be null, for now + // I know that arg represents the this reference for the layout + // process and should never be null. + + //Debug.Assert(arg != null); + + if (_invokeOnRenderCallbacks == null) + { + lock (this) + { + if (_invokeOnRenderCallbacks == null) + { + _invokeOnRenderCallbacks = new ArrayList(); + } + } + } + + lock (_invokeOnRenderCallbacks) + { + _invokeOnRenderCallbacks.Add(new InvokeOnRenderCallback(callback, arg)); + } + + PostRender(); + } + + /// + /// If there is already a render operation in the Dispatcher queue, this + /// method will do nothing. If not, it will add a + /// render operation. + /// + /// + /// This method should only be called when a render is necessary "right + /// now." Events such as a change to the visual tree would result in + /// this method being called. + /// + internal void PostRender() + { + VerifyAccess(); + + if (!_isRendering) + { + if (_currentRenderOp == null) + { + // If we don't have a render operation in the queue, add one + _currentRenderOp = Dispatcher.BeginInvoke(_renderMessage, null); + } + } + } + + internal void AddDirtyArea(int x, int y, int w, int h) + { + if (x < 0) x = 0; + if (x + w > _screenW) w = _screenW - x; + if (w <= 0) return; + + if (y < 0) y = 0; + if (y + h > _screenH) h = _screenH - y; + if (h <= 0) return; + + int x1 = x + w; + int y1 = y + h; + + if (x < _dirtyX0) _dirtyX0 = x; + if (y < _dirtyY0) _dirtyY0 = y; + if (x1 > _dirtyX1) _dirtyX1 = x1; + if (y1 > _dirtyY1) _dirtyY1 = y1; + } + + private int _screenW = SystemMetrics.ScreenWidth, _screenH = SystemMetrics.ScreenHeight; + private int _dirtyX0 = SystemMetrics.ScreenWidth, _dirtyY0 = SystemMetrics.ScreenHeight, _dirtyX1 = 0, _dirtyY1 = 0; + + /// + /// This is the standard RenderMessageHandler callback, posted via PostRender() + /// and Resize(). This wraps RenderMessageHandlerCore and emits an ETW events + /// to trace its execution. + /// + internal object RenderMessageHandler(object arg) + { + try + { + _isRendering = true; + + //_screen.Clear(); + + if (_invokeOnRenderCallbacks != null) + { + int callbackLoopCount = 0; + int count = _invokeOnRenderCallbacks.Count; + + while (count > 0) + { + callbackLoopCount++; + if (callbackLoopCount > 153) + { + throw new InvalidOperationException("infinite loop"); + } + + InvokeOnRenderCallback[] callbacks; + + lock (_invokeOnRenderCallbacks) + { + count = _invokeOnRenderCallbacks.Count; + callbacks = new InvokeOnRenderCallback[count]; + + _invokeOnRenderCallbacks.CopyTo(callbacks); + _invokeOnRenderCallbacks.Clear(); + } + + for (int i = 0; i < count; i++) + { + callbacks[i].DoWork(); + } + + count = _invokeOnRenderCallbacks.Count; + } + } + + DrawingContext dc = new DrawingContext(_screen); + + /* The dirty rectange MUST be read after the InvokeOnRender callbacks are + * complete, as they can trigger layout changes or invalidate controls + * which are expected to be redrawn. */ + int x = _dirtyX0; + int y = _dirtyY0; + int w = _dirtyX1 - _dirtyX0; + int h = _dirtyY1 - _dirtyY0; + _dirtyX0 = _screenW; _dirtyY0 = _screenH; + _dirtyX1 = _dirtyY1 = 0; + + try + { + if (w > 0 && h > 0) + { + // + // This is the big Render! + // + // We've now updated layout and the updated scene will be + // rendered. + dc.PushClippingRectangle(x, y, w, h); + _target.RenderRecursive(dc); + dc.PopClippingRectangle(); + } + } + finally + { + dc.Close(); + if (w > 0 && h > 0) + { + _screen.Flush(x, y, w, h); + } + } + } + finally + { + _currentRenderOp = null; + _isRendering = false; + } + + return null; + } + + /// + /// Message delegate. + /// + private DispatcherOperation _currentRenderOp; + private DispatcherOperationCallback _renderMessage; + + /// + /// Indicates that we are in the middle of processing a render message. + /// + private bool _isRendering; + + private ArrayList _invokeOnRenderCallbacks; + + private UIElement _target; + private Bitmap _screen; + + private class GlobalLock { } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + _screen.Dispose(); + } + + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Pen.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Pen.cs new file mode 100644 index 0000000..9502d27 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Pen.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public class Pen + { + /// + /// + /// + public Color Color; + + /// + /// + /// + public ushort Thickness; + + /// + /// + /// + /// + public Pen(Color color) + : this(color, 1) + { + } + + /// + /// + /// + /// + /// + public Pen(Color color, ushort thickness) + { + Color = color; + Thickness = thickness; + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/SolidColorBrush.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/SolidColorBrush.cs new file mode 100644 index 0000000..41928cb --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/SolidColorBrush.cs @@ -0,0 +1,349 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public sealed class SolidColorBrush : Brush + { + /// + /// + /// + public Color Color; + + /// + /// + /// + /// + public SolidColorBrush(Color color) + { + Color = color; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected internal override void RenderRectangle(Bitmap bmp, Pen pen, int x, int y, int width, int height) + { + Color outlineColor = (pen != null) ? pen.Color : (Color)0x0; + ushort outlineThickness = (pen != null) ? pen.Thickness : (ushort)0; + + bmp.DrawRectangle(outlineColor, outlineThickness, x, y, width, height, 0, 0, + Color, 0, 0, Color, 0, 0, Opacity); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected internal override void RenderEllipse(Bitmap bmp, Pen pen, int x, int y, int xRadius, int yRadius) + { + Color outlineColor = (pen != null) ? pen.Color : (Color)0x0; + ushort outlineThickness = (pen != null) ? pen.Thickness : (ushort)0; + + bmp.DrawEllipse(outlineColor, outlineThickness, x, y, xRadius, yRadius, + Color, 0, 0, Color, 0, 0, Opacity); + } + + class LineSegment + { + public int x1; + public int y1; + public int x2; + public int y2; + + /// + /// We will use Bresenham alg for line calc. + /// + public int dx; + public int dy; + public int cx; + public int e; + public bool highSlope; + public int ix; + public int processedPts; + } + + private void Swap(ref int a, ref int b) + { + int t = a; + a = b; + b = t; + } + + private int Abs(int a) + { + if (a < 0) return -a; + + return a; + } + + const int c_YMinBit = 0x40000000; + const int c_XValueMask = 0x3FFFFFFF; + + /// + /// Basic algorithm uses scan lines to fill the polygon. + /// No multiplication or division is needed, neither is floating point calculation. + /// + /// + /// + /// + protected internal override void RenderPolygon(Bitmap bmp, Pen outline, int[] pts) + { + int n = pts.Length / 2; // This is number of points and number of lines (closed polygon). + + // Polygon to fill must have at least 3 points. + if (n < 3) + return; + + // Nothing to do if this is a transparent brush. + if (Opacity == Bitmap.OpacityTransparent) + return; + + int i = 0; + int y = 0; + int yLow = 0; + int yHigh = 0; + + LineSegment[] lines = new LineSegment[n]; + + int []xPoints = new int[n]; + + // Initialize line segments. + for (i = 0; i < n; i++) + { + lines[i] = new LineSegment(); + lines[i].processedPts = 0; + xPoints[i] = new int(); + + lines[i].x1 = pts[i * 2]; + lines[i].y1 = pts[i * 2 + 1]; + if (i < (n - 1)) + { + lines[i].x2 = pts[(i + 1) * 2]; + lines[i].y2 = pts[(i + 1) * 2 + 1]; + } + else + { + lines[i].x2 = pts[0]; + lines[i].y2 = pts[1]; + } + + // Reverse the points to make sure y1 <= y2 always. + if (lines[i].y2 < lines[i].y1) + { + Swap(ref lines[i].y2, ref lines[i].y1); + Swap(ref lines[i].x2, ref lines[i].x1); + } + + // Calculate slopes and increments. + lines[i].dx = Abs(lines[i].x2 - lines[i].x1); + lines[i].dy = Abs(lines[i].y2 - lines[i].y1); + lines[i].cx = lines[i].x1; + lines[i].e = 0; + + if (lines[i].dx < lines[i].dy) + { + // Angle is 45 degree or more. So y increases faster. + lines[i].highSlope = true; + } + else + { + lines[i].highSlope = false; + } + + // Actual increment direction. + if (lines[i].x2 > lines[i].x1) lines[i].ix = 1; + else lines[i].ix = -1; + + if (i == 0) + { + yLow = lines[i].y1; + yHigh = lines[i].y2; + } + else + { + if (lines[i].y1 < yLow) yLow = lines[i].y1; + if (lines[i].y2 > yHigh) yHigh = lines[i].y2; + } + } + + // Fill via scan lines between yLow and yHigh. + for (y = yLow; y <= yHigh; y++) + { + int j = 0; + for (i = 0; i < n; i++) + { + xPoints[i] = Int32.MaxValue; + } + + // Find intersection points for given y. + for (i = 0; i < n; i++) + { + if (y < lines[i].y1) continue; + if (y > lines[i].y2) continue; + + lines[i].processedPts++; + + if (lines[i].dy != 0) + { + if (lines[i].highSlope) + { + // For this y find the x, which either the same pixel + // in last iteration or next one. + if (y == lines[i].y1) lines[i].cx = lines[i].x1; + else if (y == lines[i].y2) lines[i].cx = lines[i].x2; + else + { + lines[i].e += lines[i].dx; + if ((lines[i].e << 1) >= lines[i].dy) + { + lines[i].cx += lines[i].ix; + lines[i].e -= lines[i].dy; + } + } + } + else + { + // In this case for every y pixel inc, x increases more than 1 pixel. + if (y == lines[i].y1) lines[i].cx = lines[i].x1; + else if (y == lines[i].y2) lines[i].cx = lines[i].x2; + else + { + for (; ; ) + { + lines[i].cx += lines[i].ix; + + lines[i].e += lines[i].dy; + if ((lines[i].e << 1) >= lines[i].dx) + { + lines[i].e -= lines[i].dx; + break; + } + } + } + } + } + + // Insertion sort, do not insert back to back duplicates + int x1; + bool x1YMin; + int x2 = int.MaxValue; + bool x2YMin = false; + + // add both x endpoints if the line is horizontal + + if (lines[i].dy == 0) + { + x1 = lines[i].x1; + x1YMin = true; + + x2 = lines[i].x2; + x2YMin = true; + + if (x2 < 0) x2 = 0; + } + else + { + x1 = lines[i].cx; + x1YMin = lines[i].processedPts == 1; + } + + // We don't need to process negative values of x + if (x1 < 0) x1 = 0; + + + int idx1 = -1; + int idx2 = x2 == int.MaxValue ? x2 : -1; + int offset = 0; + + // First we search for the indexes of x1 and x2 (if neccessary) and then we will insert the + // items, by shifting the elements in the array + for (j = 0; j < n; j++) + { + int ix = (xPoints[j] & c_XValueMask); + bool isYMin = (xPoints[j] & c_YMinBit) != 0; + + if (idx1 == -1) + { + + // Only add duplicate x values if the intersection produces up (^) or down (v) angles + // as opposed to right (<) or left (>) angles. + + if (ix == x1 && isYMin != x1YMin) { idx1 = int.MaxValue; } + else if (ix > x1 ) { idx1 = j + offset; offset++; } + } + + if (idx2 == -1) + { + + // Only add duplicate x values if the intersection produces up (^) or down (v) angles + // as opposed to right (<) or left (>) angles. + + if (ix == x2 && isYMin != x2YMin) { idx2 = int.MaxValue; } + else if (ix > x2 ) { idx2 = j + offset; offset++; } + } + + // don't break until we have found both indexes and then end of the list + if(idx1 != -1 && idx2 != -1 && xPoints[j] == int.MaxValue) break; + } + + int idxMin = (idx1 < idx2 ? idx1 : idx2); + + // Because we may have two values to insert, the index to the next item to be shifted + // can either be n-1 or n-2. + offset = (idx2 == int.MaxValue || idx1 == int.MaxValue ? 1 : 2); + + // j is already one element past the last valid data item, so increase it by one if we are + // inserting two elements + j += (offset - 1); + + // Make sure the index does not overflow + if (j >= xPoints.Length) j = xPoints.Length-1; + + for (; j >= idxMin; j--) + { + if (j == idx1) { xPoints[j] = x1YMin ? x1 | c_YMinBit : x1; offset--; continue; } + if (j == idx2) { xPoints[j] = x2YMin ? x2 | c_YMinBit : x2; offset--; continue; } + + if (j < offset) break; + + xPoints[j] = xPoints[j - offset]; + } + } + + // Finally draw the line segments to fill. + for (i = 0; i < xPoints.Length - 1; i += 2) + { + int ix1 = (xPoints[i ] & c_XValueMask); + int ix2 = (xPoints[i + 1] & c_XValueMask); + + if ((ix1 == c_XValueMask) || (ix2 == c_XValueMask)) break; + + bmp.DrawLine(Color, 1, ix1, y, ix2, y); + } + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Stretch.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Stretch.cs new file mode 100644 index 0000000..12bb7a5 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/Stretch.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public enum Stretch + { + /// + /// + /// + None, + + /// + /// + /// + Fill + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextAlignment.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextAlignment.cs new file mode 100644 index 0000000..30d08e1 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextAlignment.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public enum TextAlignment + { + /// + /// + /// + Left, + + /// + /// + /// + Center, + + /// + /// + /// + Right, + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextTrimming.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextTrimming.cs new file mode 100644 index 0000000..cbf9076 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Media/TextTrimming.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation.Media +{ + /// + /// + /// + public enum TextTrimming + { + /// + /// + /// + CharacterEllipsis, + + /// + /// + /// + None, + + /// + /// + /// + WordEllipsis, + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/PresentationSource.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/PresentationSource.cs new file mode 100644 index 0000000..bbc16df --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/PresentationSource.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2017 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI.Input; +using nanoFramework.UI.Threading; + +namespace nanoFramework.Presentation +{ + /// + /// Presentation source is our connection to the rest of the managed system. + /// + /// + public class PresentationSource : DispatcherObject + { + //------------------------------------------------------ + // + // Constructors + // + //------------------------------------------------------ + + #region Constructors + /// + /// Constructs an instance of the PresentationSource object. + /// + public PresentationSource() + { + } + + #endregion + + /// + /// The Root UIElement for this source. + /// + public UIElement RootUIElement + { + get + { + return _rootUIElement; + } + + set + { + VerifyAccess(); + + if (_rootUIElement != value) + { + UIElement oldRoot = _rootUIElement; + + _rootUIElement = value; + + if (value != null) + { + /* need layout events + _rootUIElement.LayoutUpdated += new EventHandler(OnLayoutUpdated); + */ + } + + if (oldRoot != null) + { + /* we need layout events + oldRoot.LayoutUpdated -= new EventHandler(OnLayoutUpdated); + */ + } + + /* we need to generate an event here + RootChanged(oldRoot, value); + */ + + // set up the size. + value.Measure(Media.Constants.MaxExtent, Media.Constants.MaxExtent); + + int desiredWidth, desiredHeight; + value.GetDesiredSize(out desiredWidth, out desiredHeight); + value.Arrange(0, 0, desiredWidth, desiredHeight); + + // update focus. + Buttons.Focus(value); + } + } + } + + private UIElement _rootUIElement; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Ellipse.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Ellipse.cs new file mode 100644 index 0000000..683c9e9 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Ellipse.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Presentation.Shapes +{ + /// + /// + /// + public class Ellipse : Shape + { + /// + /// + /// + /// + /// + public Ellipse(int xRadius, int yRadius) + { + if( xRadius < 0 || yRadius < 0) + { + throw new ArgumentException(); + } + + this.Width = xRadius * 2 + 1; + this.Height = yRadius * 2 + 1; + } + + /// + /// + /// + /// + public override void OnRender(Media.DrawingContext dc) + { + // Make room for cases when strokes are thick. + int x = _renderWidth / 2 + Stroke.Thickness - 1; + int y = _renderHeight / 2 + Stroke.Thickness - 1; + int w = _renderWidth / 2 - (Stroke.Thickness - 1) * 2; + int h = _renderHeight / 2 - (Stroke.Thickness - 1) * 2; + + dc.DrawEllipse(Fill, Stroke, x, y, w, h); + } + } +} diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Line.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Line.cs new file mode 100644 index 0000000..09220e7 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Line.cs @@ -0,0 +1,96 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Presentation.Shapes +{ + /// + /// + /// + public enum Direction + { + /// + /// + /// + TopToBottom, + + /// + /// + /// + BottomToTop + } + + /// + /// + /// + public class Line : Shape + { + /// + /// + /// + public Line() + : this(0, 0) + { + } + + /// + /// + /// + /// + /// + public Line(int dx, int dy) + { + if( dx < 0 || dy < 0) + { + throw new ArgumentException(); + } + + this.Width = dx + 1; + this.Height = dy + 1; + } + + /// + /// + /// + public Direction Direction + { + get + { + return _direction; + } + + set + { + _direction = value; + Invalidate(); + } + } + + /// + /// + /// + /// + public override void OnRender(Media.DrawingContext dc) + { + int width = this._renderWidth; + int height = this._renderHeight; + + if (_direction == Direction.TopToBottom) + { + dc.DrawLine(Stroke, 0, 0, width - 1, height - 1); + } + else + { + dc.DrawLine(Stroke, 0, height - 1, width - 1, 0); + } + } + + private Direction _direction; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Polygon.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Polygon.cs new file mode 100644 index 0000000..2fb1569 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Polygon.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Presentation.Shapes +{ + /// + /// + /// + public class Polygon : Shape + { + /// + /// + /// + public Polygon() + { + } + + /// + /// + /// + /// + public Polygon(int[] pts) + { + Points = pts; + } + + /// + /// + /// + public int[] Points + { + get + { + return _pts; + } + + set + { + if(value == null || value.Length == 0) + { + throw new ArgumentException(); + } + + _pts = value; + + InvalidateMeasure(); + } + } + + /// + /// + /// + /// + public override void OnRender(Media.DrawingContext dc) + { + if (_pts != null) + { + dc.DrawPolygon(Fill, Stroke, _pts); + } + } + + private int[] _pts; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Rectangle.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Rectangle.cs new file mode 100644 index 0000000..54622ed --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Rectangle.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Presentation.Shapes +{ + /// + /// + /// + public class Rectangle : Shape + { + /// + /// + /// + public Rectangle() + { + Width = 0; + Height = 0; + } + + /// + /// + /// + /// + /// + public Rectangle(int width, int height) + { + if( width < 0 || height < 0) + { + throw new ArgumentException(); + } + + Width = width; + Height = height; + } + + /// + /// + /// + /// + public override void OnRender(Media.DrawingContext dc) + { + int offset = Stroke != null ? Stroke.Thickness / 2 : 0; + + dc.DrawRectangle(Fill, Stroke, offset, offset, _renderWidth - 2 * offset, _renderHeight - 2 * offset); + } + } +} diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Shape.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Shape.cs new file mode 100644 index 0000000..cbc36d3 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Shapes/Shape.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using nanoFramework.UI; + +namespace nanoFramework.Presentation.Shapes +{ + /// + /// + /// + public abstract class Shape : UIElement + { + /// + /// + /// + public Brush Fill + { + get + { + if (_fill == null) + { + _fill = new SolidColorBrush(Color.Black); + _fill.Opacity = Bitmap.OpacityTransparent; + } + + return _fill; + } + + set + { + _fill = value; + Invalidate(); + } + } + + /// + /// + /// + public Pen Stroke + { + get + { + if (_stroke == null) + { + _stroke = new Pen(Color.White, 0); + } + + return _stroke; + } + + set + { + _stroke = value; + Invalidate(); + } + } + + private Brush _fill; + private Pen _stroke; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/SizeToContent.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/SizeToContent.cs new file mode 100644 index 0000000..01ab9bd --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/SizeToContent.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation +{ + /// + /// SizeToContent + /// + public enum SizeToContent + { + /// + /// Does not size to content + /// + Manual = 0, + /// + /// Sizes Width to content's Width + /// + Width = 1, + /// + /// Sizes Height to content's Height + /// + Height = 2, + /// + /// Sizes both Width and Height to content's size + /// + WidthAndHeight = 3, + // Please update IsValidSizeToContent if there are name changes. + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElement.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElement.cs new file mode 100644 index 0000000..e16c437 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElement.cs @@ -0,0 +1,2116 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI.Input; +using nanoFramework.Presentation.Media; +using nanoFramework.UI; +using System; +using System.Collections; +using System.Diagnostics; +using nanoFramework.Runtime.Events; +using nanoFramework.UI.Threading; +using nanoFramework.Touch; +using static nanoFramework.UI.Temporary; + +namespace nanoFramework.Presentation +{ + /// + /// + /// + public abstract class UIElement : DispatcherObject + { + /// + /// + /// + protected UIElement() + { + EnsureClassHandlers(); + + _flags = Flags.NeverMeasured | Flags.NeverArranged | Flags.Enabled; + + _visibility = Visibility.Visible; + + _verticalAlignment = VerticalAlignment.Stretch; + _horizontalAlignment = HorizontalAlignment.Stretch; + } + + #region Class Handlers + + private static void AddRoutedEventHandler( + Hashtable table, + RoutedEvent routedEvent, + RoutedEventHandler handler, + bool handledEventsToo) + { + if (routedEvent == null || handler == null) + { + throw new ArgumentNullException(); + } + + // Create a new RoutedEventHandler + RoutedEventHandlerInfo routedEventHandlerInfo = + new RoutedEventHandlerInfo(handler, handledEventsToo); + + // Get the entry corresponding to the given RoutedEvent + ArrayList handlers = (ArrayList)table[routedEvent]; + if (handlers == null) + { + table[routedEvent] = (handlers = new ArrayList()); + } + + // Add the RoutedEventHandlerInfo to the list + handlers.Add(routedEventHandlerInfo); + } + + private static void EnsureClassHandlers() + { + // since we can't rely on the order of static constructors, + // we will do this the first time an element is created. + // we have a single dispatcher, so we don't have to worry about + // race conditions here.. + + if (_classEventHandlersStore == null) + { + _classEventHandlersStore = new Hashtable(); + + // buttons + AddRoutedEventHandler(_classEventHandlersStore, Buttons.PreviewButtonDownEvent, new RoutedEventHandler(UIElement.OnPreviewButtonDownThunk), false); + AddRoutedEventHandler(_classEventHandlersStore, Buttons.ButtonDownEvent , new RoutedEventHandler(UIElement.OnButtonDownThunk) , false); + AddRoutedEventHandler(_classEventHandlersStore, Buttons.PreviewButtonUpEvent , new RoutedEventHandler(UIElement.OnPreviewButtonUpThunk) , false); + AddRoutedEventHandler(_classEventHandlersStore, Buttons.ButtonUpEvent , new RoutedEventHandler(UIElement.OnButtonUpThunk) , false); + + // focus + AddRoutedEventHandler(_classEventHandlersStore, Buttons.GotFocusEvent , new RoutedEventHandler(UIElement.OnGotFocusThunk) , true); + AddRoutedEventHandler(_classEventHandlersStore, Buttons.LostFocusEvent , new RoutedEventHandler(UIElement.OnLostFocusThunk) , true); + + AddRoutedEventHandler(_classEventHandlersStore, TouchEvents.TouchDownEvent , new RoutedEventHandler(UIElement.OnTouchDownThunk) , false); + AddRoutedEventHandler(_classEventHandlersStore, TouchEvents.TouchUpEvent , new RoutedEventHandler(UIElement.OnTouchUpThunk) , false); + AddRoutedEventHandler(_classEventHandlersStore, TouchEvents.TouchMoveEvent , new RoutedEventHandler(UIElement.OnTouchMoveThunk) , false); + + AddRoutedEventHandler(_classEventHandlersStore, GenericEvents.GenericStandardEvent, new RoutedEventHandler(UIElement.OnGenericEventThunk) , true); + + } + } + + // + // We have this thunking layer because the delegates for class handlers are static, + // but we need to call instance methods. An alternative would be to either not have class handlers (inconsistent), + // or to use per-instance storage for every class handler (wasteful). So we will trade off some code size here + // to get class handlers. + + private static void OnPreviewButtonDownThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnPreviewButtonDown((ButtonEventArgs)e); } + + private static void OnButtonDownThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnButtonDown((ButtonEventArgs)e); } + + private static void OnPreviewButtonUpThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnPreviewButtonUp((ButtonEventArgs)e); } + + private static void OnButtonUpThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnButtonUp((ButtonEventArgs)e); } + + private static void OnGotFocusThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnGotFocus((FocusChangedEventArgs)e); } + + private static void OnLostFocusThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnLostFocus((FocusChangedEventArgs)e); } + + private static void OnGenericEventThunk(object sender, RoutedEventArgs e) { ((UIElement)sender).OnGenericEvent((GenericEventArgs)e); } + + /// + /// + /// + /// + protected virtual void OnGenericEvent(GenericEventArgs e) + { + GenericEventEx genericEvent = e.InternalEvent; + switch ((EventCategory)genericEvent.Category) + { + case (EventCategory)EventCategoryEx.Gesture: + { + TouchGestureEventArgs ge = new TouchGestureEventArgs(); + + ge.Gesture = (TouchGesture)genericEvent.Message; + ge.X = genericEvent.X; + ge.Y = genericEvent.Y; + ge.Arguments = (ushort)genericEvent.Data; + + if (ge.Gesture == TouchGesture.Begin) + { + OnTouchGestureStarted(ge); + } + else if (ge.Gesture == TouchGesture.End) + { + OnTouchGestureEnded(ge); + } + else + { + OnTouchGestureChanged(ge); + } + + break; + } + default: + { + break; + } + } + + } + + private static void OnTouchDownThunk(object sender, RoutedEventArgs e) + { + ((UIElement)sender).OnTouchDown((TouchEventArgs)e); + } + + private static void OnTouchUpThunk(object sender, RoutedEventArgs e) + { + ((UIElement)sender).OnTouchUp((TouchEventArgs)e); + } + + private static void OnTouchMoveThunk(object sender, RoutedEventArgs e) + { + ((UIElement)sender).OnTouchMove((TouchEventArgs)e); + } + + /// + /// + /// + /// + protected virtual void OnTouchDown(TouchEventArgs e) + { + if (TouchDown != null) + { + TouchDown(this, e); + } + } + + /// + /// + /// + /// + protected virtual void OnTouchUp(TouchEventArgs e) + { + if (TouchUp != null) + { + TouchUp(this, e); + } + } + +/// +/// +/// +/// + protected virtual void OnTouchMove(TouchEventArgs e) + { + if (TouchMove != null) + { + TouchMove(this, e); + } + } + + /// + /// + /// + /// + protected virtual void OnTouchGestureStarted(TouchGestureEventArgs e) + { + if (TouchGestureStart != null) + { + TouchGestureStart(this, e); + } + } + + /// + /// + /// + /// + protected virtual void OnTouchGestureChanged(TouchGestureEventArgs e) + { + if (TouchGestureChanged != null) + { + TouchGestureChanged(this, e); + } + } + + /// + /// + /// + /// + protected virtual void OnTouchGestureEnded(TouchGestureEventArgs e) + { + if (TouchGestureEnd != null) + { + TouchGestureEnd(this, e); + } + } + + /// + /// + /// + /// + protected virtual void OnPreviewButtonDown(ButtonEventArgs e) { } + + /// + /// + /// + /// + protected virtual void OnButtonDown(ButtonEventArgs e) { } + + /// + /// + /// + /// + protected virtual void OnPreviewButtonUp(ButtonEventArgs e) { } + + /// + /// + /// + /// + protected virtual void OnButtonUp(ButtonEventArgs e) { } + + /// + /// + /// + /// + protected virtual void OnGotFocus(FocusChangedEventArgs e) { } + + /// + /// + /// + /// + protected virtual void OnLostFocus(FocusChangedEventArgs e) { } + + #endregion Class Handlers + + /// + /// + /// + public event TouchEventHandler TouchDown; + + /// + /// + /// + public event TouchEventHandler TouchUp; + + /// + /// + /// + public event TouchEventHandler TouchMove; + + /// + /// + /// + public event TouchGestureEventHandler TouchGestureStart; + /// + /// + /// + public event TouchGestureEventHandler TouchGestureChanged; + /// + /// + /// + public event TouchGestureEventHandler TouchGestureEnd; + + /// + /// + /// + /// + /// + public void GetDesiredSize(out int width, out int height) + { + if (this.Visibility == Visibility.Collapsed) + { + width = 0; + height = 0; + } + else + { + width = _desiredWidth; + height = _desiredHeight; + } + } + + /// + /// + /// + /// + /// + /// + /// + public void GetMargin(out int left, out int top, out int right, out int bottom) + { + left = _marginLeft; + top = _marginTop; + right = _marginRight; + bottom = _marginBottom; + } + + /// + /// + /// + /// + public void SetMargin(int length) + { + VerifyAccess(); + + SetMargin(length, length, length, length); + } + + /// + /// + /// + /// + /// + /// + /// + public void SetMargin(int left, int top, int right, int bottom) + { + VerifyAccess(); + + _marginLeft = left; + _marginTop = top; + _marginRight = right; + _marginBottom = bottom; + InvalidateMeasure(); + } + + /// + /// + /// + public int ActualWidth + { + get + { + return _renderWidth; + } + } + + /// + /// + /// + public int ActualHeight + { + get + { + return _renderHeight; + } + } + + /// + /// + /// + public int Height + { + get + { + int height; + if (IsHeightSet(out height)) + { + return height; + } + else + { + throw new InvalidOperationException("height not set"); + } + } + + set + { + VerifyAccess(); + + if(value < 0) throw new ArgumentException(); + + if (_requestedSize == null) + { + _requestedSize = new Pair(); + } + + _requestedSize._second = value; + _requestedSize._status |= Pair.Flags_Second; + InvalidateMeasure(); + } + } + + /// + /// + /// + public int Width + { + get + { + int width; + if (IsWidthSet(out width)) + { + return width; + } + else + { + throw new InvalidOperationException("width not set"); + } + } + + set + { + VerifyAccess(); + + if(value < 0) throw new ArgumentException(); + + if (_requestedSize == null) + { + _requestedSize = new Pair(); + } + + _requestedSize._first = value; + _requestedSize._status |= Pair.Flags_First; + InvalidateMeasure(); + } + } + + private bool IsHeightSet(out int height) + { + Pair size = _requestedSize; + if (size != null && (size._status & Pair.Flags_Second) != 0) + { + height = size._second; + return true; + } + + height = 0; + return false; + } + + private bool IsWidthSet(out int width) + { + Pair size = _requestedSize; + if (size != null && (size._status & Pair.Flags_First) != 0) + { + width = size._first; + return true; + } + + width = 0; + return false; + } + + /// + /// + /// + /// + /// + public void GetLayoutOffset(out int x, out int y) + { + x = this._offsetX; + y = this._offsetY; + } + + /// + /// + /// + /// + /// + public void GetRenderSize(out int width, out int height) + { + width = this._renderWidth; + height = this._renderHeight; + } + + /// + /// + /// + protected UIElementCollection LogicalChildren + { + get + { + VerifyAccess(); + + if (_logicalChildren == null) + { + _logicalChildren = new UIElementCollection(this); + } + + return _logicalChildren; + } + } + + /// + /// OnChildrenChanged is called when the UIElementCollection of the UIElement is edited. + /// + protected internal virtual void OnChildrenChanged( + UIElement added, + UIElement removed, + int indexAffected) + { + //Child visibility can't change if parent isn't visible + if ((this._flags & Flags.IsVisibleCache) != 0) + { + if (removed != null && removed._visibility == Visibility.Visible) + { + removed._flags &= ~Flags.IsVisibleCache; + removed.OnIsVisibleChanged(true); + } + + if (added != null && added._visibility == Visibility.Visible) + { + added._flags |= Flags.IsVisibleCache; + added.OnIsVisibleChanged(false); + } + } + } + + /// + /// A property indicating if the button is focused on this + /// element or not. + /// + public bool IsFocused + { + get + { + // avalon also has a dependencyproperty for this I think, but I'm not sure we need it. + return (Buttons.FocusedElement == this); + } + } + + private void ComputeAlignmentOffset(int clientWidth, int clientHeight, + int arrangeWidth, int arrangeHeight, + out int dx, out int dy) + { + HorizontalAlignment ha = HorizontalAlignment; + VerticalAlignment va = VerticalAlignment; + + //this is to degenerate Stretch to Top-Left in case when clipping is about to occur + //if we need it to be Center instead, simply remove these 2 ifs + if (ha == HorizontalAlignment.Stretch + && arrangeWidth > clientWidth) + { + ha = HorizontalAlignment.Left; + } + + if (va == VerticalAlignment.Stretch + && arrangeHeight > clientHeight) + { + va = VerticalAlignment.Top; + } + + //end of degeneration of Stretch to Top-Left + + if (ha == HorizontalAlignment.Center + || ha == HorizontalAlignment.Stretch) + { + dx = (clientWidth - arrangeWidth) / 2; + } + else if (ha == HorizontalAlignment.Right) + { + dx = clientWidth - arrangeWidth; + } + else + { + dx = 0; + } + + if (va == VerticalAlignment.Center + || va == VerticalAlignment.Stretch) + { + dy = (clientHeight - arrangeHeight) / 2; + } + else if (va == VerticalAlignment.Bottom) + { + dy = clientHeight - arrangeHeight; + } + else + { + dy = 0; + } + } + + /// + /// Recursively propagates IsLayoutSuspended flag down to the whole v's sub tree. + /// + internal static void PropagateSuspendLayout(UIElement v) + { + if ((v._flags & Flags.IsLayoutSuspended) == 0) + { + v._flags |= Flags.IsLayoutSuspended; + + UIElementCollection children = v._logicalChildren; + if (children != null) + { + int count = children.Count; + for (int i = 0; i < count; i++) + { + UIElement child = children[i]; + if (child != null) + { + PropagateSuspendLayout(child); + } + } + } + } + } + + /// + /// Recursively resets IsLayoutSuspended flag on all visuals of the whole v's sub tree. + /// For UIElements also re-inserts the UIElement into Measure and / or Arrange update queues + /// if necessary. + /// + internal static void PropagateResumeLayout(UIElement e) + { + if ((e._flags & Flags.IsLayoutSuspended) != 0) + { + e._flags &= ~Flags.IsLayoutSuspended; + + //Debug.Assert((e._flags & (Flags.MeasureInProgress | Flags.ArrangeInProgress)) == 0); + + bool requireMeasureUpdate = ((e._flags & Flags.InvalidMeasure) != 0) && ((e._flags & Flags.NeverMeasured) == 0); + bool requireArrangeUpdate = ((e._flags & Flags.InvalidArrange) != 0) && ((e._flags & Flags.NeverArranged) == 0); + LayoutManager layoutManager = (requireMeasureUpdate || requireArrangeUpdate) ? LayoutManager.From(e.Dispatcher) : null; + if (requireMeasureUpdate) + { + layoutManager.MeasureQueue.Add(e); + } + + if (requireArrangeUpdate) + { + layoutManager.ArrangeQueue.Add(e); + } + + UIElementCollection children = e._logicalChildren; + if (children != null) + { + int count = children.Count; + for (int i = 0; i < count; i++) + { + UIElement child = children[i]; + if (child != null) + { + PropagateResumeLayout(child); + } + } + } + } + } + + /// + /// Updates DesiredSize of the UIElement. Must be called by parents from their MeasureOverride, to form recursive update. + /// This is first pass of layout update. + /// + /// + /// Measure is called by parents on their children. Internally, Measure calls MeasureOverride override on the same object, + /// giving it opportunity to compute its DesiredSize. + /// This method will return immediately if child is not Dirty, previously measured + /// and availableSize is the same as cached. + /// This method also resets the IsMeasureinvalid bit on the child. + /// In case when "unbounded measure to content" is needed, parent can use availableSize + /// as double.PositiveInfinity. Any returned size is OK in this case. + /// + /// Available width that parent can give to the child. May be MaxValue (when parent wants to + /// measure to content). This is soft constraint. Child can return bigger size to indicate that it wants bigger space and hope + /// that parent can throw in scrolling... + /// Available height that parent can give to the child. May be MaxValue (when parent wants to + /// measure to content). This is soft constraint. Child can return bigger size to indicate that it wants bigger space and hope + /// that parent can throw in scrolling... + public void Measure(int availableWidth, int availableHeight) + { + VerifyAccess(); + +#if NANOCLR_TRACE_DEBUG_LAYOUT + Trace.Print(this.ToString() + ".Measure " + PrintSize(availableWidth, availableHeight)); +#endif + + // cache here, since we read this a few times before writing. + Flags flags = _flags; + + //if Collapsed, we should not Measure, keep dirty bit but remove request + if (this.Visibility == Visibility.Collapsed || ((flags & Flags.IsLayoutSuspended) != 0)) + { + //reset measure request. + + LayoutManager.CurrentLayoutManager.MeasureQueue.Remove(this); + + // remember though that parent tried to measure at this size + // in case when later this element is called to measure incrementally + // it has up-to-date information stored in _previousAvailableSize + _previousAvailableWidth = availableWidth; + _previousAvailableHeight = availableHeight; + + return; + } + +#if NANOCLR_TRACE_DEBUG_LAYOUT + if (true) + { + bool neverMeasured = ((_flags & Flags.NeverMeasured) != 0); + + String traceMessage = this.GetType().FullName + ": "; + + if (_flags&Flags.InvalidMeasure == 0) + { + traceMessage += "dirty "; + } + else + { + traceMessage += "not dirty "; + } + + if (neverMeasured == false) + { + traceMessage += "measured never"; + } + + else + { + traceMessage += "measured before "; + } + + traceMessage += " old-h= " + Convert.ToString(_previousAvailableHeight); + traceMessage += " old-w= " + Convert.ToString(_previousAvailableWidth); + traceMessage += " cur-h= " + Convert.ToString(availableHeight); + traceMessage += " cur-w= " + Convert.ToString(availableWidth); + + Trace.Print(traceMessage); + } + +#endif + + // Bypass if possible + // + if (((flags & Flags.InvalidMeasure) == 0) && + ((flags & Flags.NeverMeasured) == 0) && + availableWidth == _previousAvailableWidth && + availableHeight == _previousAvailableHeight) + { +#if NANOCLR_TRACE_DEBUG_LAYOUT + String traceMessage = this.GetType().FullName + ": measure by passed"; + + Trace.Print(traceMessage); +#endif + return; + } + + _flags &= ~Flags.NeverMeasured; + + int previousWidth = _desiredWidth; + int previousHeight = _desiredHeight; + + //we always want to be arranged, ensure arrange request + //doing it before OnMeasure prevents unneeded requests from children in the queue + InvalidateArrange(); + + // MeasureInProgress prevents OnChildDesiredSizeChange to cause the elements be put + // into the queue. + _flags |= Flags.MeasureInProgress; + + int desiredWidth; + int desiredHeight; + try + { + // Compute available size for call into MeasureOverride + // + int marginWidth = _marginLeft + _marginRight; + int marginHeight = _marginTop + _marginBottom; + int frameworkAvailableWidth = availableWidth - marginWidth; + int frameworkAvailableHeight = availableHeight - marginHeight; + if (_requestedSize != null) + { + // Respect requested size + // + bool haveRequestedWidth = (_requestedSize._status & Pair.Flags_First) != 0; + bool haveRequestedHeight = (_requestedSize._status & Pair.Flags_Second) != 0; + if (haveRequestedWidth) + { + frameworkAvailableWidth = Mathematics.Min(_requestedSize._first, frameworkAvailableWidth); + } + + if (haveRequestedHeight) + { + frameworkAvailableHeight = Mathematics.Min(_requestedSize._second, frameworkAvailableHeight); + } + + MeasureOverride(frameworkAvailableWidth, frameworkAvailableHeight, out desiredWidth, out desiredHeight); + + if (haveRequestedWidth) + { + desiredWidth = _requestedSize._first; + } + + if (haveRequestedHeight) + { + desiredHeight = _requestedSize._second; + } + } + else + { + // No requested size specified + MeasureOverride(frameworkAvailableWidth, frameworkAvailableHeight, out desiredWidth, out desiredHeight); + } + + // Restrict the desired size to the available size + _unclippedWidth = desiredWidth; + _unclippedHeight = desiredHeight; + desiredWidth = Mathematics.Min(desiredWidth, frameworkAvailableWidth); + desiredHeight = Mathematics.Min(desiredHeight, frameworkAvailableHeight); + + // Add margins + desiredWidth += marginWidth; + desiredHeight += marginHeight; + } + finally + { + _flags &= ~Flags.MeasureInProgress; + _previousAvailableWidth = availableWidth; + _previousAvailableHeight = availableHeight; + } + + _flags &= ~Flags.InvalidMeasure; + + LayoutManager.CurrentLayoutManager.MeasureQueue.Remove(this); + + _desiredWidth = desiredWidth; + _desiredHeight = desiredHeight; + + //notify parent if our desired size changed (waterfall effect) + if ((_flags & Flags.MeasureDuringArrange) == 0 && !(previousWidth == desiredWidth && previousHeight == desiredHeight)) + { + UIElement parent = _parent; + if (parent != null && (parent._flags & Flags.MeasureInProgress) == 0) + { + parent.OnChildDesiredSizeChanged(this); + } + } + +#if TNANOCLR_TRACE_DEBUG_LAYOUT + if (true) + { + String traceMessage = this.GetType().FullName + ": measured"; + + Trace.Print(traceMessage); + } + +#endif + } + + /// + /// Parents or system call this method to arrange the internals of children on a second pass of layout update. + /// + /// + /// This method internally calls ArrangeOverride override, giving the derived class opportunity + /// to arrange its children and/or content using final computed size. + /// In their ArrangeOverride overrides, derived class is supposed to create its visual structure and + /// prepare itself for rendering. Arrange is called by parents + /// from their implementation of ArrangeOverride or by system when needed. + /// This method sets Bounds=finalSize before calling ArrangeOverride. + /// + /// This is the final X location that parent or system wants this UIElement to assume. + /// This is the final Y location that parent or system wants this UIElement to assume. + /// This is the Width that parent or system wants this UIElement to assume. + /// This is the height that parent or system wants this UIElement to assume. + + public void Arrange(int finalRectX, int finalRectY, int finalRectWidth, int finalRectHeight) + { + VerifyAccess(); + + //in case parent did not call Measure on a child, we call it now. + //parent can skip calling Measure on a child if it does not care about child's size + //passing finalSize practically means "set size" because that's what Measure(sz)/Arrange(same_sz) means + if ((_flags & (Flags.InvalidMeasure | Flags.NeverMeasured)) != 0) + { + try + { + _flags |= Flags.MeasureDuringArrange; + Measure(finalRectWidth, finalRectHeight); + } + finally + { + _flags &= ~Flags.MeasureDuringArrange; + } + } + + //if Collapsed, we should not Arrange, keep dirty bit but remove request + // + if (this.Visibility == Visibility.Collapsed + || ((_flags & Flags.IsLayoutSuspended) != 0) + + ) + { + //reset arrange request. + LayoutManager.CurrentLayoutManager.ArrangeQueue.Remove(this); + + // remember though that parent tried to arrange at this rect + // in case when later this element is called to arrange incrementally + // it has up-to-date information stored in _finalRect + _finalX = finalRectX; + _finalY = finalRectY; + _finalWidth = finalRectWidth; + _finalHeight = finalRectHeight; + return; + } + + //your basic bypass. No reason to calc the same thing. + if (((_flags & Flags.InvalidArrange) == 0) && + ((_flags & Flags.NeverArranged) == 0) && + finalRectWidth == _finalWidth && + finalRectHeight == _finalHeight) + { + if (finalRectX != _finalX || finalRectY != _finalY) + { + _offsetX = _offsetX - _finalX + finalRectX; + _offsetY = _offsetY - _finalY + finalRectY; + + // Cache final position + _finalX = finalRectX; + _finalY = finalRectY; + + if (IsRenderable()) + { + PropagateFlags(this, Flags.IsSubtreeDirtyForRender); + } + } + + return; + } + + _flags = (_flags & ~Flags.NeverArranged) | Flags.ArrangeInProgress; + + int marginWidth = _marginLeft + _marginRight; + int marginHeight = _marginTop + _marginBottom; + + // Alignment==Stretch --> arrange at the slot size minus margins + // Alignment!=Stretch --> arrange at the desiredSize minus margins + int arrangeWidth = (HorizontalAlignment == HorizontalAlignment.Stretch) ? finalRectWidth : _desiredWidth; + int arrangeHeight = (VerticalAlignment == VerticalAlignment.Stretch) ? finalRectHeight : _desiredHeight; + + arrangeWidth -= marginWidth; + arrangeHeight -= marginHeight; + + // If a particular size has been requested, and that size is less than the available size, + // honor the requested size. + if (_requestedSize != null) + { + if ((_requestedSize._status & Pair.Flags_First) != 0) + { + int width = _requestedSize._first; + if (width < arrangeWidth) + { + arrangeWidth = width; + } + } + + if ((_requestedSize._status & Pair.Flags_Second) != 0) + { + int height = _requestedSize._second; + if (height < arrangeHeight) + { + arrangeHeight = height; + } + } + } + + try + { + ArrangeOverride(arrangeWidth, arrangeHeight); + } + finally + { + _flags &= ~Flags.ArrangeInProgress; + } + + // Account for alignment + int offsetX, offsetY; + int clientWidth = Mathematics.Max(0, finalRectWidth - marginWidth); + int clientHeight = Mathematics.Max(0, finalRectHeight - marginHeight); + + if (clientWidth != arrangeWidth || clientHeight != arrangeHeight) + { + ComputeAlignmentOffset(clientWidth, clientHeight, arrangeWidth, arrangeHeight, out offsetX, out offsetY); +#if NANOCLR_TRACE_DEBUG_LAYOUT + Trace.Print(this.ToString() + ": ComputeAlignmentOffset: " + PrintSize(clientWidth, clientHeight) + ", " + PrintSize(arrangeWidth, arrangeHeight) + " returned " + offsetX.ToString() + ", " + offsetY.ToString()); +#endif + } + else + { + offsetX = offsetY = 0; + } + + offsetX += finalRectX + _marginLeft; + offsetY += finalRectY + _marginTop; + + _offsetX = offsetX; + _offsetY = offsetY; + _renderWidth = arrangeWidth; + _renderHeight = arrangeHeight; + + // Cache final rect + _finalX = finalRectX; + _finalY = finalRectY; + _finalWidth = finalRectWidth; + _finalHeight = finalRectHeight; + +#if NANOCLR_TRACE_DEBUG_LAYOUT + Trace.Print(this.ToString() + ": Layout offset = " + PrintSize(_offsetX, _offsetY)); +#endif + + // Reset dirtiness + // + _flags &= ~Flags.InvalidArrange; + + LayoutManager.CurrentLayoutManager.ArrangeQueue.Remove(this); + + if (IsRenderable()) + { + PropagateFlags(this, Flags.IsSubtreeDirtyForRender); + } + } + + /// + /// Measurement override. Implement your size-to-content logic here. + /// + /// + /// MeasureOverride is designed to be the main customizability point for size control of layout. + /// UIElement authors should override this method, call Measure on each child UIElement, + /// and compute their desired size based upon the measurement of the children. + /// The return value should be the desired size. + /// Note: It is required that a parent UIElement calls Measure on each child or they won't be sized/arranged. + /// Typical override follows a pattern roughly like this (pseudo-code): + /// + /// + /// + /// + /// + /// The key aspects of this snippet are: + /// + /// You must call Measure on each child UIElement + /// It is common to cache measurement information between the MeasureOverride and ArrangeOverride method calls + /// Calling base.MeasureOverride is not required. + /// Calls to Measure on children are passing either the same availableSize as the parent, or a subset of the area depending + /// on the type of layout the parent will perform (for example, it would be valid to remove the area + /// for some border or padding). + /// + /// + /// Available size that parent can give to the child. May be MaxValue(when parent wants to + /// measure to content). This is soft constraint. Child can return bigger size to indicate that it wants bigger space and hope + /// that parent can throw in scrolling... + /// + /// + /// + protected virtual void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + desiredHeight = desiredWidth = 0; + } + + /// + /// ArrangeOverride allows for the customization of the final sizing and positioning of children. + /// + /// + /// UIElement authors should override this method, call Arrange on each visible child UIElement, + /// to size and position each child UIElement by passing a rectangle reserved for the child within parent space. + /// Note: It is required that a parent UIElement calls Arrange on each child or they won't be rendered. + /// Typical override follows a pattern roughly like this (pseudo-code): + /// + /// + /// + /// + /// + /// + /// Final width + /// Final height + protected virtual void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + UIElementCollection children = _logicalChildren; + if (children != null) + { + int count = children.Count; + for (int i = 0; i < count; i++) + { + UIElement child = children[i]; + child.Arrange(0, 0, arrangeWidth, arrangeHeight); + } + } + } + + /// + /// Call this method to ensure that the whoel subtree of elements that includes this UIElement + /// is properly updated. + /// + /// + /// This ensures that UIElements with IsMeasureInvalid or IsArrangeInvalid will + /// get call to their MeasureOverride and ArrangeOverride, and all computed sizes will be validated. + /// This method does nothing if layout is clean but it does work if layout is not clean so avoid calling + /// it after each change in the UIElement tree. It makes sense to either never call it (system will do this + /// in a deferred manner) or only call it if you absolutely need updated sizes and positions after you do all changes. + /// + public void UpdateLayout() + { + VerifyAccess(); + + LayoutManager.CurrentLayoutManager.UpdateLayout(); + } + + /// + /// Determines if the DesiredSize is valid. + /// + /// + /// A developer can force arrangement to be invalidated by calling InvalidateMeasure. + /// IsArrangeValid and IsMeasureValid are related, + /// in that arrangement cannot be valid without measurement first being valid. + /// + public bool IsMeasureValid + { + get + { + return (_flags & Flags.InvalidMeasure) == 0; + } + } + + /// + /// Determines if the RenderSize and position of child elements is valid. + /// + /// + /// A developer can force arrangement to be invalidated by calling InvalidateArrange. + /// IsArrangeValid and IsMeasureValid are related, in that arrangement cannot be valid without measurement first + /// being valid. + /// + public bool IsArrangeValid + { + get + { + return (_flags & Flags.InvalidArrange) == 0; + } + } + + /// + /// Given x, y co-ordinates of the parent UIElement, + /// find the child control that is directly underneath that point. + /// If there are multiple such controls, the one that was created/inserted + /// into the list last wins. This is because we don't have explicit z-ordering + /// right now. + /// + + /// + /// + /// + public UIElement ChildElementFromPoint(int x, int y) + { + UIElement targetElement = null; + + // Translate. + x -= _offsetX; + y -= _offsetY; + + if ((x >= 0) && (y >= 0) && (x <= _renderWidth) && (y <= _renderHeight)) + { + targetElement = this; + + UIElementCollection children = _logicalChildren; + if (children != null) //children appears to be null, instead of empty array in some cases. + { + if (children.Count > 0) + { + int count = children.Count; + for (int i = count - 1; i >= 0; i--) + { + UIElement child = children[i]; + UIElement target = child.ChildElementFromPoint(x, y); + if (target != null) + { + targetElement = target; + break; + } + } + } + } + } + + return targetElement; + } + + /// + /// + /// + /// + /// + public void GetUnclippedSize(out int width, out int height) + { + width = _unclippedWidth; + height = _unclippedHeight; + } + + /// + /// + /// + /// + /// + /// + public bool ContainsPoint(int x, int y) + { + return (x >= _offsetX && x < (_offsetX + _renderWidth) && y >= _offsetY && y < (_offsetY + _renderHeight)); + } + + /// + /// + /// + /// + /// + /// + public UIElement GetPointerTarget(int x, int y) + { + UIElement target = null; + + UIElementCollection children = _logicalChildren; + + while (children != null) + { + int i = children.Count; + while (--i >= 0) + { + UIElement element = children[i]; + if (element != null && element.Visibility == Visibility.Visible && element.ContainsPoint(x, y)) + { + target = element; + children = element._logicalChildren; + + x -= target._offsetX; + y -= target._offsetY; + break; + } + } + + if (i < 0) + { + break; + } + } + + return target; + } + + /// + /// We are deviating little from their desktop counter parts, mostly for simplicity and perf. + /// + /// + /// + public void PointToScreen(ref int x, ref int y) + { + UIElement client = this; + while (client != null) + { + x += client._offsetX; + y += client._offsetY; + + client = client._parent; + } + } + + /// + /// + /// + /// + /// + public void PointToClient(ref int x, ref int y) + { + UIElement client = this; + //need to cache this value on first call after relayout. + while (client != null) + { + x -= client._offsetX; + y -= client._offsetY; + + client = client._parent; + } + } + + // This is needed to prevent dirty elements from drawing and crashing while doing so. + private bool IsRenderable() + { + // VerifyAccess(); - we're only called by Arrange() which already did verification + + //elements that were created but never invalidated/measured are clean + //from layout perspective, but we still don't want to render them + //because they don't have state build up enough for that. + if ((_flags & (Flags.NeverMeasured | Flags.NeverArranged)) != 0) + return false; + + //if UIElement is collapsed, no rendering is needed + //it is not only perf optimization, but also protection from + //UIElement to break itself since RenderSize is reported as (0,0) + //when UIElement is Collapsed + // + if (_visibility == Visibility.Collapsed || _visibility == Visibility.Hidden) + return false; + + return (_flags & (Flags.InvalidArrange | Flags.InvalidMeasure)) == 0; + } + + /// + /// Invalidates the measurement state for the UIElement. + /// This has the effect of also invalidating the arrange state for the UIElement. + /// The UIElement will be queued for an update layout that will occur asynchronously. + /// + public void InvalidateMeasure() + { + VerifyAccess(); + + Flags flags = _flags; + + if ((flags & (Flags.InvalidMeasure | Flags.MeasureInProgress)) == 0) + { + _flags |= Flags.InvalidMeasure; + + if ((flags & Flags.NeverMeasured) == 0) + { + LayoutManager.CurrentLayoutManager.MeasureQueue.Add(this); + } + } + } + + /// + /// Invalidates the arrange state for the UIElement. + /// The UIElement will be queued for an update layout that will occur asynchronously. + /// MeasureOverride will not be called unless InvalidateMeasure is also called - or that something + /// else caused the measure state to be invalidated. + /// + public void InvalidateArrange() + { + VerifyAccess(); + + Flags flags = _flags; + + if ((flags & (Flags.InvalidArrange | Flags.ArrangeInProgress)) == 0) + { + _flags |= Flags.InvalidArrange; + + if ((flags & Flags.NeverArranged) == 0) + { + LayoutManager.CurrentLayoutManager.ArrangeQueue.Add(this); + } + } + } + + /// + /// + /// + public UIElement Parent + { + get + { + return _parent; + } + } + + /// + /// + /// + public UIElement RootUIElement + { + get + { + // we use two pointers to atomically check / iterate + // through parents + UIElement p = null; + UIElement pp = this; + do + { + p = pp; + pp = p._parent; + } while (pp != null); + + return p; + } + } + + /// + /// The CompositionTarget marks the root element. The root element is responsible + /// for posting renders. This method is also used to ensure that the Visual is not + /// used in multiple CompositionTargets. + /// + internal bool GetIsRootElement() + { + return ((_flags & Flags.ShouldPostRender) != 0); + } + + /// + /// + /// + public HorizontalAlignment HorizontalAlignment + { + get + { + return _horizontalAlignment; + } + + set + { + VerifyAccess(); + + _horizontalAlignment = value; + InvalidateArrange(); + } + } + + /// + /// + /// + public VerticalAlignment VerticalAlignment + { + get + { + return _verticalAlignment; + } + + set + { + VerifyAccess(); + + _verticalAlignment = value; + InvalidateArrange(); + } + } + + /// + /// Notification that is called by Measure of a child when + /// it ends up with different desired size for the child. + /// + /// + /// Default implementation simply calls invalidateMeasure(), assuming that layout of a + /// parent should be updated after child changed its size. + /// Finer point: this method can only be called in the scenario when the system calls Measure on a child, + /// not when parent calls it since if parent calls it, it means parent has dirty layout and is recalculating already. + /// + protected virtual void OnChildDesiredSizeChanged(UIElement child) + { + if (IsMeasureValid) + { + InvalidateMeasure(); + } + } + + /// + /// + /// + /// + public virtual void OnRender(DrawingContext dc) + { + } + + /// + /// Visibility accessor + /// + public Visibility Visibility + { + get + { + return _visibility; + } + + set + { + VerifyAccess(); + + if (_visibility != value) + { + bool wasVisible = (_flags & Flags.IsVisibleCache) != 0; + + bool invalidateMeasure = (_visibility == Visibility.Collapsed || value == Visibility.Collapsed); + + _visibility = value; + + bool isVisible = false; + + bool parentVisible = (_parent == null) ? false : (_parent._flags & Flags.IsVisibleCache) != 0; + if (parentVisible && value == Visibility.Visible) + { + _flags = _flags | Flags.IsVisibleCache; + isVisible = true; + } + else + { + _flags = _flags & ~Flags.IsVisibleCache; + } + + if (invalidateMeasure && _parent != null) + { + _parent.InvalidateMeasure(); + } + + if (wasVisible != isVisible) + { + OnIsVisibleChanged(wasVisible); + } + } + } + } + + private void OnIsVisibleChanged(bool wasVisible) + { + //Desktop WPF sends the event to the parent first. + if (_isVisibleChanged != null) + { + _isVisibleChanged(this, new PropertyChangedEventArgs("IsVisible", wasVisible, !wasVisible)); + } + + // Loop through all children and fire events for only those that need it. + UIElementCollection children = _logicalChildren; + if (children != null) + { + int n = children.Count; + for (int i = 0; i < n; i++) + { + UIElement child = children[i]; + + if (child._visibility == Visibility.Visible) + { + /* The IsVisbile property on a child can only change if it wants to be visible. + * If the parent is transitioning from visible to not, it only affects visible children. + * If the parent is transitioning from invisible to visible, it only affects children who want to be visible. + */ + if (!wasVisible) + { + child._flags = child._flags | Flags.IsVisibleCache; + } + else + { + child._flags = child._flags & ~Flags.IsVisibleCache; + } + + child.OnIsVisibleChanged(wasVisible); + } + } + } + } + + /// + /// A property indicating if this element is Visible or not. + /// + public bool IsVisible + { + get + { + return (_flags & Flags.IsVisibleCache) != 0; + } + } + + /// + /// + /// + public event PropertyChangedEventHandler IsVisibleChanged + { + add + { + VerifyAccess(); + + _isVisibleChanged += value; + } + + remove + { + VerifyAccess(); + + _isVisibleChanged -= value; + } + } + + /// + /// Fetches the value of the IsEnabled property + /// + public bool IsEnabled + { + get + { + // REFACTOR + // Implement this similar to IsVisibleChanged and fire + // IsEnabledChanged events on child elements? + + // If our parent is not enabled, then we cannot be either. + UIElement parent = _parent; + + bool isEnabled = parent == null ? true : parent.IsEnabled; + + if (isEnabled) + { + // If our parent was enabled, then we may or may not be. + isEnabled = ((_flags & Flags.Enabled) != 0); + } + + return isEnabled; + } + + set + { + VerifyAccess(); + + bool wasEnabled = IsEnabled; + + if (value) + { + _flags |= Flags.Enabled; + } + else + { + _flags &= ~Flags.Enabled; + } + + if (_isEnabledChanged != null && wasEnabled != IsEnabled) + { + _isEnabledChanged(this, new PropertyChangedEventArgs("IsEnabled", wasEnabled, !wasEnabled)); + } + } + } + + /// + /// + /// + public event PropertyChangedEventHandler IsEnabledChanged + { + add + { + VerifyAccess(); + + _isEnabledChanged += value; + } + + remove + { + VerifyAccess(); + + _isEnabledChanged -= value; + } + } + + /// + /// + /// + /// + protected internal virtual void RenderRecursive(DrawingContext dc) + { + //Debug.Assert(this.IsMeasureValid && this.IsArrangeValid); + + dc.Translate(_offsetX, _offsetY); + dc.PushClippingRectangle(0, 0, _renderWidth, _renderHeight); + try + { + // Debug.Assert(this.Visibility == Visibility.Visible); + + if (!dc.EmptyClipRect) + { + OnRender(dc); + UIElementCollection children = _logicalChildren; + if (children != null) + { + int n = children.Count; + for (int i = 0; i < n; i++) + { + UIElement child = children[i]; + if (child.IsRenderable()) + { + child.RenderRecursive(dc); + } + } + } + } + } + finally + { + dc.PopClippingRectangle(); + dc.Translate(-_offsetX, -_offsetY); + + //------------------------------------------------------------------------------- + // Reset the render flags. + + _flags &= ~(Flags.IsSubtreeDirtyForRender | Flags.IsDirtyForRender); + } + } + + internal static void PropagateFlags(UIElement e, Flags flags) + { + while ((e != null) && ((e._flags & flags) == 0)) + { + e._flags |= flags; + + if ((e._flags & Flags.ShouldPostRender) != 0) + { + MediaContext.From(e.Dispatcher).PostRender(); + } + + e = e._parent; + } + } + + private void MarkDirtyRect(int x, int y, int w, int h) + { + PointToScreen(ref x, ref y); + MediaContext.From(Dispatcher).AddDirtyArea(x, y, w, h); + + PropagateFlags( + this, + Flags.IsSubtreeDirtyForRender); + } + + /// + /// + /// + /// + /// + /// + /// + public void InvalidateRect(int x, int y, int w, int h) + { + VerifyAccess(); + + MarkDirtyRect(x, y, w, h); + } + + /// + /// + /// + public void Invalidate() + { + VerifyAccess(); + + MarkDirtyRect(0, 0, _renderWidth, _renderHeight); + } + + #region Eventing + + /// + /// Raise the events specified by + /// + /// + /// + /// This method is a shorthand for + /// This method walks up the visual tree, calling + /// cref="UIElement.BuildRouteCore" + /// on every cref="UIElement" + /// + /// NOTE: The RoutedEvent in RoutedEventArgs + /// and EventRoute must be matched + /// + /// Once the route is built, it calls InvokeHandlers() + /// + /// + /// for the event to + /// be raised + /// + public void RaiseEvent(RoutedEventArgs args) + { + // Verify Context Access + VerifyAccess(); + + if (args == null) + { + throw new ArgumentNullException(); + } + + EventRoute route = new EventRoute(args._routedEvent); + + // Set Source + args.Source = this; + + // direct. + if (args._routedEvent._routingStrategy == RoutingStrategy.Direct) + { + this.AddToEventRouteImpl(route, args); + } + else + { + int cElements = 0; + + UIElement uiElement = this; + + do + { + // Protect against infinite loops by limiting the number of elements + // that we will process. + if (cElements++ > MAX_ELEMENTS_IN_ROUTE) + { + throw new InvalidOperationException(/*SR.Get(SRID.TreeLoop) */); + } + + uiElement.AddToEventRouteImpl(route, args); + + uiElement = uiElement._parent; + + } while (uiElement != null); + } + + route.InvokeHandlers(this, args); + + // Reset Source to OriginalSource + args.Source = args.OriginalSource; + } + + /// + /// Add the event handlers for this element to the route. + /// + /// + /// + // REFACTOR -- do we need this to be public? + public void AddToEventRoute(EventRoute route, RoutedEventArgs args) + { + VerifyAccess(); + + if (route == null || args == null) + { + throw new ArgumentNullException(); + } + + AddToEventRouteImpl(route, args); + } + + private void AddToEventRouteImpl(EventRoute route, RoutedEventArgs args) + { + // + // add class listeners then instance listeners. + // + Hashtable store = _classEventHandlersStore; + RoutedEvent evt = args._routedEvent; + for (int repeat = 0; repeat < 2; repeat++) + { + if (store != null) + { + ArrayList eventListeners = (ArrayList)store[evt]; + + // Add all listeners for this UIElement + if (eventListeners != null) + { + for (int i = 0, count = eventListeners.Count; i < count; i++) + { + RoutedEventHandlerInfo eventListener = (RoutedEventHandlerInfo)eventListeners[i]; + route.Add(this, eventListener._handler, eventListener._handledEventsToo); + } + } + } + + store = _instanceEventHandlersStore; + } + } + + #endregion + + #region Instance Handlers + + /// + /// Ensure the store has been created. + /// + protected Hashtable InstanceEventHandlersStore + { + get + { + if (_instanceEventHandlersStore == null) + { + _instanceEventHandlersStore = new Hashtable(); + } + + return _instanceEventHandlersStore; + } + } + + /// + /// Adds a routed event handler for the particular + /// + /// + /// + /// The handler added thus is also known as + /// an instance handler + /// + /// + /// NOTE: It is not an error to add a handler twice + /// (handler will simply be called twice) + /// + /// + /// Input parameters + /// and handler cannot be null + /// handledEventsToo input parameter when false means + /// that listener does not care about already handled events. + /// Hence the handler will not be invoked on the target if + /// the RoutedEvent has already been + /// + /// handledEventsToo input parameter when true means + /// that the listener wants to hear about all events even if + /// they have already been handled. Hence the handler will + /// be invoked irrespective of the event being + /// + /// + /// + /// for which the handler + /// is attached + /// + /// + /// The handler that will be invoked on this object + /// when the RoutedEvent is raised + /// + /// + /// Flag indicating whether or not the listener wants to + /// hear about events that have already been handled + /// + public void AddHandler( + RoutedEvent routedEvent, + RoutedEventHandler handler, + bool handledEventsToo) + { + // Verify Context Access + this.VerifyAccess(); + + if (routedEvent == null || handler == null) + { + throw new ArgumentNullException(); + } + + AddRoutedEventHandler(InstanceEventHandlersStore, routedEvent, handler, handledEventsToo); + } + + #endregion + +#if NANOCLR_TRACE_DEBUG_LAYOUT + public static string PrintRect(int x, int y, int width, int height) + { + return "[" + x + ", " + y + ", " + width + ", " + height + "]"; + } + + public static string PrintSize(int x, int y) + { + return "[" + x + ", " + y + "]"; + } + +#endif + + internal const int MAX_ELEMENTS_IN_ROUTE = 256; + + [Flags] + internal enum Flags : uint + { + None = 0x00000000, + // IsSubtreeDirtyForRender indicates that at least one element in the sub-graph of this element needs to + // be re-rendered. + IsSubtreeDirtyForRender = 0x00000002, + // IsDirtyForRender indicates that the element has changed in a way that all it's children + // need to be updated. E.g. more/less children clipped, children themselves + // changed, clip changed => more/less children clipped + // + IsDirtyForRender = 0x00000004, + + Enabled = 0x00000020, + InvalidMeasure = 0x00000040, + InvalidArrange = 0x00000080, + MeasureInProgress = 0x00000100, + ArrangeInProgress = 0x00000200, + MeasureDuringArrange = 0x00000400, + NeverMeasured = 0x00000800, + NeverArranged = 0x00001000, + // Should post render indicates that this is a root element and therefore we need to indicate that this + // element tree needs to be re-rendered. Today we are doing this by posting a render queue item. + ShouldPostRender = 0x00002000, + IsLayoutSuspended = 0x00004000, + + IsVisibleCache = 0x00008000, + } + + //--// + + internal UIElement _parent; + internal UIElementCollection _logicalChildren; + + // + internal Flags _flags; + private Visibility _visibility; + + // Layout + // + internal class Pair + { + /// + /// + /// + public const int Flags_First = 0x1; // Can be (optionally) used with _status + + /// + /// + /// + public const int Flags_Second = 0x2; + + /// + /// + /// + public int _first; + + /// + /// + /// + public int _second; + + /// + /// + /// + public int _status; + } + + internal Pair _requestedSize; // Used when Width/Height properties are set + internal Pair _anchorInfo; // Used if the parent is a Canvas + + private int _marginLeft; + private int _marginTop; + private int _marginRight; + private int _marginBottom; + + /// + /// + /// + protected HorizontalAlignment _horizontalAlignment; + + /// + /// + /// + protected VerticalAlignment _verticalAlignment; + + // Cached layout information + // + internal int _finalX; + internal int _finalY; + internal int _finalWidth; + internal int _finalHeight; + + internal int _offsetX; + internal int _offsetY; + internal int _renderWidth; + internal int _renderHeight; + + internal int _previousAvailableWidth; + internal int _previousAvailableHeight; + + private int _desiredWidth; + private int _desiredHeight; + + internal int _unclippedWidth; + internal int _unclippedHeight; + + // Routed Event Handling + + private static Hashtable _classEventHandlersStore; + private Hashtable _instanceEventHandlersStore; + + // Regular Events + PropertyChangedEventHandler _isEnabledChanged; + PropertyChangedEventHandler _isVisibleChanged; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElementCollection.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElementCollection.cs new file mode 100644 index 0000000..405fdca --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/UIElementCollection.cs @@ -0,0 +1,734 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using nanoFramework.UI; + +namespace nanoFramework.Presentation +{ + /// + /// A UIElementCollection is a ordered collection of UIElements. + /// + /// + /// A UIElementCollection has implied context affinity. It is a violation to access + /// the collection from a different context than that of the owning Element + /// + /// This collection is an amalgam of UIElementCollection and UIElementCollection from Avalon + /// + /// + public class UIElementCollection : ICollection + { + + /// + public UIElementCollection(UIElement owner) + { + //Debug.Assert(owner != null); + _owner = owner; + } + + /// + public virtual int Count + { + get + { + return _size; + } + } + + /// + public virtual bool IsSynchronized + { + get + { + return false; + } + } + + /// + public virtual object SyncRoot + { + get + { + return this; + } + } + + /// + /// Copies the UIElement collection to the specified array starting at the specified index. + /// + public void CopyTo(Array array, int index) + { + if (array == null) + { + throw new ArgumentNullException("array"); + } + + if ((index < 0) || + (array.Length - index < _size)) + { + throw new ArgumentOutOfRangeException("index"); + } + + Array.Copy(_items, 0, array, index, _size); + } + + /// + /// Strongly typed version of CopyTo + /// Copies the collection into the Array. + /// + public virtual void CopyTo(UIElement[] array, int index) + { + CopyTo((Array)array, index); + } + + // ---------------------------------------------------------------- + // ArrayList like operations for the UIElementCollection + // ---------------------------------------------------------------- + + /// + /// Ensures that the capacity of this list is at least the given minimum + /// value. If the currect capacity of the list is less than min, the + /// capacity is increased to min. + /// + private void EnsureCapacity(int min) + { + if (Capacity < min) + { + Capacity = Mathematics.Max(min, (int)(Capacity * c_growFactor)); + } + } + + /// + /// Gets or sets the number of elements that the UIElementCollection can contain. + /// + /// + /// The number of elements that the UIElementCollection can contain. + /// + /// + /// Capacity is the number of elements that the UIElementCollection is capable of storing. + /// Count is the number of UIElements that are actually in the UIElementCollection. + /// + /// Capacity is always greater than or equal to Count. If Count exceeds + /// Capacity while adding elements, the capacity of the UIElementCollection is increased. + /// + /// By default the capacity is 0. + /// + /// Capacity is set to a value that is less than Count. + public virtual int Capacity + { + get + { + return _items != null ? _items.Length : 0; + } + + set + { + int currentCapacity = _items != null ? _items.Length : 0; + if (value != currentCapacity) + { + if (value < _size) + { + throw new ArgumentOutOfRangeException("value", "not enough capacity"); + } + + if (value > 0) + { + UIElement[] newItems = new UIElement[value]; + if (_size > 0) + { + //Debug.Assert(_items != null); + Array.Copy(_items, 0, newItems, 0, _size); + } + + _items = newItems; + } + else + { + //Debug.Assert(value == 0, "There shouldn't be a case where value != 0."); + //Debug.Assert(_size == 0, "Size must be 0 here."); + _items = null; + } + } + } + } + + /// + /// Indexer for the UIElementCollection. Gets or sets the UIElement stored at the + /// zero-based index of the UIElementCollection. + /// + /// This property provides the ability to access a specific UIElement in the + /// UIElementCollection by using the following systax: myUIElementCollection[index]. + /// index is less than zero -or- index is equal to or greater than Count. + /// If the new child has already a parent or if the slot a the specified index is not null. + public UIElement this[int index] + { + get + { + if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException("index"); + return _items[index]; + } + + set + { + _owner.VerifyAccess(); + + if (value == null) + { + throw new ArgumentNullException("value"); + } + + if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException("index"); + + UIElement child = _items[index]; + + if (child != value) + { + if ((value == null) && (child != null)) + { + DisconnectChild(index); + } + else if (value != null) + { + if ((value._parent != null) || // Only a visual that isn't a visual parent or + (value.GetIsRootElement())) // are a root node of a visual target can be set into the collection. + { + throw new System.ArgumentException("element has parent"); + } + + ConnectChild(index, value); + } + + _owner.InvalidateMeasure(); + } + } + } + + /// + /// Sets the specified element at the specified index into the child + /// collection. It also corrects the parent. + /// Note that the function requires that _item[index] == null and it + /// also requires that the passed in child is not connected to another UIElement. + /// + /// If the new child has already a parent or if the slot a the specified index is not null. + private void ConnectChild(int index, UIElement value) + { + // Every function that calls this function needs to call VerifyAccess to prevent + // foreign threads from changing the tree. + // + // We also need to ensure that the tree is homogenous with respect + // to the dispatchers that the elements belong to. + // + value.VerifyAccess(); + + //Debug.Assert(_items[index] == null); + + value._parent = _owner; + + // The child might be dirty. Hence we need to propagate dirty information + // from the parent and from the child. + UIElement.PropagateFlags(_owner, UIElement.Flags.IsSubtreeDirtyForRender); + UIElement.PropagateFlags(value, UIElement.Flags.IsSubtreeDirtyForRender); + value._flags |= UIElement.Flags.IsDirtyForRender; + _items[index] = value; + _version++; + + UIElement.PropagateResumeLayout(value); + + // Fire notifications + _owner.OnChildrenChanged(value, null /* no removed child */, index); + } + + /// + /// Disconnects a child. + /// + private void DisconnectChild(int index) + { + //Debug.Assert(_items[index] != null); + + UIElement child = _items[index]; + + // Every function that calls this function needs to call VerifyAccess to prevent + // foreign threads from changing the tree. + + UIElement oldParent = child._parent; + + _items[index] = null; + + child._parent = null; + UIElement.PropagateFlags(_owner, UIElement.Flags.IsSubtreeDirtyForRender); + _version++; + + UIElement.PropagateSuspendLayout(child); + + oldParent.OnChildrenChanged(null /* no child added */, child, index); + } + + /// + /// Appends a UIElement to the end of the UIElementCollection. + /// + /// + /// The UIElementCollection index at which the UIElement has been added. + /// Adding a null is allowed. + /// + /// + /// If the new child has already a parent. + public int Add(UIElement element) + { + _owner.VerifyAccess(); + + if (element == null) + { + throw new ArgumentNullException("element"); + } + + if (((element._parent != null) || // Only visuals that are not connected to another tree + (element.GetIsRootElement()))) // or a visual target can be added. + { + throw new System.ArgumentException("element has parent"); + } + + if ((_items == null) || (_size == _items.Length)) + { + EnsureCapacity(_size + 1); + } + + int addedPosition = _size++; + //Debug.Assert(_items[addedPosition] == null); + + ConnectChild(addedPosition, element); + _version++; + + // invalidate measure on parent + _owner.InvalidateMeasure(); + + return addedPosition; + } + + /// + /// Returns the zero-based index of the UIElement. If the UIElement is not + /// in the UIElementCollection -1 is returned. If null is passed to the method, the index + /// of the first entry with null is returned. If there is no null entry -1 is returned. + /// + /// + /// + public int IndexOf(UIElement element) + { + if (element == null || element._parent == _owner) + { + for (int i = 0; i < _size; i++) + { + if (_items[i] == element) + { + return i; + } + } + + // not found, return -1 + return -1; + } + else + { + return -1; + } + } + + /// + /// Removes the specified element from the UIElementCollection. + /// + /// The UIElement to remove from the UIElementCollection. + /// + /// The UIElements that follow the removed UIElements move up to occupy + /// the vacated spot. The indexes of the UIElements that are moved are + /// also updated. + /// + /// If element is null then the first null entry is removed. Note that removing + /// a null entry is linear in the size of the collection. + /// + public void Remove(UIElement element) + { + int indexToRemove = -1; + + _owner.VerifyAccess(); + + if (element != null) + { + if (element._parent != _owner) + { + // If the UIElement is not in this collection we silently return without + // failing. This is the same behavior that ArrayList implements. + return; + } + + //Debug.Assert(element._parent != null); + + DisconnectChild(indexToRemove = IndexOf(element)); + } + else + { + // This is the case where element == null. We then remove the first null + // entry. + for (int i = 0; i < _size; i++) + { + if (_items[i] == null) + { + indexToRemove = i; + break; + } + } + } + + if (indexToRemove != -1) + { + --_size; + + for (int i = indexToRemove; i < _size; i++) + { + UIElement child = _items[i + 1]; + _items[i] = child; + } + + _items[_size] = null; + } + + _owner.InvalidateMeasure(); + } + + /// + /// Determines whether a element is in the UIElementCollection. + /// + /// + /// + public bool Contains(UIElement element) + { + if (element == null) + { + for (int i = 0; i < _size; i++) + { + if (_items[i] == null) + { + return true; + } + } + + return false; + } + else + { + return (element._parent == _owner); + } + } + + /// + /// Removes all elements from the UIElementCollection. + /// + /// + /// Count is set to zero. Capacity remains unchanged. + /// To reset the capacity of the UIElementCollection, call TrimToSize + /// or set the Capacity property directly. + /// + public void Clear() + { + _owner.VerifyAccess(); + + for (int i = 0; i < _size; i++) + { + if (_items[i] != null) + { + //Debug.Assert(_items[i]._parent == _owner); + DisconnectChild(i); + } + + _items[i] = null; + } + + _size = 0; + _version++; + + _owner.InvalidateMeasure(); + } + + /// + /// Inserts an element into the UIElementCollection at the specified index. + /// + /// The zero-based index at which value should be inserted. + /// The UIElement to insert. + /// + /// index is less than zero. + /// + /// -or- + /// + /// index is greater than Count. + /// + /// + /// If Count already equals Capacity, the capacity of the + /// UIElementCollection is increased before the new UIElement + /// is inserted. + /// + /// If index is equal to Count, value is added to the + /// end of UIElementCollection. + /// + /// The UIElements that follow the insertion point move down to + /// accommodate the new UIElement. The indexes of the UIElements that are + /// moved are also updated. + /// + public void Insert(int index, UIElement element) + { + _owner.VerifyAccess(); + + if (index < 0 || index > _size) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (element == null) + { + throw new ArgumentNullException("element"); + } + + if (((element._parent != null) || // Only elements that are not connected to another tree + (element.GetIsRootElement()))) // or a element target can be added. + { + throw new System.ArgumentException("element has parent"); + } + + if ((_items == null) || (_size == _items.Length)) + { + EnsureCapacity(_size + 1); + } + + for (int i = _size - 1; i >= index; i--) + { + UIElement child = _items[i]; + _items[i + 1] = child; + } + + _items[index] = null; + + _size++; + ConnectChild(index, element); + // Note SetUIElement that increments the version to ensure proper enumerator + // functionality. + _owner.InvalidateMeasure(); + } + + /// + /// Removes the UIElement at the specified index. + /// + /// The zero-based index of the element to remove. + /// index is less than zero + /// - or - index is equal or greater than count. + /// + /// The UIElements that follow the removed UIElements move up to occupy + /// the vacated spot. The indexes of the UIElements that are moved are + /// also updated. + /// + public void RemoveAt(int index) + { + if (index < 0 || index >= _size) + { + throw new ArgumentOutOfRangeException("index"); + } + + Remove(_items[index]); + } + + /// + /// Removes a range of UIElements from the UIElementCollection. + /// + /// The zero-based index of the range + /// of elements to remove + /// The number of elements to remove. + /// + /// index is less than zero. + /// -or- + /// count is less than zero. + /// + /// + /// index and count do not denote a valid range of elements in the UIElementCollection. + /// + /// + /// The UIElements that follow the removed UIElements move up to occupy + /// the vacated spot. The indexes of the UIElements that are moved are + /// also updated. + /// + public void RemoveRange(int index, int count) + { + _owner.VerifyAccess(); + + if (index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException("count"); + } + + if (_size - index < count) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (count > 0) + { + for (int i = index; i < index + count; i++) + { + if (_items[i] != null) + { + DisconnectChild(i); + _items[i] = null; + } + } + + _size -= count; + for (int i = index; i < _size; i++) + { + UIElement child = _items[i + count]; + _items[i] = child; + _items[i + count] = null; + } + + _version++; // Incrementing version number here to be consistent with the ArrayList + // implementation. + } + } + + // ---------------------------------------------------------------- + // IEnumerable Interface + // ---------------------------------------------------------------- + + /// + /// Returns an enumerator that can iterate through the UIElementCollection. + /// + /// Enumerator that enumerates the UIElementCollection in order. + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + /// + /// This is a simple UIElementCollection enumerator that is based on + /// the ArrayListEnumeratorSimple that is used for ArrayLists. + /// + /// The following comment is from the CLR people: + /// For a straightforward enumeration of the entire ArrayList, + /// this is faster, because it's smaller. Benchmarks showed + /// this. + /// + public struct Enumerator : IEnumerator, ICloneable + { + private UIElementCollection _collection; + private int _index; // -1 means not started. -2 means that it reached the end. + private int _version; + private UIElement _currentElement; + + internal Enumerator(UIElementCollection collection) + { + _collection = collection; + _index = -1; // not started. + _version = _collection._version; + _currentElement = null; + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + public Object Clone() + { + return MemberwiseClone(); + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + public bool MoveNext() + { + if (_version == _collection._version) + { + if ((_index > -2) && (_index < (_collection._size - 1))) + { + _index++; + _currentElement = _collection[_index]; + return true; + } + else + { + _currentElement = null; + _index = -2; // -2 <=> reached the end. + return false; + } + } + else + { + throw new InvalidOperationException("collection changed"); + } + } + + /// + /// Gets the current UIElement. + /// + object IEnumerator.Current + { + get + { + return this.Current; + } + } + + /// + /// Gets the current UIElement. + /// + public UIElement Current + { + get + { + // Disable PREsharp warning about throwing exceptions in property + // get methods; see Windows OS Bugs #1035349 for an explanation. + + if (_index < 0) + { + if (_index == -1) + { + // Not started. + throw new InvalidOperationException("not started"); + } + else + { + // Reached the end. + //Debug.Assert(_index == -2); + throw new InvalidOperationException("reached end"); + } + } + + return _currentElement; + } + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + public void Reset() + { + if (_version != _collection._version) + throw new InvalidOperationException("collection changed"); + _index = -1; // not started. + } + } + + internal UIElement[] _items; + internal int _size; + private int _version; + private UIElement _owner; + + private const int c_defaultCapacity = 2; + private const int c_growFactor = 2; + + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/VerticalAlignment.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/VerticalAlignment.cs new file mode 100644 index 0000000..a44fd9c --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/VerticalAlignment.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation +{ + /// + /// + /// + public enum VerticalAlignment + { + + /// + /// + /// + Top, + + + /// + /// + /// + Center, + + /// + /// + /// + Bottom, + + /// + /// + /// + Stretch + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Visibility.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Visibility.cs new file mode 100644 index 0000000..d23ddb7 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Visibility.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Presentation +{ + /// + /// Visibility - Enum which describes 3 possible visibility options. + /// + public enum Visibility + { + /// + /// Normally visible. + /// + Visible = 0, + + /// + /// Occupies space in the layout, but is not visible (completely transparent). + /// + Hidden, + + /// + /// Not visible and does not occupy any space in layout, as if it doesn't exist. + /// + Collapsed + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/Window.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Window.cs new file mode 100644 index 0000000..dc3c765 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/Window.cs @@ -0,0 +1,314 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Controls; +using nanoFramework.Presentation.Media; +using nanoFramework.UI; +using System.Runtime.CompilerServices; +using nanoFramework.UI.Threading; + +namespace nanoFramework.Presentation +{ + /// + /// + /// + public class Window : ContentControl + { + //--------------------------------------------------- + // + // Constructors + // + //--------------------------------------------------- + #region Constructors + + /// + /// Constructs a window object + /// + /// + /// Automatic determination of current Dispatcher. Use alternative constructor + /// that accepts a Dispatcher for best performance. + /// REFACTOR -- consider specifying app default window sizes to cover Aux case for default window size. + /// + /// Initializes the Width/Height, Top/Left properties to use windows + /// default. Updates Application object properties if inside app. + public Window() + { + //There is only one WindowManager. All Windows currently are forced to be created + //and to live on the same thread. + _windowManager = WindowManager.EnsureInstance(); + + _background = new SolidColorBrush(Color.White); + // + // dependency property initialization. + // we don't have them, so we just update the properties on the base class, + // like normal *bleep* fearing developers. + // + // Visibility HAS to be set to Collapsed prior to adding this child to the + // window manager, otherwise the window manager sets the focus to this window + this.Visibility = Visibility.Collapsed; + + // register us with the window manager, like a good little boy + _windowManager.Children.Add(this); + + Application app = Application.Current; + + // check if within an app && on the same thread + if (app != null) + { + if (app.Dispatcher.Thread == Dispatcher.CurrentDispatcher.Thread) + { + // add to window collection + // use internal version since we want to update the underlying collection + app.WindowsInternal.Add(this); + if (app.MainWindow == null) + { + app.MainWindow = this; + } + } + else + { + app.NonAppWindowsInternal.Add(this); + } + } + } + + #endregion Constructors + + #region Public Methods + + /// + /// + /// + [MethodImplAttribute( MethodImplOptions.Synchronized )] + public void Close() + { + Application app = Application.Current; + if (app != null) + { + app.WindowsInternal.Remove(this); + app.NonAppWindowsInternal.Remove(this); + } + + if(_windowManager != null) + { + _windowManager.Children.Remove(this); + _windowManager = null; + } + } + + #endregion Public Methods + + #region Public Properties + + /// + /// Auto size Window to its content's size + /// + /// + /// 1. SizeToContent can be applied to Width Height independently + /// 2. After SizeToContent is set, setting Width/Height does not take affect if that + /// dimension is sizing to content. + /// + /// + /// Default value is SizeToContent.Manual + /// + public SizeToContent SizeToContent + { + get + { + return _sizeToContent; + } + + set + { + VerifyAccess(); + _sizeToContent = value; + } + } + + /// + /// Position for Top of the host window + /// + /// + public int Top + { + get + { + return Canvas.GetTop(this); + } + + set + { + VerifyAccess(); + Canvas.SetTop(this, value); + } + } + + /// + /// + /// + public int Left + { + get + { + return Canvas.GetLeft(this); + } + + set + { + VerifyAccess(); + + Canvas.SetLeft(this, value); + } + } + + /// + /// Determines if this window is always on the top. + /// + public bool Topmost + { + get + { + return _windowManager.IsTopMost(this); + } + + set + { + VerifyAccess(); + + _windowManager.SetTopMost(this); + } + } + + #endregion Public Properties + + //--------------------------------------------------- + // + // Public Events + // + //--------------------------------------------------- + #region Public Events + + #endregion Public Events + + //--------------------------------------------------- + // + // Protected Methods + // + //--------------------------------------------------- + #region Protected Methods + + // REFACTOR -- need to track if our parent changes. + + /// + /// Measurement override. Implements content sizing logic. + /// + /// + /// Deducts the frame size from the constraint and then passes it on + /// to it's child. Only supports one Visual child (just like control) + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + UIElementCollection children = this.LogicalChildren; + + if (children.Count > 0) + { + UIElement child = (UIElement)children[0]; + if (child != null) + { + // REFACTOR --we need to subtract the frame & chrome around the visual child. + child.Measure(availableWidth, availableHeight); + child.GetDesiredSize(out desiredWidth, out desiredHeight); + + return; + } + } + + desiredWidth = availableWidth; + desiredHeight = availableHeight; + } + + /// + /// ArrangeOverride allows for the customization of the positioning of children. + /// + /// + /// Deducts the frame size of the window from the constraint and then + /// arranges it's child. Supports only one child. + /// + protected override void ArrangeOverride(int arrangeWidth, int arrangeHeight) + { + UIElementCollection children = this.LogicalChildren; + + if (children.Count > 0) + { + UIElement child = children[0] as UIElement; + if (child != null) + { + child.Arrange(0, 0, arrangeWidth, arrangeHeight); + } + } + } + + #endregion Protected Methods + + //--------------------------------------------------- + // + // Internal Methods + // + //--------------------------------------------------- + #region Internal Methods + + #endregion Internal Methods + + //---------------------------------------------- + // + // Internal Properties + // + //---------------------------------------------- + #region Internal Properties + + #endregion Internal Properties + + //---------------------------------------------- + // + // Private Methods + // + //---------------------------------------------- + #region Private Methods + + // + // These are the callbacks used by the windowSource to notify the window + // about what the window manager wants it to do. + // + #region WindowManager internal methods + + #endregion WindowManager internal methods + + #endregion Private Methods + + //---------------------------------------------- + // + // Private Properties + // + //---------------------------------------------- + #region Private Properties + + #endregion Private Properties + + //---------------------------------------------- + // + // Private Fields + // + //---------------------------------------------- + #region Private Fields + + private SizeToContent _sizeToContent; + private WindowManager _windowManager; + + #endregion Private Fields + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/Presentation/WindowManager.cs b/source/nanoFramework.Graphics.Wpf/Core/Presentation/WindowManager.cs new file mode 100644 index 0000000..c521130 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/Presentation/WindowManager.cs @@ -0,0 +1,161 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation.Media; +using nanoFramework.UI.Input; +using nanoFramework.UI; + +namespace nanoFramework.Presentation +{ + /// + /// + /// + /// + public delegate void PostRenderEventHandler(DrawingContext dc); + + /// + /// + /// + public class WindowManager : Controls.Canvas + { + private WindowManager() + { + // + // initially measure and arrange ourselves. + // + Instance = this; + + // + // WindowManagers have no parents but they are Visible. + // + _flags = _flags | Flags.IsVisibleCache; + + Measure(Media.Constants.MaxExtent, Media.Constants.MaxExtent); + + int desiredWidth, desiredHeight; + + GetDesiredSize(out desiredWidth, out desiredHeight); + + Arrange(0, 0, desiredWidth, desiredHeight); + } + + internal static WindowManager EnsureInstance() + { + if (Instance == null) + { + WindowManager wm = new WindowManager(); + // implicitly the window manager is responsible for posting renders + wm._flags |= Flags.ShouldPostRender; + } + + return Instance; + } + + /// + /// + /// + /// + /// + /// + /// + protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight) + { + base.MeasureOverride(availableWidth, availableHeight, out desiredWidth, out desiredHeight); + desiredWidth = SystemMetrics.ScreenWidth; + desiredHeight = SystemMetrics.ScreenHeight; + } + + internal void SetTopMost(Window window) + { + UIElementCollection children = LogicalChildren; + + if (!IsTopMost(window)) + { + children.Remove(window); + children.Add(window); + } + } + + internal bool IsTopMost(Window window) + { + int index = LogicalChildren.IndexOf(window); + return (index >= 0 && index == LogicalChildren.Count - 1); + } + + /// + /// + /// + /// + /// + /// + protected internal override void OnChildrenChanged(UIElement added, UIElement removed, int indexAffected) + { + base.OnChildrenChanged(added, removed, indexAffected); + + UIElementCollection children = LogicalChildren; + int last = children.Count - 1; + + // something was added, and it's the topmost. Make sure it is visible before setting focus + if (added != null && indexAffected == last && Visibility.Visible == added.Visibility) + { + Buttons.Focus(added); + TouchCapture.Capture(added); + } + + // something was removed and it lost focus to us. + if (removed != null && this.IsFocused) + { + // we still have a window left, so make it focused. + if (last >= 0) + { + Buttons.Focus(children[last]); + TouchCapture.Capture(children[last]); + } + } + } + + /// + /// + /// + public static WindowManager Instance; + + + private PostRenderEventHandler _postRenderHandler; + + /// + /// + /// + public event PostRenderEventHandler PostRender + { + add + { + _postRenderHandler += value; + } + + remove + { + _postRenderHandler -= value; + } + } + + /// + /// + /// + /// + protected internal override void RenderRecursive(DrawingContext dc) + { + base.RenderRecursive(dc); + + if (_postRenderHandler != null) + { + _postRenderHandler(dc); + } + } + } + +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Application.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Application.cs new file mode 100644 index 0000000..314a1da --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Application.cs @@ -0,0 +1,1059 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.UI.Input; +using nanoFramework.Presentation; +using System; +using System.Runtime.CompilerServices; +using nanoFramework.Runtime.Events; +using nanoFramework.UI.Threading; +using nanoFramework.Touch; + +namespace nanoFramework.UI +{ + + /* + /// + + /// Delegate for the Exit event. + /// + public delegate void ExitEventHandler(Object sender, ExitEventArgs e); + + /// + /// Delegate for SessionEnding event + /// + public delegate void SessionEndingCancelEventHandler(Object sender, SessionEndingCancelEventArgs e); + +*/ + + #region Application Class + + /// + /// Application base class + /// + public class Application : DispatcherObject, IEventListener + { + + //------------------------------------------------------ + // + // Constructors + // + //------------------------------------------------------ + + #region Constructors + + /// + /// Initializes a new instance of the Application class. + /// + public Application() + { + /* TRACING + if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) + { + EventTrace.EventProvider.TraceEvent(EventTrace.APPGUID, MS.Utility.EventType.Info, + EventTrace.APPCTOR); + } + + */ + lock (typeof(GlobalLock)) + { + // set the default statics + // DO NOT move this from the begining of this constructor + if (_appCreatedInThisAppDomain == false) + { + //Debug.Assert(_appInstance == null, "_appInstance must be null here."); + _appInstance = this; + IsShuttingDown = false; + _appCreatedInThisAppDomain = true; + } + else + { + //lock will be released, so no worries about throwing an exception inside the lock + throw new InvalidOperationException("application is a singleton"); + } + } + + Dispatcher.SetFinalDispatcherExceptionHandler(new DispatcherExceptionEventHandler(DefaultContextExceptionHandler)); + + // + // post item to do startup work + // posting it here so that this is the first item in the queue. Devs + // could post items before calling run and then those will be serviced + // before if we don't post this one here. + // + // Also, doing startup (firing OnStartup etc.) once our dispatcher + // is run ensures that we run before any external code is run in the + // application's Dispatcher. + Dispatcher.BeginInvoke(new DispatcherOperationCallback(this.StartupCallback), null); + } + + private object StartupCallback(object unused) + { + // Event handler exception continuality: if exception occurs in Startup event handler, + // our state would not be corrupted because it is fired by posting the item in the queue. + // Please check Event handler exception continuality if the logic changes. + OnStartup(new EventArgs()); + + return null; + } + + #endregion Constructors + + //------------------------------------------------------ + // + // Public Methods + // + //------------------------------------------------------ + + #region Public Methods + + /// + /// Run is called to start an application. + /// + /// Typically a developer will do some setting of properties/attaching to events after instantiating an application object, + /// and then call Run() to start the application. + /// + /// + /// Once run has been called - an application's OnStartup override and Startup event is called + /// immediately afterwards. + /// + /// ExitCode of the application + public void Run() + { + /* TRACING + if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) + { + EventTrace.EventProvider.TraceEvent(EventTrace.APPGUID, MS.Utility.EventType.Info, + EventTrace.APPRUN); + } + + */ + this.Run(null); + } + + /// + /// Run is called to start an application. + /// + /// Typically a developer will do some setting of properties/attaching to events after instantiating an application object, + /// and then call Run() to start the application. + /// + /// + /// Once run has been called - an application's OnStartup override and Startup event is called + /// immediately afterwards. + /// + /// Window that will be added to the Windows property and made the MainWindow of the Applcation. + /// The passed Window must be created on the same thread as the Application object. Furthermore, this Window is + /// shown once the Application is run. + public void Run(Window window) + { + VerifyAccess(); + // In this case, we should throw an exception when Run is called for the second time. + // When app is shutdown, _appIsShutdown is set to true. If it is true here, then we + // throw an exception + if (_appIsShutdown == true) + { + throw new InvalidOperationException("cannot call Run multiple times"); + } + + //This is the only UI thread. Make sure the WindowManager is created on this thread. + WindowManager.EnsureInstance(); + + if (window != null) + { + if (window.CheckAccess() == false) + { + throw new ArgumentException("window must be on same dispatcher"); + } + + if (WindowsInternal.HasItem(window) == false) + { + WindowsInternal.Add(window); + } + + if (MainWindow == null) + { + MainWindow = window; + } + + if (window.Visibility != Visibility.Visible) + { + Dispatcher.BeginInvoke(new DispatcherOperationCallback(this.ShowWindow), window); + } + } + + /* + REFACTOR + + In Avalon at this point, the application hooks up a message loop + so that it can detect when the application is activated and + de-activated. + + In our case this communication will happen with the main application domain. + + We will need to pass it a stub that is an MBRO so that we can + get callbacks when various things that are important to the app happen. + + */ + + //Even if the subclass app cancels the event we still want to create and run the dispatcher + //so that when the app explicitly calls Shutdown, we have a dispatcher to service the posted + //Shutdown DispatcherOperationCallback + + // run the dispatcher - this method will not return + // until the dispatcher is killed + _ownDispatcherStarted = true; + Dispatcher.Run(); + } + + /// + /// Shutdown is called to programmatically shutdown an application. + /// + /// Once shutdown() is called, the application gets called with the + /// OnShutdown method to raise the Shutdown event. + /// + public void Shutdown() + { + VerifyAccess(); + + //Already called once?? + if (IsShuttingDown == true) + { + return; + } + + IsShuttingDown = true; + + Dispatcher.BeginInvoke(new DispatcherOperationCallback(ShutdownCallback), null); + } + + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.Synchronized)] + public void InitializeForEventSource() + { + if (_inputManager == null) + { + _inputManager = InputManager.CurrentInputManager; + + _inputProviderSite = + _inputManager.RegisterInputProvider(this); + + _reportInputMethod = + new DispatcherOperationCallback(delegate(object o) + { + InputReportArgs args = (InputReportArgs)o; + return _inputProviderSite.ReportInput(args.Device, args.Report); + }); + } + } + + /// + /// + /// + /// + /// + public bool OnEvent(BaseEvent ev) + { + InputReport ir = null; + InputDevice dev = null; + + // Process known events, otherwise forward as generic to MainWindow. + // + + TouchEvent touchEvent = ev as TouchEvent; + if (touchEvent != null) + { + nanoFramework.Presentation.UIElement targetWindow = TouchCapture.Captured; + + // + // Make sure the current event's coordinates are contained in the current + // stylus/touch window, if not then search for the appropriate window + // + if (targetWindow != null && touchEvent.Message == (byte)TouchMessages.Down) + { + int x = 0, y = 0, w, h; + int xSrc = touchEvent.Touches[0].X; + int ySrc = touchEvent.Touches[0].Y; + + targetWindow.PointToScreen(ref x, ref y); + targetWindow.GetRenderSize(out w, out h); + + if (!(x <= xSrc && xSrc <= (x + w) && + y <= ySrc && ySrc <= (y + h))) + { + // only look for different target window if the touch point is inside + // the system metrics, otherwise, it may be a touch calibration point + // which is translated in the application layer. + if(xSrc <= SystemMetrics.ScreenWidth && + ySrc <= SystemMetrics.ScreenHeight) + { + targetWindow = null; + } + } + } + + if (targetWindow == null) + { + //If we can enforce that the first event in the array is always the primary touch, we don't have to + //search. + targetWindow = WindowManager.Instance.GetPointerTarget(touchEvent.Touches[0].X, touchEvent.Touches[0].Y); + } + + if (targetWindow != null) + { + _inputManager.TouchDevice.SetTarget(targetWindow); + } + else + { + _inputManager.TouchDevice.SetTarget(MainWindow); + } + + ir = + new RawTouchInputReport( + null, + touchEvent.Time, + touchEvent.Message, + touchEvent.Touches + ); + + dev = _inputManager._touchDevice; + + } + else if (ev is GenericEvent) + { + GenericEventEx genericEvent = (GenericEventEx)ev; + + nanoFramework.Presentation.UIElement targetWindow = TouchCapture.Captured; + + if (targetWindow == null) + { + targetWindow = WindowManager.Instance.GetPointerTarget(genericEvent.X, genericEvent.Y); + } + + if (targetWindow != null) + { + _inputManager.GenericDevice.SetTarget(targetWindow); + } + else + { + _inputManager.GenericDevice.SetTarget(MainWindow); + } + + ir = new RawGenericInputReport( + null, + genericEvent + ); + + dev = _inputManager._genericDevice; + + } + else + { + // Unkown event. + } + + this.Dispatcher.BeginInvoke(_reportInputMethod, new InputReportArgs(dev, ir)); + + return true; + } + + #endregion Public Methods + + //------------------------------------------------------ + // + // Public Properties + // + //------------------------------------------------------ + + #region Public Properties + + /// + /// The Current property enables the developer to always get to the application in + /// AppDomain in which they are running. + /// + static public Application Current + { + get + { + lock (typeof(GlobalLock)) + { + return _appInstance; + } + } + } + + /// + /// The Windows property exposes a WindowCollection object, from which a developer + /// can iterate over all the windows that have been opened in the current application. + /// + // DO-NOT USE THIS PROPERY IF YOU MEAN TO MODIFY THE UNDERLYING COLLECTION. USE + // WindowsInternal PROPERTY FOR MODIFYING THE UNDERLYING DATASTRUCTURE. + public WindowCollection Windows + { + get + { + return WindowsInternal.Clone(); + } + } + + /// + /// The MainWindow property indicates the primary window of the application. + /// + /// + /// By default - MainWindow will be set to the first window opened in the application. + /// However the MainWindow may be set programmatically to indicate "this is my main window". + /// It is a recommended programming style to refer to MainWindow in code instead of Windows[0]. + /// + /// + public Window MainWindow + { + get + { + // We do not need VerifyAccess here, MainWindow property should be accessible + // from any thread, since it could be needed for non-UI related operations. + // This is also in-line with desktop CLR behavior, where you can access Form object + // itself from any thread, although many actionable items are blocked from + // non-UI ones. + return _mainWindow; + } + + set + { + VerifyAccess(); + + if (value != _mainWindow) + { + _mainWindow = value; + } + } + } + + /// + /// The ShutdownMode property is called to set the shutdown specific mode of + /// the application. Setting this property controls the way in which an application + /// will shutdown. + /// The three values for the ShutdownMode enum are : + /// OnLastWindowClose + /// OnMainWindowClose + /// OnExplicitShutdown + /// + /// OnLastWindowClose - this mode will shutdown the application when the + /// last window is closed, or an explicit call is made + /// to Application.Shutdown(). This is the default mode. + /// + /// OnMainWindowClose - this mode will shutdown the application when the main + /// window has been closed, or Application.Shutdown() is + /// called. Note that if the MainWindow property has not + /// been set - this mode is equivalent to OnExplicitOnly. + /// + /// OnExplicitShutdown- this mode will shutdown the application only when an + /// explicit call to OnShutdown() has been made. + /// + public ShutdownMode ShutdownMode + { + get + { + return _shutdownMode; + } + + set + { + VerifyAccess(); + if (!IsValidShutdownMode(value)) + { + throw new ArgumentOutOfRangeException("value", "enum"); + } + + if (IsShuttingDown == true || _appIsShutdown == true) + { + throw new InvalidOperationException(); + } + + _shutdownMode = value; + } + } + + #endregion Public Properties + + //------------------------------------------------------ + // + // Public Events + // + //------------------------------------------------------ + + #region Public Events + + /// + /// The Startup event is fired when an application is starting. + /// This event is raised by the OnStartup method. + /// + public event EventHandler Startup + { + add { VerifyAccess(); _startupEventHandler += value; } + remove { VerifyAccess(); _startupEventHandler -= value; } + } + + /// + /// The Exit event is fired when an application is shutting down. + /// This event is raised by the OnExit method. + /// + public event EventHandler Exit + { + add { VerifyAccess(); _exitEventHandler += value; } + remove { VerifyAccess(); _exitEventHandler -= value; } + } + +#if(FALSE) + /// + /// The activate event is fired when an applications window has been activated from + /// the OS ( alt-tab, or changing application from taskbar, or clicking on a winodw). + /// This event is raised by the OnActivate method. + /// + public event EventHandler Activate + { + add { VerifyAccess(); } + remove { VerifyAccess(); } + } + + /// + /// The deactivate event is fired when an applications window has been de-activated + /// from the OS ( alt-tab, or changing application from taskbar, or clicking away + /// from an applications window). This event is raised by the OnDeactivate method. + /// + public event EventHandler Deactivate + { + add { VerifyAccess(); } + remove { VerifyAccess(); } + } + + /// + /// The SessionEnding event is fired when windows is ending, either due to a shutdown, + /// or loggoff from the start menu ( or calling the ExitWindows function). The + /// ReasonSessionEnding enum on the SessionEndingEventArgs indicates whether the session + /// is ending in response to a shutdown of the OS, or if the user is logging off. + /// + /// + /// By setting cancel to true on the SessionEndingCancelEventArgs, the app can prevent + /// the user from logging off. Hence attempting to cancel this is a high trust + /// operation and this is enforced when the event is handled. No listener can cancel + /// this event if the app is partial trust. + /// + public event SessionEndingCancelEventHandler SessionEnding + { + add { VerifyAccess(); } + remove { VerifyAccess(); } + } + +#endif + + #endregion Public Events + + //------------------------------------------------------ + // + // Protected Methods + // + //------------------------------------------------------ + + #region Protected Methods + + /// + /// OnStartup is called to raise the Startup event. The developer will typically override this method + /// if they want to take action at startup time ( or they may choose to attach an event). + /// This method will be called once when the application begins, once that application's Run() method + /// has been called. + /// + /// + /// This method follows the .Net programming guideline of having a protected virtual method + /// that raises an event, to provide a convenience for developers that subclass the event. + /// If you override this method - you need to call Base.OnStartup(...) for the corresponding event + /// to be raised. + /// + /// The event args that will be passed to the Startup event + protected virtual void OnStartup(EventArgs e) + { + // VerifyAccess(); //we're only called via Invoke so this is unnecessary? + + if (_startupEventHandler != null) + { + _startupEventHandler(this, e); + } + } + + /// + /// OnExit is called to raise the Exit event. + /// The developer will typically override this method if they want to take + /// action when the application exits ( or they may choose to attach an event). + /// + /// + /// This method follows the .Net programming guideline of having a protected virtual method + /// that raises an event, to provide a convenience for developers that subclass the event. + /// If you override this method - you need to call Base.OnExit(...) for the + /// corresponding event to be raised. + /// + /// The event args that will be passed to the Exit event + protected virtual void OnExit(EventArgs e) + { + // VerifyAccess(); //- Only called via an Invoked frame, so unnecessary? + + if (_exitEventHandler != null) + { + _exitEventHandler(this, e); + } + } + +#if(false) + // REFACTOR + + /// + /// OnActivate is called to raise the Activate event. + /// The developer will typically override this method if they want to take action + /// when the application gets activated ( or they may choose to attach an event). + /// This method will be called when one of the current applications windows gets + /// activated on the desktop. ( This corresponds to Users WM_ACTIVATEAPP message). + /// + /// + /// This method follows the .Net programming guideline of having a protected + /// virtual method that raises an event, to provide a convenience for developers + /// that subclass the event. + /// + /// + protected virtual void OnActivate(EventArgs e) + { + VerifyAccess(); + + EventHandler handler = (EventHandler)Events[EVENT_ACTIVATE]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// OnDeactivate is called to raise the Deactivate event. The developer will + /// typically override this method if they want to take action when the application + /// gets deactivated ( or they may choose to attach an event). + /// This method will be called when one of the current applications windows gets + /// activated on the desktop. ( This corresponds to Users WM_ACTIVATEAPP message, + /// with an wparam indicating the app is being deactivated). + /// + /// + /// This method follows the .Net programming guideline of having a protected virtual + /// method that raises an event, to provide a convenience for developers that + /// subclass the event. + /// + /// + protected virtual void OnDeactivate(EventArgs e) + { + VerifyAccess(); + + EventHandler handler = (EventHandler)Events[EVENT_DEACTIVATE]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// OnSessionEnding is called to raise the SessionEnding event. The developer will + /// typically override this method if they want to take action when the OS is ending + /// a session ( or they may choose to attach an event). This method will be called when + /// the user has chosen to either logoff or shutdown. These events are equivalent + /// to receiving a WM_QUERYSESSION window event. Windows will send it when user is + /// logging out/shutting down. ( See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/wm_queryendsession.asp ). + /// By default if this event is not cancelled - Avalon will then call Application.Shutdown. + /// + /// + /// This method follows the .Net programming guideline of having a protected virtual + /// method that raises an event, to provide a convenience for developers that subclass + /// the event. + /// + /// + protected virtual void OnSessionEnding(SessionEndingCancelEventArgs e) + { + VerifyAccess(); + + SessionEndingCancelEventHandler handler = (SessionEndingCancelEventHandler)Events[EVENT_SESSIONENDING]; + if (handler != null) + { + handler(this, e); + } + } + +#endif + + #endregion Protected Methods + + //------------------------------------------------------ + // + // Internal Methods + // + //------------------------------------------------------ + + #region Internal Methods + + /// + /// DO NOT USE - internal method + /// + internal virtual void DoShutdown() + { + // VerifyAccess(); - is this really necessary when we're only called via an Invoke? + + // We need to know if we have been shut down already. + // We cannot check the IsShuttingDown variable because it is set true + // in the function that calls us. + + lock (typeof(GlobalLock)) + { + _appWindowList = null; + } + + EventArgs e = new EventArgs(); + + // Event handler exception continuality: if exception occurs in ShuttingDown event handler, + // our cleanup action is to finish Shuttingdown. Since Shuttingdown cannot be cancelled. + // We don't want user to use throw exception and catch it to cancel Shuttingdown. + try + { + // fire Applicaiton Exit event + OnExit(e); + } + finally + { + + lock (typeof(GlobalLock)) + { + _appInstance = null; + _nonAppWindowList = null; + } + + _mainWindow = null; + + // REFACTOR -- disconnect from managed system + + _appIsShutdown = true; // mark app as shutdown + } + } + + private object ShowWindow(object obj) + { + Window win = obj as Window; + win.Visibility = Visibility.Visible; + return null; + } + + #endregion Internal methods + + //------------------------------------------------------ + // + // Internal Properties + // + //------------------------------------------------------ + + #region Internal Properties + + // The public Windows property returns a copy of the underlying + // WindowCollection. This property is used internally to enable + // modyfying the underlying collection. + internal WindowCollection WindowsInternal + { + get + { + lock (typeof(GlobalLock)) + { + if (_appWindowList == null) + { + _appWindowList = new WindowCollection(); + } + + return _appWindowList; + } + } + } + + internal WindowCollection NonAppWindowsInternal + { + get + { + lock (typeof(GlobalLock)) + { + if (_nonAppWindowList == null) + { + _nonAppWindowList = new WindowCollection(); + } + + return _nonAppWindowList; + } + } + + } + + internal static bool IsShuttingDown + { + get + { + lock (typeof(GlobalLock)) + { + return _isShuttingDown; + } + } + + set + { + lock (typeof(GlobalLock)) + { + _isShuttingDown = value; + } + } + } + + #endregion Internal Properties + + //------------------------------------------------------ + // + // Private Methods + // + //------------------------------------------------------ + #region Private Methods + + /* + + /// + /// Creates hwndsource so that we can listen to some window msgs. + /// + private void EnsureHwndSource() + { + // We don't support Activate, Deactivate, and SessionEnding + // events for browser hosted scenarios thus don't create + // this HwndSource if BrowserCallbackServices is valid + if (BrowserCallbackServices == null && _parkingHwnd == null) + { + // We need these asserts b/c some PTT drts run as standalone + // apps under limited security permissions. + // + HwndSourceParameters param = new HwndSourceParameters("", 0 , 0); + param.HwndSourceHook = new HwndSourceHook(AppFilterMessage); + param.WindowStyle = 0; + param.ExtendedWindowStyle = 0; + _parkingHwnd = new HwndSource(param); + } + } + + private IntPtr AppFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + IntPtr retInt = IntPtr.Zero; + switch (msg) + { + case NativeMethods.WM_ACTIVATEAPP: + handled = WmActivateApp(NativeMethods.IntPtrToInt32(wParam)); + break; + case NativeMethods.WM_QUERYENDSESSION : + handled = WmQueryEndSession(lParam, ref retInt); + break; + default: + handled = false; + break; + } + + return retInt; + } + + private bool WmActivateApp(Int32 wParam) + { + int temp = wParam; + bool isActivated = (temp == 0? false : true); + + // Event handler exception continuality: if exception occurs in Activate/Deactivate event handlers, our state would not + // be corrupted because no internal state are affected by Activate/Deactivate. Please check Event handler exception continuality + // if a state depending on those events is added. + if (isActivated == true) + { + OnActivate( EventArgs.Empty ); + } + else + { + OnDeactivate( EventArgs.Empty ); + } + + return false; + } + + private bool WmQueryEndSession(IntPtr lParam, ref IntPtr refInt) + { + int reason = NativeMethods.IntPtrToInt32(lParam); + bool retVal = false; + + // Event handler exception continuality: if exception occurs in SessionEnding event handlers, our state would not + // be corrupted because no internal state are affected by SessionEnding. Please check Event handler exception continuality + // if a state depending on this event is added. + SessionEndingCancelEventArgs secEventArgs = new SessionEndingCancelEventArgs( (reason & NativeMethods.ENDSESSION_LOGOFF) != 0? ReasonSessionEnding.Logoff : ReasonSessionEnding.Shutdown ); + OnSessionEnding( secEventArgs ); + + // we need this check to decided whether we have handled the WM_QUERYENDSESSION or not + SessionEndingCancelEventHandler handler = (SessionEndingCancelEventHandler)Events[EVENT_SESSIONENDING]; + if (handler != null) + { + // we have handled the event DefWndProc will not be called for this msg + retVal = true; + + // shut down the app if not cancelled + if ( secEventArgs.Cancel == false ) + { + Shutdown(); + // return true to the wnd proc to signal that we can terminate properly + refInt = new IntPtr(1); + } + else + { + // + // This'll stop a user from Logging off and hence is a high trust operation. + // Demand high level of trust. + // + SecurityHelper.DemandUnmanagedCode(); + refInt = IntPtr.Zero; + } + } + + return retVal; + } + +*/ + + /// + /// This method gets called on dispatch of the Shutdown DispatcherOperationCallback + /// + + private object ShutdownCallback(object arg) + { + // Event handler exception continuality: if exception occurs in Exit event handler, + // our cleanup action is to finish Shutdown since Exit cannot be cancelled. We don't + // want user to use throw exception and catch it to cancel Shutdown. + try + { + DoShutdown(); + } + finally + { + // Quit the dispatcher if we ran our own. + if (_ownDispatcherStarted == true) + { + Dispatcher.InvokeShutdown(); + } + + } + + return null; + } + + /// + /// This DispatcherException event handler creates the default UI + /// + static private bool DefaultContextExceptionHandler(object sender, Exception e) + { + // what do we want to do when we get an exception? throw up a dialog? + + return true; + } + + private static bool IsValidShutdownMode(ShutdownMode value) + { + return value == ShutdownMode.OnExplicitShutdown + || value == ShutdownMode.OnLastWindowClose + || value == ShutdownMode.OnMainWindowClose; + } + + #endregion Private Methods + + //------------------------------------------------------ + // + // Private Fields + // + //------------------------------------------------------ + + #region Private Fields + private class GlobalLock { } + static private bool _isShuttingDown; + static private bool _appCreatedInThisAppDomain; + static private Application _appInstance; + + private WindowCollection _appWindowList; + private WindowCollection _nonAppWindowList; + private Window _mainWindow; + + private bool _ownDispatcherStarted; + private bool _appIsShutdown; + private ShutdownMode _shutdownMode = ShutdownMode.OnLastWindowClose; + + private EventHandler _startupEventHandler; + private EventHandler _exitEventHandler; + + private static DispatcherOperationCallback _reportInputMethod; + private static InputManager _inputManager = null; + private InputProviderSite _inputProviderSite = null; + + private static readonly int _stylusMaxX = SystemMetrics.ScreenWidth; + private static readonly int _stylusMaxY = SystemMetrics.ScreenHeight; + + /*REFACTOR private EventHandlerList _events; + + private static readonly object EVENT_STARTUP = new object(); + private static readonly object EVENT_EXIT = new object(); + private static readonly object EVENT_ACTIVATE = new object(); + private static readonly object EVENT_DEACTIVATE = new object(); + private static readonly object EVENT_SESSIONENDING = new object(); + */ + #endregion Private Fields + } + + #endregion Application Class + + #region enum ShutdownMode + + /// + /// Enum for ShutdownMode + /// + public enum ShutdownMode : byte + { + /// + /// + /// + OnLastWindowClose = 0, + + /// + /// + /// + OnMainWindowClose = 1, + + /// + /// + /// + OnExplicitShutdown + + // NOTE: if you add or remove any values in this enum, be sure to update Application.IsValidShutdownMode() + } + + #endregion enum ShutdownMode + + #region enum ReasonSessionEnding + + /// + /// Enum for ReasonSessionEnding + /// + public enum ReasonSessionEnding : byte + { + /// + /// + /// + Logoff = 0, + /// + /// + /// + Shutdown + } + + #endregion enum ReasonSessionEnding +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/EventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/System/EventHandler.cs new file mode 100644 index 0000000..75643c5 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/EventHandler.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + + +// need move this into mscorlib, also get the real implementation. +using System; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI +{ + /// + /// + /// + /// + /// + public delegate void EventHandler(object sender, EventArgs e); + + /// + /// + /// + /// + /// + public delegate void CancelEventHandler(object sender, CancelEventArgs e); + + /// + /// + /// + public class CancelEventArgs : EventArgs + { + /// + /// + /// + public bool Cancel; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/EventRoute.cs b/source/nanoFramework.Graphics.Wpf/Core/System/EventRoute.cs new file mode 100644 index 0000000..c58102d --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/EventRoute.cs @@ -0,0 +1,197 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; + +namespace nanoFramework.UI +{ + /// + /// Container for the route to be followed + /// by a RoutedEvent when raised + /// + /// + /// EventRoute constitues + /// a non-null + /// and + /// an ordered list of (target object, handler list) + /// pairs + /// + /// + /// It facilitates adding new entries to this list + /// and also allows for the handlers in the list + /// to be invoked + /// + public sealed class EventRoute + { + #region Construction + + /// + /// Constructor for given + /// the associated + /// + /// + /// Non-null to be associated with + /// this + /// + public EventRoute(RoutedEvent routedEvent) + { + if (routedEvent == null) + { + throw new ArgumentNullException("routedEvent"); + } + + RoutedEvent = routedEvent; + _routeItemList = new ArrayList(); + } + + #endregion Construction + + #region External API + + /// + /// Adds this handler for the + /// specified target to the route + /// + /// + /// NOTE: It is not an error to add a + /// handler for a particular target instance + /// twice (handler will simply be called twice). + /// + /// + /// Target object whose handler is to be + /// added to the route + /// + /// + /// Handler to be added to the route + /// + /// + /// Flag indicating whether or not the listener wants to + /// hear about events that have already been handled + /// + // need to consider creating an AddImpl, and consider if we even need to + // make this public? + public void Add(object target, RoutedEventHandler handler, bool handledEventsToo) + { + if (target == null) + { + throw new ArgumentNullException("target"); + } + + if (handler == null) + { + throw new ArgumentNullException("handler"); + } + + RouteItem routeItem = new RouteItem(target, new RoutedEventHandlerInfo(handler, handledEventsToo)); + + _routeItemList.Add(routeItem); + } + + /// + /// Invokes all the handlers that have been + /// added to the route + /// + /// + /// NOTE: If the + /// of the associated + /// is + /// the last handlers added are the + /// last ones invoked + /// However if the + /// of the associated + /// is , + /// the last handlers added are the + /// first ones invoked. + /// However the handlers for a particular object + /// are always invoked in the order they were added + /// regardless of whether its a tunnel or buble. + /// + /// + /// + /// + /// that raised the RoutedEvent + /// + /// + /// that carry + /// all the details specific to this RoutedEvent + /// + internal void InvokeHandlers(object source, RoutedEventArgs args) + { + // Check RoutingStrategy to know the order of invocation + if (args.RoutedEvent.RoutingStrategy == RoutingStrategy.Bubble || + args.RoutedEvent.RoutingStrategy == RoutingStrategy.Direct) + { + + // If the RoutingStrategy of the associated is + // Bubble the handlers for the last target + // added are the last ones invoked + // Invoke class listeners + for (int i = 0, count = _routeItemList.Count; i < count; i++) + { + RouteItem ri = ((RouteItem)_routeItemList[i]); + args.InvokeHandler(ri); + } + } + else + { + int endTargetIndex = _routeItemList.Count - 1; + int startTargetIndex; + + // If the RoutingStrategy of the associated is + // Tunnel the handlers for the last target + // added are the first ones invoked + while (endTargetIndex >= 0) + { + // For tunnel events we need to invoke handlers for the last target first. + // However the handlers for that individual target must be fired in the right order. + object currTarget = ((RouteItem)_routeItemList[endTargetIndex]).Target; + for (startTargetIndex = endTargetIndex; startTargetIndex >= 0; startTargetIndex--) + { + if (((RouteItem)_routeItemList[startTargetIndex]).Target != currTarget) + { + if(startTargetIndex == endTargetIndex && endTargetIndex > 0) + { + endTargetIndex--; + } + else + { + break; + } + } + } + + for (int i = startTargetIndex + 1; i <= endTargetIndex; i++) + { + RouteItem ri = ((RouteItem)_routeItemList[i]); + args.InvokeHandler(ri); + } + + endTargetIndex = startTargetIndex; + } + } + } + + #endregion External API + + #region Operations + + // Return associated RoutedEvent + internal RoutedEvent RoutedEvent; + + #endregion Operations + + #region Data + + // Stores the routed event handlers to be + // invoked for the associated RoutedEvent + private ArrayList _routeItemList; + + #endregion Data + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventArgs.cs new file mode 100644 index 0000000..78692f3 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventArgs.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// Provides data for the various property changed events. + /// + public class PropertyChangedEventArgs + { + /// + /// Initializes a new instance of the PropertyChangedEventArgs class. + /// + /// + /// The property whose value changed. + /// + /// + /// The value of the property before the change. + /// + /// + /// The value of the property after the change. + /// + public PropertyChangedEventArgs(string property, object oldValue, object newValue) + { + Property = property; + OldValue = oldValue; + NewValue = newValue; + } + + /// + /// The property whose value changed. + /// + public readonly string Property; + + /// + /// The value of the property before the change. + /// + public readonly object OldValue; + + /// + /// The value of the property after the change. + /// + public readonly object NewValue; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventHandler.cs b/source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventHandler.cs new file mode 100644 index 0000000..a535143 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/PropertyChangedEventHandler.cs @@ -0,0 +1,16 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// Represents the method that will handle the event raised when a + /// Property is changed + /// + public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e); +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/RouteItem.cs b/source/nanoFramework.Graphics.Wpf/Core/System/RouteItem.cs new file mode 100644 index 0000000..ac60497 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/RouteItem.cs @@ -0,0 +1,90 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + // An item in the EventRoute + // + // RouteItem constitutes + // the target object and + // list of RoutedEventHandlerInfo that need + // to be invoked upon the target object + internal class RouteItem + { + #region Construction + + // Constructor for RouteItem + internal RouteItem(object target, RoutedEventHandlerInfo routedEventHandlerInfo) + { + _target = target; + _routedEventHandlerInfo = routedEventHandlerInfo; + } + + #endregion Construction + + #region Operations + + // Returns target + internal object Target + { + get { return _target; } + } + + /// + /// Is the given object equals the current + /// + public override bool Equals(object o) + { + return Equals((RouteItem)o); + } + + /// + /// Is the given RouteItem equals the current + /// + public bool Equals(RouteItem routeItem) + { + return ( + routeItem._target == this._target && + routeItem._routedEventHandlerInfo == this._routedEventHandlerInfo); + } + + /// + /// Serves as a hash function for a particular type, suitable for use in + /// hashing algorithms and data structures like a hash table + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + /// Equals operator overload + /// + public static bool operator ==(RouteItem routeItem1, RouteItem routeItem2) + { + return routeItem1.Equals(routeItem2); + } + + /// + /// NotEquals operator overload + /// + public static bool operator !=(RouteItem routeItem1, RouteItem routeItem2) + { + return !routeItem1.Equals(routeItem2); + } + + #endregion Operations + + #region Data + + internal object _target; + internal RoutedEventHandlerInfo _routedEventHandlerInfo; + + #endregion Data + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEvent.cs b/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEvent.cs new file mode 100644 index 0000000..a7ba1d3 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEvent.cs @@ -0,0 +1,130 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.UI +{ + /// + /// RoutedEvent is a unique identifier for + /// any registered RoutedEvent + /// + /// + /// RoutedEvent constitutes the + /// cref="RoutedEvent.Name" + /// cref="RoutedEvent.RoutingStrategy" + /// cref="RoutedEvent.HandlerType" + /// cref="RoutedEvent.OwnerType" + /// + /// NOTE: None of the members can be null + /// + /// + public sealed class RoutedEvent + { + #region External API + + /// + /// Returns the Name of the RoutedEvent + /// + /// + /// RoutedEvent Name is unique within the + /// OwnerType (super class types not considered + /// when talking about uniqueness) + /// + /// + public string Name + { + get { return _name; } + } + + /// + /// Returns the + /// of the RoutedEvent + /// + /// + public RoutingStrategy RoutingStrategy + { + get { return _routingStrategy; } + } + + /// + /// Returns Type of Handler for the RoutedEvent + /// + /// + /// HandlerType is a type of delegate + /// + /// + public Type HandlerType + { + get { return _handlerType; } + } + + /// + /// String representation + /// + public override string ToString() + { + return _name; + } + + #endregion External API + + #region Construction + + /// + /// Create a new routed event. + /// + /// You have to promise not to duplicate another event name in the system, + /// or you will be sorry. + /// + /// + /// + /// + public RoutedEvent( + string name, + RoutingStrategy routingStrategy, + Type handlerType + ) + { + _name = name; + _routingStrategy = routingStrategy; + _handlerType = handlerType; + lock (typeof(GlobalLock)) + { + if (_eventCount >= Int32.MaxValue) + { + throw new InvalidOperationException("too many events"); + } + + _globalIndex = _eventCount++; + } + } + + /// + /// Index for this event + /// + internal int GlobalIndex + { + get { return _globalIndex; } + } + + #endregion Construction + + #region Data + + private string _name; // do we need this ? we will incur some dumb strings. + internal RoutingStrategy _routingStrategy; + private Type _handlerType; + private int _globalIndex; + static int _eventCount; + + private class GlobalLock { } + + #endregion Data + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventArgs.cs b/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventArgs.cs new file mode 100644 index 0000000..31995d9 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventArgs.cs @@ -0,0 +1,271 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI +{ + /// + /// The container for all state associated + /// with a RoutedEvent + /// + /// + /// + /// constitutes the + /// , + /// , + /// and + /// + /// + /// + /// Different + /// can be used with a single + /// + /// + /// The is responsible + /// for packaging the , + /// providing extra event state info, and invoking the + /// handler associated with the RoutedEvent + /// + public class RoutedEventArgs : EventArgs + { + #region Construction + + /// + /// Constructor for + /// + /// + /// All members take default values + /// + /// + /// + /// defaults to null + /// defaults to + /// false + /// defaults to null + /// also defaults to null + /// + /// + public RoutedEventArgs() + { + } + + /// + /// Constructor for + /// + /// The new value that the RoutedEvent Property is being set to + public RoutedEventArgs(RoutedEvent routedEvent) + : this(routedEvent, null) + { + } + + /// + /// Constructor for + /// + /// The new value that the SourceProperty is being set to + /// The new value that the RoutedEvent Property is being set to + public RoutedEventArgs(RoutedEvent routedEvent, object source) + { + _routedEvent = routedEvent; + _source = _originalSource = source; + } + + #endregion Construction + + #region External API + /// + /// Returns the associated + /// with this + /// + /// + /// The cannot be null + /// at any time + /// + public RoutedEvent RoutedEvent + { + get { return _routedEvent; } + set + { + if ((_flags & (Flags.InvokingHandler)) != 0) + throw new InvalidOperationException(); + + _routedEvent = value; + } + } + + /// + /// Returns a boolean flag indicating if or not this + /// RoutedEvent has been handled this far in the route + /// + /// + /// Initially starts with a false value before routing + /// has begun + /// + public bool Handled + { + get + { + return ((_flags & Flags.Handled) != 0); + } + + set + { + if (_routedEvent == null) + { + throw new InvalidOperationException(); + } + + // Note: We need to allow the caller to change the handled value + // from true to false. + // + // We are concerned about scenarios where a child element + // listens to a high-level event (such as TextInput) while a + // parent element listens tp a low-level event such as KeyDown. + // In these scenarios, we want the parent to not respond to the + // KeyDown event, in deference to the child. + // + // Internally we implement this by asking the parent to only + // respond to KeyDown events if they have focus. This works + // around the problem and is an example of an unofficial + // protocol coordinating the two elements. + // + // But we imagine that there will be some cases we miss or + // that third parties introduce. For these cases, we expect + // that the application author may need to mark the KeyDown + // as handled in the child, and then reset the event to + // being unhandled after the parent, so that default processing + // and promotion still occur. + // + // For more information see the following task: + // 20284: Input promotion breaks down when lower level input is intercepted + + _flags |= Flags.Handled; + } + } + + /// + /// Returns Source object that raised the RoutedEvent + /// + public object Source + { + get { return _source; } + set + { + + if ((_flags & (Flags.InvokingHandler)) != 0) + throw new InvalidOperationException(); + + if (_routedEvent == null) + { + throw new InvalidOperationException(); + } + + object source = value; + if (_source == null && _originalSource == null) + { + // Gets here when it is the first time that the source is set. + // This implies that this is also the original source of the event + _source = _originalSource = source; + OnSetSource(source); + } + else if (_source != source) + { + // This is the actiaon taken at all other times when the + // source is being set to a different value from what it was + _source = source; + OnSetSource(source); + } + } + } + + /// + /// Returns OriginalSource object that raised the RoutedEvent + /// + /// + /// Always returns the OriginalSource object that raised the + /// RoutedEvent unlike + /// that may vary under specific scenarios + /// This property acquires its value once before the event + /// handlers are invoked and never changes then on + /// + public object OriginalSource + { + get { return _originalSource; } + } + + /// + /// Invoked when the source of the event is set + /// + /// + /// Changing the source of an event can often + /// require updating the data within the event. + /// For this reason, the OnSource= method is + /// protected virtual and is meant to be + /// overridden by sub-classes of + /// + /// Also see + /// + /// + /// The new value that the SourceProperty is being set to + /// + protected virtual void OnSetSource(object source) + { + } + + #endregion External API + + #region Operations + + /// + /// Invokes the handler associated with the specified RouteItem + /// + /// + /// RouteItem containing handler and target + /// + internal void InvokeHandler(RouteItem routeItem) + { + RoutedEventHandlerInfo routedEventHandlerInfo = routeItem._routedEventHandlerInfo; + + if (this.Handled == false || routedEventHandlerInfo._handledEventsToo == true) + { + RoutedEventHandler handler = routedEventHandlerInfo._handler; + _flags |= Flags.InvokingHandler; + + try + { + handler(routeItem._target, this); + } + finally + { + _flags &= ~Flags.InvokingHandler; + } + } + } + + #endregion Operations + + #region Data + + internal RoutedEvent _routedEvent; + internal object _source; + private object _originalSource; + + private Flags _flags; + + [Flags] + private enum Flags : uint + { + Handled = 1, + InvokingHandler = 2, + } + + #endregion Data + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventHandlerInfo.cs b/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventHandlerInfo.cs new file mode 100644 index 0000000..bd96f96 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/RoutedEventHandlerInfo.cs @@ -0,0 +1,129 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + + /// + /// RoutedEventHandler Definition + /// + /// + public delegate void RoutedEventHandler(object sender, RoutedEventArgs e); + + /// + /// Container for handler instance and other + /// invocation preferences for this handler + /// instance + /// + /// + /// RoutedEventHandlerInfo constitutes the + /// handler instance and flag that indicates if + /// or not this handler must be invoked for + /// already handled events + /// + /// + /// This class needs to be public because it is + /// used by ContentElement in the Framework + /// to store Instance EventHandlers + /// + public class RoutedEventHandlerInfo + { + #region Construction + + /// + /// Construtor for RoutedEventHandlerInfo + /// + /// + /// Non-null handler + /// + /// + /// Flag that indicates if or not the handler must + /// be invoked for already handled events + /// + internal RoutedEventHandlerInfo(RoutedEventHandler handler, bool handledEventsToo) + { + _handler = handler; + _handledEventsToo = handledEventsToo; + } + + #endregion Construction + + #region Operations + + /// + /// Returns associated handler instance + /// + public RoutedEventHandler Handler + { + get { return _handler; } + } + + /// + /// Returns HandledEventsToo Flag + /// + public bool InvokeHandledEventsToo + { + get { return _handledEventsToo; } + } + + /// + /// Is the given object equivalent to the current one + /// + public override bool Equals(object obj) + { + RoutedEventHandlerInfo tmp = obj as RoutedEventHandlerInfo; + + if (tmp == null ) + return false; + + return Equals(tmp); + } + + /// + /// Is the given RoutedEventHandlerInfo equals the current + /// + public bool Equals(RoutedEventHandlerInfo handlerInfo) + { + return _handler == handlerInfo._handler && _handledEventsToo == handlerInfo._handledEventsToo; + } + + /// + /// Serves as a hash function for a particular type, suitable for use in + /// hashing algorithms and data structures like a hash table + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + /// Equals operator overload + /// + public static bool operator ==(RoutedEventHandlerInfo handlerInfo1, RoutedEventHandlerInfo handlerInfo2) + { + return handlerInfo1.Equals(handlerInfo2); + } + + /// + /// NotEquals operator overload + /// + public static bool operator !=(RoutedEventHandlerInfo handlerInfo1, RoutedEventHandlerInfo handlerInfo2) + { + return !handlerInfo1.Equals(handlerInfo2); + } + + #endregion Operations + + #region Data + + internal RoutedEventHandler _handler; + internal bool _handledEventsToo; + + #endregion Data + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/RoutingStrategy.cs b/source/nanoFramework.Graphics.Wpf/Core/System/RoutingStrategy.cs new file mode 100644 index 0000000..8a2fb3e --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/RoutingStrategy.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// Routing Strategy can be either of + /// Tunnel or Bubble + /// + /// + public enum RoutingStrategy + { + /// + /// Tunnel + /// + /// + /// Route the event starting at the root of + /// the visual tree and ending with the source + /// + Tunnel, + + /// + /// Bubble + /// + /// + /// Route the event starting at the source + /// and ending with the root of the visual tree + /// + Bubble, + + /// + /// Direct + /// + /// + /// Raise the event at the source only. + /// + Direct + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/SystemMetrics.cs b/source/nanoFramework.Graphics.Wpf/Core/System/SystemMetrics.cs new file mode 100644 index 0000000..409de87 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/SystemMetrics.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// + /// + public sealed class SystemMetrics + { + // Gets the color depth of the screen + // color depth isn't the whole story, this needs to be improved. + /// + /// + /// + public static int ScreenColorDepth + { + get + { + // int bpp, orientation, height, width; + // NetMF Microsoft.SPOT.Hardware.HardwareProvider hwProvider = Microsoft.SPOT.Hardware.HardwareProvider.HwProvider; + // hwProvider.GetLCDMetrics(out width, out height, out bpp, out orientation); + +// New + DisplayControl dc = new DisplayControl(); + int bpp = dc.BitsPerPixel; + + return bpp; + } + } + // Gets the width of the screen + /// + /// + /// + public static int ScreenWidth + { + get + { +// int bpp, orientation, height, width; +// NetMF Microsoft.SPOT.Hardware.HardwareProvider hwProvider = Microsoft.SPOT.Hardware.HardwareProvider.HwProvider; +// hwProvider.GetLCDMetrics(out width, out height, out bpp, out orientation); + + +// New + DisplayControl dc = new DisplayControl(); + int width = dc.LongerSide; + return width; + } + } + // Gets the height of the screen + /// + /// + /// + public static int ScreenHeight + { + get + { +// int bpp, orientation, height, width; +// NetMf HardwareProvider hwProvider = HardwareProvider.HwProvider; +// hwProvider.GetLCDMetrics(out width, out height, out bpp, out orientation); + +// New + int height; + DisplayControl dc = new DisplayControl(); + height = dc.ShorterSide; + + return height; + } + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Threading/Dispatcher.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/Dispatcher.cs new file mode 100644 index 0000000..b65353c --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/Dispatcher.cs @@ -0,0 +1,626 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + + +//In what case was EnsureStatics needed? +//When a program has a static Window type that gets initialized in the static constructor? +//Does this case still work or not? If not, keep code as it was but inlined. + +using System; +using System.Collections; +using System.Diagnostics; +using System.Threading; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI.Threading +{ + /// + /// Provides UI services for a thread. + /// + public sealed class Dispatcher + { + /// + /// Returns the Dispatcher for the current thread. + /// + /// Dispatcher + public static Dispatcher CurrentDispatcher + { + get + { + Dispatcher dispatcher = FromThread(Thread.CurrentThread); + + //While FromThread() and Dispatcher() both operate in the GlobalLock, + //and this function does not, there is no race condition because threads cannot + //create Dispatchers on behalf of other threads. Thus, while other threads may + //create a Dispatcher for themselves, they cannot create a Dispatcher for this + //thread, therefore only one Dispatcher for each thread can exist in the ArrayList, + //and there is no race condition. + if (dispatcher == null) + { + lock(typeof(GlobalLock)) + { + dispatcher = FromThread(Thread.CurrentThread); + + if(dispatcher == null) + { + dispatcher = new Dispatcher(); + } + } + } + + return dispatcher; + } + } + + /// + /// Returns the Dispatcher for the specified thread. + /// + /// + /// If there is no dispatcher available for the specified thread, + /// this method will return null. + /// + public static Dispatcher FromThread(Thread thread) + { + Dispatcher dispatcher = null; + + // _possibleDispatcher is initialized in the static constructor and is never changed. + // According to section 12.6.6 of Partition I of ECMA 335, reads and writes of object + // references shall be atomic. + dispatcher = _possibleDispatcher; + if (dispatcher == null || dispatcher._thread != thread) + { + // The "possible" dispatcher either was null or belongs to + // the a different thread. + dispatcher = null; + + WeakReference wref = (WeakReference)_dispatchers[thread.ManagedThreadId]; + + if(wref != null) + { + dispatcher = wref.Target as Dispatcher; + if (dispatcher != null) + { + if (dispatcher._thread == thread) + { + // Shortcut: we track one static reference to the last current + // dispatcher we gave out. For single-threaded apps, this will + // be set all the time. For multi-threaded apps, this will be + // set for periods of time during which accessing CurrentDispatcher + // is cheap. When a thread switch happens, the next call to + // CurrentDispatcher is expensive, but then the rest are fast + // again. + _possibleDispatcher = dispatcher; + } + else + { + _dispatchers.Remove(thread.ManagedThreadId); + } + } + } + } + + return dispatcher; + } + + private Dispatcher() + { + _thread = Thread.CurrentThread; + _queue = new Queue(); + _event = new AutoResetEvent(false); + _instanceLock = new Object(); + + // Add ourselves to the map of dispatchers to threads. + _dispatchers[_thread.ManagedThreadId] = new WeakReference(this); + + if(_possibleDispatcher == null) + { + _possibleDispatcher = this; + } + } + + /// + /// Checks that the calling thread has access to this object. + /// + /// + /// Only the dispatcher thread may access DispatcherObjects. + ///

+ /// This method is public so that any thread can probe to + /// see if it has access to the DispatcherObject. + /// + /// + /// True if the calling thread has access to this object. + /// + public bool CheckAccess() + { + return (_thread == Thread.CurrentThread); + } + + ///

+ /// Verifies that the calling thread has access to this object. + /// + /// + /// Only the dispatcher thread may access DispatcherObjects. + ///

+ /// This method is public so that derived classes can probe to + /// see if the calling thread has access to itself. + /// + public void VerifyAccess() + { + if (_thread != Thread.CurrentThread) + { + throw new InvalidOperationException(); + } + } + + ///

+ /// Thread for the dispatcher. + /// + /// + public Thread Thread + { + get + { + return _thread; + } + } + + // Returns whether or not the operation was removed. + internal bool Abort(DispatcherOperation operation) + { + bool notify = false; + + lock (_instanceLock) + { + if (operation.Status == DispatcherOperationStatus.Pending) + { + operation.Status = DispatcherOperationStatus.Aborted; + notify = true; + } + } + + return notify; + } + + /// + /// Begins the process of shutting down the dispatcher, synchronously. + /// The process may complete asynchronously, since we may be + /// nested in dispatcher frames. + /// + public void InvokeShutdown() + { + VerifyAccess(); + + if (_hasShutdownFinished) + { + throw new InvalidOperationException(); + } + + try + { + if (!_hasShutdownStarted) + { + // Call the ShutdownStarted event before we actually mark ourselves + // as shutting down. This is so the handlers can actaully do work + // when they get this event without throwing exceptions. + EventHandler e = ShutdownStarted; + if (e != null) + { + e(this, EventArgs.Empty); + } + + _hasShutdownStarted = true; + + if (_frameDepth > 0) + { + // If there are any frames running, we have to wait for them + // to unwind before we can safely destroy the dispatcher. + } + else + { + // The current thread is not spinning inside of the Dispatcher, + // so we can go ahead and destroy it. + ShutdownImpl(); + } + + _dispatchers.Remove(_thread.ManagedThreadId); + } + } + catch (Exception e) + { + if (_finalExceptionHandler == null || !_finalExceptionHandler(this, e)) + { + throw; + } + } + } + + private void ShutdownImpl() + { + //Debug.Assert(_hasShutdownStarted); + //Debug.Assert(!_hasShutdownFinished); + + // Call the ShutdownFinished event before we actually mark ourselves + // as shut down. This is so the handlers can actaully do work + // when they get this event without throwing exceptions. + EventHandler e = ShutdownFinished; + if (e != null) + { + e(this, EventArgs.Empty); + } + + // Mark this dispatcher as shut down. Attempts to BeginInvoke + // or Invoke will result in an exception. + _hasShutdownFinished = true; + + lock (_instanceLock) + { + // Now that the queue is off-line, abort all pending operations, + // including inactive ones. + while (_queue.Count > 0) + { + DispatcherOperation operation = (DispatcherOperation)_queue.Dequeue(); + + if (operation != null) + { + operation.Abort(); + } + } + } + } + + // + // wakes up the dispatcher to force it to check the + // frame.Continue flag + internal void QueryContinueFrame() + { + _event.Set(); + } + + /// + /// Whether or not the dispatcher is shutting down. + /// + public bool HasShutdownStarted + { + get + { + return _hasShutdownStarted; + } + } + + /// + /// Whether or not the dispatcher has been shut down. + /// + public bool HasShutdownFinished + { + get + { + return _hasShutdownFinished; + } + } + + /// + /// Raised when the dispatcher starts shutting down. + /// + public event EventHandler ShutdownStarted; + + /// + /// Raised when the dispatcher is shut down. + /// + public event EventHandler ShutdownFinished; + + /// + /// Push the main execution frame. + /// + /// + /// This frame will continue until the dispatcher is shut down. + /// + public static void Run() + { + PushFrame(new DispatcherFrame()); + } + + /// + /// Push an execution frame. + /// + /// + /// The frame for the dispatcher to process. + /// + public static void PushFrame(DispatcherFrame frame) + { + if (frame == null) + { + throw new ArgumentNullException(); + } + + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + if (dispatcher._hasShutdownFinished) + { + throw new InvalidOperationException(); + } + + dispatcher.PushFrameImpl(frame); + } + + internal DispatcherFrame CurrentFrame + { + get { return _currentFrame; } + } + + // + // instance implementation of PushFrame + private void PushFrameImpl(DispatcherFrame frame) + { + DispatcherFrame prevFrame = _currentFrame; + _frameDepth++; + try + { + _currentFrame = frame; + + while (frame.Continue) + { + DispatcherOperation op = null; + bool aborted = false; + + // + // Dequeue the next operation if appropriate + if (_queue.Count > 0) + { + op = (DispatcherOperation)_queue.Dequeue(); + + //Must check aborted flag inside lock because + //user program could call op.Abort() between + //here and before the call to Invoke() + aborted = op.Status == DispatcherOperationStatus.Aborted; + } + + if (op != null) + { + if (!aborted) + { + // Invoke the operation: + //Debug.Assert(op._status == DispatcherOperationStatus.Pending); + + // Mark this operation as executing. + op._status = DispatcherOperationStatus.Executing; + + op._result = null; + + try + { + op._result = op._method(op._args); + } + catch (Exception e) + { + if (_finalExceptionHandler == null || + !_finalExceptionHandler(op, e)) + { + throw; + } + } + + // Mark this operation as completed. + op._status = DispatcherOperationStatus.Completed; + + // Raise the Completed so anyone who is waiting will wake up. + op.OnCompleted(); + } + } + else + { + _event.WaitOne(); + } + } + } + finally + { + _frameDepth--; + + _currentFrame = prevFrame; + + // If this was the last frame to exit after a quit, we + // can now dispose the dispatcher. + if (_frameDepth == 0) + { + if (_hasShutdownStarted) + { + ShutdownImpl(); + } + } + } + } + + /// + /// Executes the specified delegate asynchronously with the specified + /// arguments, on the thread that the Dispatcher was created on. + /// + /// + /// A delegate to a method that takes parameters of the same number + /// and type that are contained in the args parameter. + /// + /// + /// An object to pass as the argument to the given method. + /// This can be null if no arguments are needed. + /// + /// + /// A DispatcherOperation object that represents the result of the + /// BeginInvoke operation. null if the operation could not be queued. + /// + public DispatcherOperation BeginInvoke(DispatcherOperationCallback method, object args) + { + if (method == null) + { + throw new ArgumentNullException(); + } + + DispatcherOperation operation = null; + + if (!_hasShutdownFinished) + { + operation = new DispatcherOperation(this, method, args); + + // Add the operation to the work queue + _queue.Enqueue(operation); + + // this will only cause at most 1 extra dispatcher loop, so + // always set the event. + _event.Set(); + } + + return operation; + } + + /// + /// Executes the specified delegate synchronously with the specified + /// arguments, on the thread that the Dispatcher was created on. + /// + /// + /// The maximum amount of time to wait for the operation to complete. + /// + /// + /// A delegate to a method that takes parameters of the same number + /// and type that are contained in the args parameter. + /// + /// + /// An object to pass as the argument to the given method. + /// This can be null if no arguments are needed. + /// + /// + /// The return value from the delegate being invoked, or null if + /// the delegate has no return value or if the operation was aborted. + /// + public object Invoke(TimeSpan timeout, DispatcherOperationCallback method, object args) + { + if (method == null) + { + throw new ArgumentNullException(); + } + + object result = null; + + DispatcherOperation op = BeginInvoke(method, args); + + if (op != null) + { + op.Wait(timeout); + + if (op.Status == DispatcherOperationStatus.Completed) + { + result = op.Result; + } + else if (op.Status == DispatcherOperationStatus.Aborted) + { + // Hm, someone aborted us. Maybe the dispatcher got + // shut down on us? Just return null. + } + else + { + // We timed out, just abort the op so that it doesn't + // invoke later. + // + // Note the race condition: if this is a foreign thread, + // it is possible that the dispatcher thread could actually + // dispatch the operation between the time our Wait() + // call returns and we get here. In the case the operation + // will actually be dispatched, but we will return failure. + // + // We recognize this but decide not to do anything about it, + // as this is a common problem is multi-threaded programming. + op.Abort(); + } + } + + return result; + } + + // + // Invoke a delegate in a try/catch. + // + internal object WrappedInvoke(DispatcherOperationCallback callback, object arg) + { + object result = null; + + try + { + result = callback(arg); + } + catch (Exception e) + { +#if NANOCLR_TRACE_DEBUG_DISPATCHER + // allow the debugger to break on the original exception. + if (System.Diagnostics.Debugger.IsAttached) + { + } + else +#endif + if (_finalExceptionHandler == null || !_finalExceptionHandler(this, e)) + { + throw; + } + } + + return result; + } + + internal static void SetFinalDispatcherExceptionHandler(DispatcherExceptionEventHandler handler) + { + Dispatcher.CurrentDispatcher.SetFinalExceptionHandler(handler); + } + + internal void SetFinalExceptionHandler(DispatcherExceptionEventHandler handler) + { + _finalExceptionHandler = handler; + } + + private DispatcherFrame _currentFrame; + private int _frameDepth; + internal bool _hasShutdownStarted; // used from DispatcherFrame + private bool _hasShutdownFinished; + + private Queue _queue; + private AutoResetEvent _event; + private object _instanceLock; + + static Hashtable _dispatchers = new Hashtable(); + static Dispatcher _possibleDispatcher; + + // note: avalon uses a weakreference to track the thread. the advantage i can see to that + // is in case some other thread has a reference to the dispatcher object, but the dispatcher thread + // has terminated. In that case the Thread object would remain until the Dispatcher is GC'd. + // we dont' have much unmanaged state associated with a dead thread, so it's probably okay to let it + // hang around. if we need to run a finalizer on the thread or something, then we should use a weakreference here. + + private Thread _thread; + + // Raised when a dispatcher exception was caught during an Invoke or BeginInvoke + // Hooked in by the application. + internal DispatcherExceptionEventHandler _finalExceptionHandler; + + // these are per dispatcher, track them here. + internal nanoFramework.Presentation.LayoutManager _layoutManager; + internal nanoFramework.UI.Input.InputManager _inputManager; + internal nanoFramework.Presentation.Media.MediaContext _mediaContext; + + // + // we use this type of a global static lock. we can't guarantee + // static constructors are run int he right order, but we can guarantee the + // lock for the type exists. + class GlobalLock { } + } + + /// + /// Delegate for processing exceptions that happen during Invoke or BeginInvoke. + /// Return true if the exception was processed. + /// + internal delegate bool DispatcherExceptionEventHandler(object sender, Exception e); + + /// + /// A convenient delegate to use for dispatcher operations. + /// + public delegate object DispatcherOperationCallback(object arg); + +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherFrame.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherFrame.cs new file mode 100644 index 0000000..de43602 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherFrame.cs @@ -0,0 +1,82 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Threading +{ + /// + /// Representation of Dispatcher frame. + /// + public class DispatcherFrame + { + /// + /// Constructs a new instance of the DispatcherFrame class. + /// + public DispatcherFrame() + : this(true) + { + } + + /// + /// Constructs a new instance of the DispatcherFrame class. + /// + /// + /// Indicates whether or not this frame will exit when all frames + /// are requested to exit. + ///

+ /// Dispatcher frames typically break down into two categories: + /// 1) Long running, general purpose frames, that exit only when + /// told to. These frames should exit when requested. + /// 2) Short running, very specific frames that exit themselves + /// when an important criteria is met. These frames may + /// consider not exiting when requested in favor of waiting + /// for their important criteria to be met. These frames + /// should have a timeout associated with them. + /// + public DispatcherFrame(bool exitWhenRequested) + { + _exitWhenRequested = exitWhenRequested; + _continue = true; + _dispatcher = Dispatcher.CurrentDispatcher; + } + + ///

+ /// Indicates that this dispatcher frame should exit. + /// + public bool Continue + { + get + { + // First check if this frame wants to continue. + bool shouldContinue = _continue; + if (shouldContinue) + { + // This frame wants to continue, so next check if it will + // respect the "exit requests" from the dispatcher. + // and if the dispatcher wants to exit. + if (_exitWhenRequested && _dispatcher._hasShutdownStarted) + { + shouldContinue = false; + } + } + + return shouldContinue; + } + + set + { + _continue = value; + + _dispatcher.QueryContinueFrame(); + } + } + + private bool _exitWhenRequested; + private bool _continue; + private Dispatcher _dispatcher; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherObject.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherObject.cs new file mode 100644 index 0000000..4370bc2 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherObject.cs @@ -0,0 +1,110 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System.Threading; + +namespace nanoFramework.UI.Threading +{ + /// + /// A DispatcherObject is an object associated with a + /// . A DispatcherObject instance should + /// only be access by the dispatcher's thread. + /// + /// + /// Subclasses of should enforce thread + /// safety by calling on all their public + /// methods to ensure the calling thread is the appropriate thread. + /// + /// DispatcherObject cannot be independently instantiated; that is, + /// all constructors are protected. + /// + public abstract class DispatcherObject + { + + /// + /// Checks that the calling thread has access to this object. + /// + /// + /// Only the dispatcher thread may access DispatcherObjects. + ///

+ /// This method is public so that any thread can probe to + /// see if it has access to the DispatcherObject. + /// + /// + /// True if the calling thread has access to this object. + /// + public bool CheckAccess() + { + bool accessAllowed = true; + + // Note: a DispatcherObject that is not associated with a + // dispatcher is considered to be free-threaded. + if (Dispatcher != null) + { + accessAllowed = Dispatcher.CheckAccess(); + } + + return accessAllowed; + } + + ///

+ /// Verifies that the calling thread has access to this object. + /// + /// + /// Only the dispatcher thread may access DispatcherObjects. + ///

+ /// This method is public so that derived classes can probe to + /// see if the calling thread has access to itself. + /// + /// This is only verified in debug builds. + /// + public void VerifyAccess() + { + // Note: a DispatcherObject that is not associated with a + // dispatcher is considered to be free-threaded. + if (Dispatcher != null) + { + Dispatcher.VerifyAccess(); + } + } + + ///

+ /// Instantiate this object associated with the current Dispatcher. + /// + protected DispatcherObject() + { + Dispatcher = Dispatcher.CurrentDispatcher; + } + + /// + /// Instantiate this object associated with the current Dispatcher. + /// + /// + /// Whether or not the object can be detached from any Dispatcher. + /// + internal DispatcherObject(bool canBeUnbound) + { + if (canBeUnbound) + { + // DispatcherObjects that can be unbound do not force + // the creation of a dispatcher. + Dispatcher = Dispatcher.FromThread(Thread.CurrentThread); + } + else + { + Dispatcher = Dispatcher.CurrentDispatcher; + } + } + + /// + /// The that this + /// is associated with. + /// + public readonly Dispatcher Dispatcher; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperation.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperation.cs new file mode 100644 index 0000000..650c88e --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperation.cs @@ -0,0 +1,327 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Threading; +using nanoFramework.Runtime; +using nanoFramework.Runtime.Events; + + +namespace nanoFramework.UI.Threading +{ + /// + /// DispatcherOperation represents a delegate that has been + /// posted to the Dispatcher queue. + /// + public sealed class DispatcherOperation + { + + internal DispatcherOperation( + Dispatcher dispatcher, + DispatcherOperationCallback method, + object args) + { + _dispatcher = dispatcher; + _method = method; + _args = args; + } + + /// + /// Returns the Dispatcher that this operation was posted to. + /// + public Dispatcher Dispatcher + { + get + { + return _dispatcher; + } + } + + /// + /// The status of this operation. + /// + public DispatcherOperationStatus Status + { + get + { + return _status; + } + + internal set + { + _status = value; + } + } + + /// + /// Waits for this operation to complete. + /// + /// + /// The status of the operation. To obtain the return value + /// of the invoked delegate, use the the Result property. + /// + public DispatcherOperationStatus Wait() + { + return Wait(new TimeSpan(-TimeSpan.TicksPerMillisecond)); // Negative one (-1) milliseconds to prevent the timer from starting. See documentation. + } + + /// + /// Waits for this operation to complete. + /// + /// + /// The maximum amount of time to wait. + /// + /// + /// The status of the operation. To obtain the return value + /// of the invoked delegate, use the the Result property. + /// + public DispatcherOperationStatus Wait(TimeSpan timeout) + { + if ((_status == DispatcherOperationStatus.Pending || _status == DispatcherOperationStatus.Executing) && + timeout.Ticks != 0) + { + if (_dispatcher.Thread == Thread.CurrentThread) + { + if (_status == DispatcherOperationStatus.Executing) + { + // We are the dispatching thread, and the current operation state is + // executing, which means that the operation is in the middle of + // executing (on this thread) and is trying to wait for the execution + // to complete. Unfortunately, the thread will now deadlock, so + // we throw an exception instead. + throw new InvalidOperationException(); + } + + // We are the dispatching thread for this operation, so + // we can't block. We will push a frame instead. + DispatcherOperationFrame frame = new DispatcherOperationFrame(this, timeout); + Dispatcher.PushFrame(frame); + } + else + { + // We are some external thread, so we can just block. Of + // course this means that the Dispatcher (queue)for this + // thread (if any) is now blocked. + // Because we have a single dispatcher per app domain, this thread + // must be from another app domain. We will enforce semantics on + // dispatching between app domains so we don't lock up the system. + + DispatcherOperationEvent wait = new DispatcherOperationEvent(this, timeout); + wait.WaitOne(); + } + } + + return _status; + } + + /// + /// Aborts this operation. + /// + /// + /// False if the operation could not be aborted (because the + /// operation was already in progress) + /// + public bool Abort() + { + bool removed = _dispatcher.Abort(this); + + if (removed) + { + _status = DispatcherOperationStatus.Aborted; + + // Raise the Aborted so anyone who is waiting will wake up. + EventHandler e = Aborted; + if (e != null) + { + e(this, EventArgs.Empty); + } + } + + return removed; + } + + /// + /// Returns the result of the operation if it has completed. + /// + public object Result + { + get + { + return _result; + } + } + + /// + /// An event that is raised when the operation is aborted. + /// + public event EventHandler Aborted; + + /// + /// An event that is raised when the operation completes. + /// + public event EventHandler Completed; + + internal void OnCompleted() + { + EventHandler e = Completed; + if (e != null) + { + e(this, EventArgs.Empty); + } + } + + private class DispatcherOperationFrame : DispatcherFrame ,IDisposable + { + // Note: we pass "exitWhenRequested=false" to the base + // DispatcherFrame construsctor because we do not want to exit + // this frame if the dispatcher is shutting down. This is + // because we may need to invoke operations during the shutdown process. + public DispatcherOperationFrame(DispatcherOperation op, TimeSpan timeout) + : base(false) + { + _operation = op; + + // We will exit this frame once the operation is completed or aborted. + _operation.Aborted += new EventHandler(OnCompletedOrAborted); + _operation.Completed += new EventHandler(OnCompletedOrAborted); + + // We will exit the frame if the operation is not completed within + // the requested timeout. + if (timeout.Ticks > 0) + { + _waitTimer = new Timer(new TimerCallback(OnTimeout), + null, + timeout, + new TimeSpan(-TimeSpan.TicksPerMillisecond)); // Negative one (-1) milliseconds to disable periodic signaling. + } + + // Some other thread could have aborted the operation while we were + // setting up the handlers. We check the state again and mark the + // frame as "should not continue" if this happened. + if (_operation._status != DispatcherOperationStatus.Pending) + { + Exit(); + } + } + + private void OnCompletedOrAborted(object sender, EventArgs e) + { + Exit(); + } + + private void OnTimeout(object arg) + { + Exit(); + } + + private void Exit() + { + Continue = false; + + if (_waitTimer != null) + { + _waitTimer.Dispose(); + } + + _operation.Aborted -= new EventHandler(OnCompletedOrAborted); + _operation.Completed -= new EventHandler(OnCompletedOrAborted); + } + + private DispatcherOperation _operation; + private Timer _waitTimer; + + public virtual void Close() + { + Dispose(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + _waitTimer.Dispose(); + } + + } + + private class DispatcherOperationEvent : IDisposable + { + public DispatcherOperationEvent(DispatcherOperation op, TimeSpan timeout) + { + _operation = op; + _timeout = timeout; + _event = new AutoResetEvent(false); + + // We will set our event once the operation is completed or aborted. + _operation.Aborted += new EventHandler(OnCompletedOrAborted); + _operation.Completed += new EventHandler(OnCompletedOrAborted); + + // Since some other thread is dispatching this operation, it could + // have been dispatched while we were setting up the handlers. + // We check the state again and set the event ourselves if this + // happened. + if (_operation._status != DispatcherOperationStatus.Pending && _operation._status != DispatcherOperationStatus.Executing) + { + _event.Set(); + } + } + + private void OnCompletedOrAborted(object sender, EventArgs e) + { + _event.Set(); + } + + public void WaitOne() + { + _waitTimer = new Timer(new TimerCallback(OnTimeout), null, _timeout, new TimeSpan(-TimeSpan.TicksPerMillisecond)); + _event.WaitOne(); + _waitTimer.Dispose(); + + // Cleanup the events. + _operation.Aborted -= new EventHandler(OnCompletedOrAborted); + _operation.Completed -= new EventHandler(OnCompletedOrAborted); + } + + private void OnTimeout(object arg) + { + _event.Set(); + } + + private DispatcherOperation _operation; + private TimeSpan _timeout; + private AutoResetEvent _event; + private Timer _waitTimer; + + public virtual void Close() + { + Dispose(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + _waitTimer.Dispose(); + } + } + + private Dispatcher _dispatcher; + internal DispatcherOperationCallback _method; + internal object _args; + internal object _result; + internal DispatcherOperationStatus _status; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperationStatus.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperationStatus.cs new file mode 100644 index 0000000..0f6b107 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherOperationStatus.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI.Threading +{ + /// + /// An enumeration describing the status of a DispatcherOperation. + /// + /// + public enum DispatcherOperationStatus + { + /// + /// The operation is still pending. + /// + Pending, + + /// + /// The operation has been aborted. + /// + Aborted, + + /// + /// The operation has been completed. + /// + Completed, + + /// + /// The operation has started executing, but has not completed yet. + /// + Executing + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherTimer.cs b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherTimer.cs new file mode 100644 index 0000000..082122a --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/Threading/DispatcherTimer.cs @@ -0,0 +1,231 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Threading; +using nanoFramework.Runtime.Events; + +namespace nanoFramework.UI.Threading +{ + /// + /// A timer that is integrated into the Dispatcher queues, and will + /// be processed after a given amount of time + /// + public class DispatcherTimer : IDisposable + { + /// + /// Creates a timer that uses the current thread's Dispatcher to + /// process the timer event + /// + public DispatcherTimer() + : this(Dispatcher.CurrentDispatcher) + { + } + + /// + /// Creates a timer that uses the specified Dispatcher to + /// process the timer event. + /// + /// + /// The dispatcher to use to process the timer. + /// + public DispatcherTimer(Dispatcher dispatcher) + { + if (dispatcher == null) + { + throw new ArgumentNullException("dispatcher"); + } + + _dispatcher = dispatcher; + + _timer = new Timer(new TimerCallback(this.Callback), null, Timeout.Infinite, Timeout.Infinite); + + } + + /// + /// Gets the dispatcher this timer is associated with. + /// + public Dispatcher Dispatcher + { + get + { + return _dispatcher; + } + } + + /// + /// Gets or sets whether the timer is running. + /// + public bool IsEnabled + { + get + { + return _isEnabled; + } + + set + { + lock (_instanceLock) + { + if (!value && _isEnabled) + { + Stop(); + } + else if (value && !_isEnabled) + { + Start(); + } + } + } + } + + /// + /// Gets or sets the time between timer ticks. + /// + public TimeSpan Interval + { + get + { + return new TimeSpan(_interval * TimeSpan.TicksPerMillisecond); + } + + set + { + bool updateTimer = false; + + long ticks = value.Ticks; + + if (ticks < 0) + throw new ArgumentOutOfRangeException("value", "too small"); + + if (ticks > Int32.MaxValue * TimeSpan.TicksPerMillisecond) + throw new ArgumentOutOfRangeException("value", "too large"); + + lock (_instanceLock) + { + _interval = (int)(ticks / TimeSpan.TicksPerMillisecond); + + if (_isEnabled) + { + updateTimer = true; + } + } + + if (updateTimer) + { + _timer.Change(_interval, _interval); + } + } + } + + /// + /// Starts the timer. + /// + public void Start() + { + lock (_instanceLock) + { + if (!_isEnabled) + { + _isEnabled = true; + + _timer.Change(_interval, _interval); + } + } + } + + /// + /// Stops the timer. + /// + public void Stop() + { + lock (_instanceLock) + { + if (_isEnabled) + { + _isEnabled = false; + + _timer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + } + + /// + /// Occurs when the specified timer interval has elapsed and the + /// timer is enabled. + /// + public event EventHandler Tick; + + /// + /// Any data that the caller wants to pass along with the timer. + /// + public object Tag + { + get + { + return _tag; + } + + set + { + _tag = value; + } + } + + private void Callback(object state) + { + // BeginInvoke a new operation. + _dispatcher.BeginInvoke( + new DispatcherOperationCallback(FireTick), + null); + } + + private object FireTick(object unused) + { + EventHandler e = Tick; + if (e != null) + { + e(this, EventArgs.Empty); + } + + return null; + } + + private object _instanceLock = new Object(); + private Dispatcher _dispatcher; + private int _interval; + private object _tag; + private bool _isEnabled; + private Timer _timer; + + /// + /// + /// + public virtual void Close() + { + Dispose(); + } + + /// + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + _timer.Dispose(); + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Core/System/WindowCollection.cs b/source/nanoFramework.Graphics.Wpf/Core/System/WindowCollection.cs new file mode 100644 index 0000000..a3cf614 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Core/System/WindowCollection.cs @@ -0,0 +1,206 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Presentation; +using System; +using System.Collections; +using System.Diagnostics; + +namespace nanoFramework.UI +{ + #region WindowCollection class + + /// + /// WindowCollection can be used to interate over all the windows that have been + /// opened in the current application. + /// + //CONSIDER: Should this be a sealed class? + public sealed class WindowCollection : ICollection + { + //------------------------------------------------------ + // + // Public Methods + // + //------------------------------------------------------ + #region Public Methods + /// + /// Default Constructor + /// + public WindowCollection() + { + _list = new ArrayList(); + _list.Capacity = 1; + } + + internal WindowCollection(int count) + { + //Debug.Assert(count >= 0, "count must not be less than zero"); + _list = new ArrayList(); + _list.Capacity = 1; + } + + #endregion Public Methods + + //------------------------------------------------------ + // + // Operator overload + // + //------------------------------------------------------ + #region Operator overload + /// + /// Overloaded [] operator to access the WindowCollection list + /// + public Window this[int index] + { + get + { + return _list[index] as Window; + } + } + + #endregion Operator overload + + //------------------------------------------------------ + // + // IEnumerable implementation + // + //------------------------------------------------------ + #region IEnumerable implementation + /// + /// GetEnumerator + /// + /// + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + #endregion IEnumerable implementation + + //-------------------------------------------------------- + // + // ICollection implementation (derives from IEnumerable) + // + //-------------------------------------------------------- + #region ICollection implementation + /// + /// CopyTo + /// + /// + /// + void ICollection.CopyTo(Array array, int index) + { + _list.CopyTo(array, index); + } + + /// + /// CopyTo + /// + /// + /// + public void CopyTo(Window[] array, int index) + { + _list.CopyTo(array, index); + } + + /// + /// Count property + /// + public int Count + { + get + { + return _list.Count; + } + } + + /// + /// IsSynchronized + /// + public bool IsSynchronized + { + get + { + return _list.IsSynchronized; + } + } + + /// + /// SyncRoot + /// + public Object SyncRoot + { + get + { + return _list.SyncRoot; + } + } + + #endregion ICollection implementation + + //------------------------------------------------------ + // + // Internal Methods + // + //------------------------------------------------------ + #region Internal Methods + internal WindowCollection Clone() + { + WindowCollection clone; + lock (_list.SyncRoot) + { + clone = new WindowCollection(_list.Count); + for (int i = 0; i < _list.Count; i++) + { + clone._list.Add(_list[i]); + } + } + + return clone; + } + + internal void Remove(Window win) + { + _list.Remove(win); + } + + internal int Add(Window win) + { + return _list.Add(win); + } + + internal bool HasItem(Window win) + { + lock (_list.SyncRoot) + { + for (int i = 0; i < _list.Count; i++) + { + if (_list[i] == win) + { + return true; + } + } + } + + return false; + } + + #endregion Internal Methods + + //------------------------------------------------------ + // + // Private Fields + // + //------------------------------------------------------ + #region Private Fields + private ArrayList _list; + #endregion Private Fields + } + + #endregion WindowCollection class +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Microsoft.SPOT.Graphics.xml b/source/nanoFramework.Graphics.Wpf/Microsoft.SPOT.Graphics.xml new file mode 100644 index 0000000..f92a8bb --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Microsoft.SPOT.Graphics.xml @@ -0,0 +1,3098 @@ + + + + + + + + + + + Occurs just before an application exits. + + + Occurs when the Run method of an Application object is called. + + + Raises the Exit event. + An EventArgs object that contains the event data for the Exit event. + + + Raises the Startup event. + An EventArgs object that contains the event data for the Startup event. + + + Starts a Windows Presentation Foundation (WPF) application and opens a specified window. + A Window object representing a window that opens automatically when the WPF application starts running. + + + Starts a Windows Presentation Foundation (WPF) application. + + + Causes an application to exit. + + + Gets the Application object for the current application domain. + + + The Application object for the current AppDomain object. + + + + Gets or sets the main window of the current application. + A Window object that represents the current application's main window. + + + Gets or sets the condition that causes the Shutdown method to be called. + A value from the ShutdownMode enumeration. The default return value is OnLastWindowClose. + + + Gets the instantiated windows in the current application. + + A WindowCollection object that contains references to all of the window objects in the current application. + + + + Encapsulates a Windows Presentation Foundation (WPF) application. + + + Initializes a new instance of the Bitmap class to the specified height and width. + An array of pixel data for the specified image. + The bitmap type for the specified image. + + + Initializes a new instance of the Bitmap class to the specified height and width. + The width of the current bitmap, in pixels. + The height of the current bitmap, in pixels. + + + Specifies that text is center-aligned as it flows around a bitmap. + + + Specifies that text is left-aligned as it flows around a bitmap. + + + Specifies that you can use a mask to get or set text alignment around a bitmap. + + + Specifies that text is right-aligned as it flows around a bitmap. + + + Fills the destination rectangle with the color associated with index number 0 in the physical palette. + + + Represents the x-coordinate location of the center of the display device, in pixels. + + + Represents the y-coordinate location of the center of the display device, in pixels. + + + Fills the destination rectangle with a dark gray color. + + + Fills the destination rectangle with a gray color. + + + Inverts the destination rectangle. + + + Fills the destination rectangle with a light gray color. + + + Specifies that the bitmap text is trimmed to the nearest character, and an ellipsis is inserted at the end of each trimmed line. + + + Specifies that if the bitmap text is larger than the space provided, the text is drawn in its full size, rather than being scaled down to fit the value in the Height property. + + + Specifies the maximum height of the display device, in pixels. + + + Specifies the maximum width of the display device, in pixels. + + + Specifies that there are no format rules. + + + Specifies that the current bitmap is opaque. + + + Specifies that the current bitmap is transparent. + + + Inverts the source rectangle. + + + Fills the destination rectangle or circle with a randomly generated pattern. + + + Specifies that a circle should have only a single-pixel border and no fill pattern or color. + + + Copies the source rectangle directly to the destination rectangle. + + + + Specifies that if the bitmap text is larger than the space provided, the text is truncated at the bottom. + + + + Fills the destination rectangle with the color associated with index number 1 in the physical palette. + + + Specifies whether a line of bitmap text automatically wraps words to the beginning of the next line when the line reaches its maximum width. + + + Performs a bit block transfer of the bitmap data corresponding to a rectangle of pixels, transferring the data from a specified source bitmap into the current destination bitmap. + The x-coordinate of the upper-left corner of the destination rectangle. + The y-coordinate of the upper-left corner of the destination rectangle. + The width of the source and destination rectangles. + The height of the source and destination rectangles. + The source bitmap. + The x-coordinate of the upper-left corner of the source rectangle. + The y-coordinate of the upper-left corner of the source rectangle. + A raster operation code. This code defines how the bitmap data for the source rectangle + is to be combined with the bitmap data for the destination rectangle. + + + Draws a circle specified by a coordinate pair (x and y) and a radius, using a specified raster operation. + The x-coordinate of the center of the circle. + The y-coordinate of the center of the circle. + The radius of the circle. + A raster operation code. + + + Clears the entire drawing surface. + + + Draws a filled ellipse on the display device. + The outline color. + The x-coordinate location of the center of the ellipse. + The y-coordinate location of the center of the ellipse. + The radius of the ellipse in the x-coordinate direction. + The radius of the ellipse in the y-coordinate direction. + + + Draws an ellipse filled with a specified color gradient. + The outline color. + The thickness of the ellipse's outline, in pixels. + The x-coordinate location of the center of the ellipse. + The y-coordinate location of the center of the ellipse. + The radius of the ellipse in the x-coordinate direction. + The radius of the ellipse in the y-coordinate direction. + The starting color of the color gradient. + The x-coordinate location of the starting point of the color gradient. + The y-coordinate location of the starting point of the color gradient. + The ending color of the color gradient. + The x-coordinate location of the ending point of the color gradient. + The y-coordinate location of the ending point of the color gradient. + The opacity of the ellipse. + + + Draws a rectangular block of pixels with a specified degree of transparency. + The x-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + The y-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + The source bitmap. + The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + The width of the rectangular block of pixels to be copied. + The height of the rectangular block of pixels to be copied. + The degree of opacity of the bitmap. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + + + Draws a rectangular block of pixels on the display device. + The x-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + The y-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + The source bitmap. + The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + The y-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + The width of the rectangular block of pixels to be copied. + The height of the rectangular block of pixels to be copied. + + + Draws a line on the display device. + The color of the line. + The thickness of the line, in pixels. + The x-coordinate location of the line's starting point. + The y-coordinate location of the line's starting point. + The x-coordinate location of the line's ending point. + The y-coordinate location of the line's ending point. + + + Draws a rectangle defined by two coordinate (x,y) pairs. + The x-coordinate of the upper-left corner of the rectangle. + The y-coordinate of the upper-left corner of the rectangle. + The x-coordinate of the lower-right corner of the rectangle. + The y-coordinate of the lower-right corner of the rectangle. + + + Draws a rectangle on the display device. + The color of the rectangle's outline. + The thickness of the rectangle's outline, in pixels. + The x-coordinate of the rectangle's upper-left corner. + The y-coordinate of the rectangle's upper-left corner. + The width of the rectangle, in pixels. + The height of the rectangle, in pixels. + The x-coordinate value of the corner radius used to produce rounded corners on the rectangle. + The y-coordinate value of the corner radius used to produce rounded corners on the rectangle. + The starting color for a color gradient. + Holds the x coordinate of the starting location of the color gradient. + Holds the y coordinate of the starting location of the color gradient. + Specifies the ending color of the color gradient. + Holds the x coordinate of the ending location of the color gradient. + Holds the y coordinate of the ending location of the color gradient. + Specifies the opacity of the fill color. Set to 0x00 for completely transparent. Set to 0xFF for completely opague. + + + Draws text in a specified rectangle. + The text to be drawn. This parameter contains the remaining text, or an empty string, if the complete text string did not fit in the specified rectangle. + The x-coordinate, relative to the rectangle, at which text drawing is to begin. + The y-coordinate, relative to the rectangle, at which text drawing is to begin. + The x-coordinate of the upper-left corner of the rectangle. + The y-coordinate of the upper-left corner of the rectangle. + The width of the rectangle. + The height of the rectangle. + Flags that specify the format of the text. + The color to be used for the text. + The font to be used for the text. + true if all of the specified text was drawn; false if this is not the case and the text parameter contains the remaining text that was not drawn. + + + + Draws text in a specified rectangle. + The text to be drawn. This parameter contains the remaining text, or an empty string, if the complete text string did not fit in the specified rectangle. + The x-coordinate of the upper-left corner of the rectangle. + The y-coordinate of the upper-left corner of the rectangle. + The width of the rectangle. + The height of the rectangle. + Flags that specify the format of the text. + The color to be used for the text. + The font to be used for the text. + + + Draws text in a specified rectangle. + The text to be drawn. This parameter contains the remaining text, or an empty string, if the complete text string did not fit in the specified rectangle. + The x-coordinate, relative to the rectangle, at which text drawing is to begin. + The y-coordinate, relative to the rectangle, at which text drawing is to begin. + The x-coordinate of the upper-left corner of the rectangle. + The y-coordinate of the upper-left corner of the rectangle. + The width of the rectangle. + The height of the rectangle. + Flags that specify the format of the text. + The font to be used for the text. + true if all of the specified text was drawn; false if this is not the case and the text parameter contains the remaining text that was not drawn. + + + Draws text in a specified rectangle. + The text to be drawn. This parameter contains the remaining text, or an empty string, if the complete text string did not fit in the specified rectangle. + The x-coordinate of the upper-left corner of the rectangle. + The y-coordinate of the upper-left corner of the rectangle. + The width of the rectangle. + The height of the rectangle. + Flags that specify the format of the text. + The font to be used for the text. + + + Draws text on the display device, using a specified font and color. + The text to be drawn. + The font to be used for the text. + The color to be used for the text. + The x-coordinate of the location where text drawing is to begin. + The y-coordinate of the location where text drawing is to begin. + + + Draws text on the display device, using a specified font. + The text to be drawn. + The x-coordinate of the location where text drawing is to begin. + The y-coordinate of the location where text drawing is to begin. + The font to be used for the text. + The width of the text, in pixels. + + + Draws text on the display device, using a specified font and kerning. + The text to be drawn. + The x-coordinate of the location where text drawing is to begin. + The y-coordinate of location where text drawing is to begin. + The font to be used for the text. + The kerning to be used for the text, in pixels. + The width of the text, in pixels. + + + Flushes the current bitmap to the display device. + + + Flushes the current bitmap to the display device. + The x-coordinate of the bitmap's upper-left corner. + The y-coordinate of the bitmap's upper-left corner. + The width of the bitmap. + The height of the bitmap. + + + Retrieves the pixel color at a specified location on the display device. + The x-coordinate of the location where pixel color is to be retrieved. + The y-coordinate of the location where pixel color is to be retrieved. + The pixel color at the specified location. + + + Draws a line connecting the two points specified by two coordinate (x,y) pairs. + The x-coordinate of the first point. + The y-coordinate of the first point. + The x-coordinate of the second point. + The y-coordinate of the second point. + + + Sets a bitmap's transparent color. + The color to be used as the bitmap's transparent color. + + + Combines the data for the source bitmap and the current (destination) bitmap, using the specified mask. + The x-coordinate of the upper-left corner of the destination rectangle. + The y-coordinate of the upper-left corner of the destination rectangle. + The width of the source and destination rectangles. + The height of the source and destination rectangles. + The source bitmap. + The x-coordinate of the upper-left corner of the source rectangle. + The y-coordinate of the upper-left corner of the source rectangle. + A mask that specifies how the bitmaps are to be combined. + + + Creates a bit pattern on a bitmap. + The x-coordinate of the upper-left corner of the destination rectangle. + The y-coordinate of the upper-left corner of the destination rectangle. + The width of the source and destination rectangles. + The height of the source and destination rectangles. + A raster operation code, which defines the pattern to be created on the bitmap. + + + Resets a bitmap's clipping region (clipping rectangle) to the entire bitmap. + + + Rotates a bitmap to a specified degree around a specified point. + The source bitmap. + The degree of rotation. + The x-coordinate of the center of the source bitmap. + The y-coordinate of the center of the source bitmap. + The x-coordinate of the center of the destination bitmap. + The y-coordinate of the center of the destination bitmap. + + + Sets the clipping region (clipping rectangle) of a bitmap with a specified coordinate pair (x,y), width, and height. + The x-coordinate of the upper-left corner of the clipping rectangle. + The y-coordinate of the upper-left corner of the clipping rectangle. + The width of the clipping rectangle. + The height of the clipping rectangle. + + + Sets the color for a specified pixel. + The x-coordinate of the pixel whose color you want to set. + The y-coordinate of the pixel whose color you want to set. + The color you want to set for the specified pixel. + + + Turns a specified pixel on or off. + The x-coordinate of the pixel you want to set. + The y-coordinate of the pixel you want to set. + The parameter that you can set to true to turn the pixel on, or set to false to turn the pixel off. + + + Draws a rectangular block of pixels on the display device, stretching or shrinking the rectangular area as necessary. + The x-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + The y-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + The source bitmap. + The width of the rectangluar area to which the pixels are to be copied. + The height of the rectangluar area to which the pixels are to be copied. + The bitmap's degree of opacity. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + + + Combines the data for the specified source bitmap and the current bitmap (destination bitmap) in a defined rectangular area. + The x-coordinate of the upper-left corner of the destination rectangle. + The y-coordinate of the upper-left corner of the destination rectangle. + The width of the source and destination rectangles. + The height of the source and destination rectangles. + The source bitmap. + The x-coordinate of the upper-left corner of the source rectangle. + The y-coordinate of the upper-left corner of the source rectangle. + + + Gets the height of the current bitmap. + The height of the current bitmap, in pixels. + + + Gets the width of the current bitmap. + The width of the current bitmap, in pixels. + + + + Initializes a new instance of the CancelEventArgs class. + + + Contains a value that indicates whether a specific event should be canceled. + + + Provides data for an event that can be canceled. + + + The exception that is thrown when an action is attempted that violates a constraint. + + + Causes a break in execution if the specified assertion (condition) evaluates to true. + The condition to be evaluated. If the return value is true, program execution stops. + The text to be displayed if the assertion is true. + The detailed message to be displayed if the assertion is true. + + + Causes a break in execution if the specified assertion (condition) evaluates to true. + The condition to be evaluated. If the return value is true, program execution stops. + + + Causes a break in execution if the specified assertion (condition) evaluates to true. + The condition to be evaluated. If the return value is true, program execution stops. + The text to be displayed if the assertion is true. + + + Prints the data for a specified buffer to the standard output stream. + An array containing the bytes of buffer data. + A Boolean value that specifies whether to compute and print the cyclic redundancy check (CRC) for each row of data. + A Boolean value that specifies whether to print the memory offset address for each row of data. + + + Prints all available heap information to the standard output stream. + + + Prints performance counter information to the standard output stream. + + + Prints all available stack information to the standard output stream. + + + Runs garbage collection, a service that automatically reclaims unused computer memory. + A Boolean value that specifies whether garbage collection should be forced. + The amount of free (unused) memory, in bytes. + + + Prints a message (followed by the current line terminator) to the standard output stream. + The message to be printed to the standard output stream. + + + Provides a set of methods and properties that help you debug your code. + + + Occurs when a Dispatcher object (a dispatcher) finishes shutting down. + + + Occurs when a Dispatcher object (a dispatcher) begins the shutdown process. + + + + Executes a specified delegate on the thread on which a particular Dispatcher object (dispatcher) was created. + + A delegate to a method that takes one argument, which is pushed into the dispatcher's event queue. + The object to be passed as an argument to the delegate. + An object, which is returned immediately after the BeginInvoke method is called, that can be used to interact with the delegate as it is pending execution in the event queue. + + + Determines whether the calling thread has access to the current Dispatcher object (dispatcher). + true if the calling thread has access to the current dispatcher; otherwise, false. + + + Gets the Dispatcher object (dispatcher) for a specified thread. + The thread from which the dispatcher is to be obtained. + The dispatcher for the specified thread. + + + Initiates the shutdown process for a Dispatcher object (dispatcher) synchronously. + + + Executes a specified delegate with a specified argument. + The maximum amount of time the program will wait for the operation to finish. + A delegate to a method that takes multiple arguments, which is pushed onto the Dispatcher object's event queue. + An object to be passed as an argument to the specified method. This can be a null reference if no arguments are needed. + The return value from the invoked delegate, or a null reference if the delegate has no return value. + + + Pushes an execution frame onto the event queue of a Dispatcher object (dispatcher). + The frame to be processed by the dispatcher. + + + Initiates the event queue of a Dispatcher object (dispatcher). + + + + Determines whether the calling thread has access to the current Dispatcher object (dispatcher). + + + Gets the Dispatcher object (dispatcher) for the thread that is currently executing and creates a new dispatcher if one is not already associated with the current thread. + + The dispatcher that is associated with the current thread. + + + Determines whether the current Dispatcher object (dispatcher) has finished shutting down. + + true if the current dispatcher has finished shutting down; otherwise, false. + + + + Determines whether the current Dispatcher object (dispatcher) is shutting down. + true if the current dispatcher has started shutting down; otherwise, false. + + + Gets the thread with which the current Dispatcher object (dispatcher) is associated. + + The thread that is associated with the current dispatcher. + + + Provides services for maintaining the event queue for a thread. + + + + Initializes a new instance of the DispatcherFrame class, using the specified exit request flag. + + Indicates whether the current dispatcher frame will exit when all frames are requested to exit. + + + + Initializes a new instance of the DispatcherFrame class. + + + + Gets or sets a value that indicates whether the current dispatcher frame should continue. + true if the dispatcher frame should continue; otherwise, false. + + + Represents an execution loop iteration in the dispatcher event queue. + + + Initializes a new instance of the DispatcherObject class. + + + Contains the Dispatcher object that the current DispatcherObject object is associated with. + + + + Determines whether the calling thread has access to the current DispatcherObject object. + true if the calling thread has access to the current object; otherwise, false. + + + + + Determines whether the calling thread has access to the current DispatcherObject object. + + + + Represents an object that is associated with a dispatcher. + + + Occurs when the delegate on the dispatcher queue associated with the current DispatcherOperation object is aborted. + + + + Occurs when the delegate on the dispatcher queue associated with the current DispatcherOperation object completes. + + + + Aborts the delegate on the dispatcher queue associated with the current DispatcherOperation object. + true if the operation was aborted; otherwise, false. + + + Waits for a specified amount of time for the delegate on the dispatcher queue to complete. + The maximum amount of time this method will wait. + The status of the operation. + + + + Waits for the delegate on the dispatcher queue associated with the current DispatcherOperation object to complete. + + The status of the operation. + + + Gets the dispatcher that the delegate associated with the current DispatcherOperation object was posted to. + The dispatcher that the delegate was posted to. + + + Gets the result of the delegate associated with the current DispatcherOperation object, if the operation has completed. + The result of the operation. + + + Gets the current operation status of the delegate on the dispatcher queue associated with the current DispatcherOperation object. + The status of the operation. + + + Enables programs to interact with a delegate that has been posted to a dispatcher queue. + + + + Initializes a new instance of the DispatcherTimer class. + + + + + Initializes a new instance of the DispatcherTimer class and associates it with a specified Dispatcher object. + + The dispatcher associated with the current timer. + + + Occurs when the specified time interval for the current DispatcherTimer object (timer) has elapsed and the timer is enabled. + + + Starts a DispatcherTimer object's timer. + + + + Stops a DispatcherTimer object's timer. + + + + Gets the Dispatcher object (dispatcher) associated with the current DispatcherTimer object (timer). + The dispatcher associated with the current timer. + + + Gets or sets the current DispatcherTimer object's defined time interval. + The time interval between the timer's ticks, in hours:minutes:seconds. The default value is 00:00:00. + + + Gets or sets a Boolean value that indicates whether a particular DispatcherTimer object (timer) is enabled — that is, currently running. + true if the timer is currently running; otherwise, false. The default value is false. + + + + Gets or sets a user-defined data object that is passed to the Tick event handler. + + The user-defined data object that is passed to the Tick event handler. The default value is a null reference. + + + A timer that is integrated into a dispatcher queue. + + + Initializes a new instance of the EventArgs class. + + + Gets an empty, read-only EventArgs object. + + An empty, read-only EventArgs object. This is equivalent to the result of calling the EventArgs constructor. + + + Constitutes the base class for classes that contain event data. + + + Initializes a new instance of the EventHandlersStore class. + + + + Adds a specified routed event handler for a specified RoutedEvent object to the collection of events in the current EventHandlersStore object. + The routed event. + The handler for the routed event. + A Boolean value that indicates whether the routed event handler should also process events that have already been handled. + + + Provides a container for event handlers. + + + Initializes a new instance of the EventRoute class. + + The event identifier (ID) to be associated with the current event route. Note that this parameter cannot be set to null. + + + Adds a specified handler for a specified target to the event route. + The target object for which a handler is to be added to the event route. + The handler to be added to the event route. + A value that indicates whether or not the listener detects events that have already been handled. + + + Provides a container for a route to be followed by a routed event. + + + Creates a subthread within the calling thread, containing a constraint that requires the calling thread to complete an operation within a specified time period and at a specified priority level. + The number of clock ticks before a ConstraintException exception is thrown. Note that the value -1 in this parameter indicates that the current constraint exception is to be uninstalled. + The priority level of the calling thread. + + + Provides a method you can use to require a thread to complete an operation within specific constraints. + + + Initializes a new instance of the ExtendedTimer class. + The callback method that is executed when the timer expires. + An object that contains application-specific information relevant to the methods invoked by the callback method, or a null reference. + The event on which the callback method is to be executed. + + + Initializes a new instance of the ExtendedTimer class, using System.DateTime and System.TimeSpan values to measure time intervals. + The callback method that is executed when the timer expires. + An object that contains application-specific information relevant to the methods invoked by the callback method, or a null reference. + The System.DateTime value that represents the amount of time the timer will wait before the callback method invokes its methods, in milliseconds. Specify System.Threading.Timeout.Infinite to prevent the timer from starting. Specify 0 (zero) to start the timer immediately. + The System.TimeSpan value that represents the time interval between invocations of the methods referenced by the callback method, in milliseconds. Specify System.Threading.Timeout.Infinite to disable periodic signaling. + + + Initializes a new instance of the ExtendedTimer class, using System.TimeSpan values to measure time intervals. + The callback method that is executed when the timer expires. + An object that contains application-specific information relevant to the methods invoked by the callback method, or a null reference. + The amount of time the timer will wait before the callback method invokes its methods, in milliseconds. Specify System.Threading.Timeout.Infinite to prevent the timer from starting. Specify 0 (zero) to start the timer immediately. + The time interval between invocations of the methods referenced by the callback method, in milliseconds. Specify System.Threading.Timeout.Infinite to disable periodic signaling. + + + Initializes a new instance of the ExtendedTimer class, using 32-bit signed integers to measure time intervals. + The callback method that is executed when the timer expires. + An object that contains application-specific information relevant to the methods invoked by the callback method, or a null reference. + The amount of time the timer will wait before the callback method invokes its methods, in milliseconds. Specify System.Threading.Timeout.Infinite to prevent the timer from starting. Specify 0 (zero) to start the timer immediately. + The time interval between invocations of the methods referenced by the callback method, in milliseconds. Specify System.Threading.Timeout.Infinite to disable periodic signaling. + + + Changes the start time and the interval between method invocations for a timer, using >System.TimeSpan values to measure time intervals. + The System.TimeSpan value that represents the amount of time the timer will wait before the callback parameter invokes its methods, in milliseconds. Specify System.Threading.Timeout.Infinite to prevent the timer from starting. Specify 0 (zero) to start the timer immediately. + + + The System.TimeSpan value that represents the time interval between invocations of the methods referenced by the delegate specified at System.Threading.Timer construction time, in milliseconds. Specify System.Threading.Timeout.Infinite to disable periodic signaling. + + + Changes the start time and the interval between method invocations for a timer, using System.DateTime and System.TimeSpan values to measure time intervals. + The System.DateTime value that represents the amount of time the timer will wait before the callback parameter invokes its methods, in milliseconds. Specify System.Threading.Timeout.Infinite to prevent the timer from starting. Specify 0 (zero) to start the timer immediately. + + + The System.TimeSpan value that represents the time interval between invocations of the methods referenced by the delegate specified at System.Threading.Timer construction time, in milliseconds. Specify System.Threading.Timeout.Infinite to disable periodic signaling. + + + Changes the start time and the interval between method invocations for a timer, using 32-bit signed integers to measure time intervals. + The amount of time the timer will wait before the delegate specified at System.Threading.Timer construction time invokes its methods, in milliseconds. Specify System.Threading.Timeout.Infinite to prevent the timer from restarting. Specify 0 (zero) to restart the timer immediately. + The time interval between invocations of the methods referenced by the delegate specified at System.Threading.Timer construction time, in milliseconds. Specify System.Threading.Timeout.Infinite to disable periodic signaling. + + + Releases all resources used by the current instance of the ExtendedTimer class. + + + Gets the System.TimeSpan value of the last expiration of the current timer. + The System.TimeSpan value of the last expiration of the current timer. + + + Provides a mechanism for executing methods at specified intervals or on specific events. + + + Specifies the number of clock ticks equal to the DateTime value of 2003/01/01 00:00:00. + + + Finds the daylight saving time period for a specified year. + The year to which the daylight saving time period applies. + A System.Globalization.DaylightTime value that contains the start and end dates for daylight saving time in the specified year. + + + Returns a time zone for a specified time zone ID. + The ID of the time zone you want to get. + A System.TimeZone value set to the time zone ID provided . + + + Finds the coordinated universal time (UTC) offset for the specified local time. + The local date and time. + The local time for which you want the UTC offset, measured in clock ticks. + + + Sets the time offset. + The amount of time you want to set the device system time ahead of the true time, in minutes. + + + Sets the extended information for a particular time zone. + The extended information for a particular time zone. + + + Sets the time zone. + The time zone you want to set the device system to. + + + Synchronizes the device system time with the network time, if the device system currently has a network connection. + The UTC time obtained from the network. + The device system time. + The ID of the time zone you want to synchronize to. + A System.TimeSpan value that is set to the difference between the network time and the device system time. + + + Gets the daylight saving time name for a particular time zone. + The daylight saving time name for a particular time zone. + + + Gets the standard name for a particular time zone. + The standard name for a particular time zone. + + + Gets the ID of a particular time zone. + The ID of a particular time zone. + + + Represents a time zone. + + + Initializes a new instance of the ExtendedWeakReference class, referencing a specified object. + The object targeted for the current weak reference. + The type that you want to associate with the current weak reference. + The indentifier that you want to associate with the current weak reference. + Flags that specify the states from which the current weak reference should be recoverable. + + + Contains a flag specifying that the current weak reference will be recoverable after the device reboots. + + + Contains a flag specifying that the current weak reference will be recoverable after the device powers down and restarts. + + + Flags an ExtendedWeakReference object as a candidate for recovery after the device reboots or powers down and restarts. + + + Attempts to recover a specific ExtendedWeakReference object, and creates a new instance of this class if the recovery attempt fails. + The type associated with the weak reference you want to recover. + The ID associated with the weak reference you want to recover. + The flags for the new ExtendedWeakReference object that will be created if the one you specified is not recovered. + The weak reference you specified, or a null reference if the recovery attempt failed. + + + Recovers a specific ExtendedWeakReference object. + The type associated with the weak reference you want to recover. + The ID associated with the weak reference you want to recover. + A weak reference, or a null reference if there are no more weak references to be recovered. + + + Gets the flags specifying the states from which the current weak reference should be recoverable. + The recovery flags for the current weak reference. + + + Gets the ID associated with the current ExtendedWeakReference object. + The ID for the current weak reference. + + + Gets or sets the priority level for the current ExtendedWeakReference object. + The priority level for the current weak reference. + + + + Gets the selector for the current ExtendedWeakReference object. + The selector for the current weak reference. + + + Represents an extended weak reference, which references an object but still leaves it prone to garbage collection. + + + Constructs a FieldNoReflectionAttribute object. + + + Sets a field such that there can be no reflection it. + + + Contains the default kerning for a particular font. + + + Gets the width of the specified character, in pixels. + The character whose width you want to find. + The width of the specified character, in pixels. + + + Computes the width, height, and kerning of a specified line of text. + The text you want to measure. + The width of the specified text. + The height of the specified text. + The spacing between consecutive characters. + + + Computes the width and height of a specified line of text. + The text you want to measure. + The width of the specified text. + The height of the specified text. + + + Computes the size of the rectangular area on the display device needed to render the specified text string. + The text you want to render on the display device. + The width, in pixels, of the rectangular area needed to render the text. + + The height, in pixels, of the rectangular area needed to render the text. + + The x-coordinate of the relative starting point for the text. + The y-coordinate of the relative starting point for the text. + The maximum width of text that will fit in the defined rectangular area. + The maximum height of text that will fit in the defined rectangular area. + Flags that specify various text characteristics, such as alignment. + + + Computes the size of the rectangular area on the display device needed to render the specified text string. + The text you want to render on the display device. + The width, in pixels, of the rectangular area needed to render the text. + The height, in pixels, of the rectangular area needed to render the text. + The maximum width of text that will fit in the defined rectangular area. + + + Computes the size of the rectangular area on the display device needed to render the specified text string. + The text you want to render on the display device. + The width, in pixels, of the rectangular area needed to render the text. + + The height, in pixels, of the rectangular area needed to render the text. + + + + Gets the ascent measurement for the current font, in pixels. + The ascent measurement for the current font, in pixels. + + + Gets the average width of the characters in the current font, in pixels. + The average width of the characters in the current font, in pixels. + + + Gets the descent measurement for the current font, in pixels. + The descent measurement for the current font, in pixels. + + + Gets the external leading measurement for the current font, in pixels. + The external leading measurement for the current font, in pixels. + + + Gets the height of the current font, in pixels. + The height of the current font, in pixels. + + + Gets the internal leading measurement for the current font, in pixels. + The internal leading measurement for the current font, in pixels. + + + Gets the width of the widest character in the current font, in pixels. + The width of the widest character in the current font, in pixels. + + + Defines a particular format for text, including font choice, font size, and style attributes. + + + Indicates that a particular method is globally synchronized. + + + Adds a Logging object (log record) at the end of the list of log records. + The log record you want to add at the end of the list. + + + Erases all Logging objects (log records). + + + Gets a Logging object (log record) from the zero-based list of log records. + The index number of the log record you want to retrieve. + A log record. + + + Gets the number of Logging objects (log records) in the current list of records. + The number of log records in the current list of records. + + + Provides methods and properties for managing logged data. + + + Gets the cosine of the specified angle. + An angle, measured in degrees. + The cosine of the specified angle, multiplied by 1000. + + + Sets a random starting point for random number generation. + + + Generates a pseudorandom number. + A modulo to apply to the pseudorandom number that is generated. Note that the generated number is greater than or equal to 0 (zero) and less than the modulo. + A pseudorandom number. + + + Gets the sine of the specified angle. + An angle, measured in degrees. + The sine of the specified angle, multiplied by 1000. + + + Provides static methods for common mathematical functions. + + + Initializes a new instance of the PropertyChangedEventArgs class. + + The name of the property that changes. + The value of the specified property before the change. + The value of the specified property after the change. + + + Contains the value of the specified property after the change. + + + Contains the value of the specified property before the change. + + + Contains the name of the property that changes. + + + Provides data for a property changed event. + + + Creates and initializes a new instance of the PublishInApplicationDirectoryAttribute class. + + + Indicates that an application should be published in its application directory. + + + Creates and initializes a new instance of the Reflection class. + + + Deserializes a byte array of data into an object of the specified type. + A byte array containing data that represents the serialized object. + The type of the object to be deserialized. This should be a null reference if the array in the v parameter contains that type. + The deserialized object represented by the byte array. + + + Gets all known assemblies. + An array containing all known assemblies. + + + Gets the assembly with the specified hash attribute. + The hash attribute for the assembly you want to get. + The assembly with the specified hash attribute. + + + Retrieves the hash attribute from a specified assembly. + The assembly you want to retrieve the hash attribute from. + The hash attribute that represents the specified assembly. + + + Get an AssemblyInfo object from a byte array that represents a specified assembly. + The byte array that represents the specified assembly. + The assembly to be populated with data extracted from the byte array. + true if the AssemblyInfo object contains valid data extracted from the byte array; otherwise, false. + + + Gets a System.TimeSpan value that represents the time at which the specified assembly was last used. + The specified assembly. + The time at which the specified assembly was last used. + + + Gets the assembly that is designated as a patch for the specified assembly. + The specified assembly. + The patch assembly for the specified assembly. + + + Gets the type that uses the specified hash attribute. + The hash attribute for the specified type. + The type that corresponds to the specified hash attribute. + + + Gets the hash attribute for a specified type. + The type you want to retrieve the hash attribute from. + The hash attribute that represents the specified type. + + + Gets the System.Type objects (types) that implement the specified interface. + The type of the interface you want to search for. + An array of types. + + + Indicates whether a specified type is loaded. + The type you want to check for. + true if the specified type is loaded; otherwise, false. + + + Serializes an object of the specified type to a byte array of data. + The object you want to serialize. + The type of the object you want to serialize. This parameter can be a null reference. + A byte array containing data that represents the serialized object. + + + Provides information about assemblies and the types defined within them, as well as information about creating, invoking, and accessing type instances at run time. + + + Creates and initializes a new instance of the AssemblyInfo class. + + + Contains flags that describe the current assembly. + + + Contains the assembly hash attribute, which is a unique identifier for the current assembly. + + + Contains the name of the current assembly. + + + Contains a flag stating that after the current assembly is installed, the device must be rebooted. + + + Contains a flag stating that the current assembly is a patch for another assembly. + + + Contains an array of references to assemblies that the current assembly is dependent on. + + + Specifies the size of the current assembly. + + + Defines an AssemblyInfo object (an assembly), which is a self-describing building block of a common language runtime (CLR) application. + + + Constructs a ResourceUtility object. + + + Retrieves the string representations of the specified resources. + The resource manager that contains the specified resources. + An enumerated value that specifies the type of the resources for which you want to get the string representations. + The string representations of the specified resources. + + + Retrieves the string representation of a specified resource. + The resource manager that contains the specified resource. + An enumerated value that indicates the type of the specified resource. + The index number of the resource (in the resource manager's resource collection) for which you want to get the string representation. + The string representation of the specified resource. + + + Gets the value of a specified Object resource for the current system culture. + The resource manager that contains the specified resource. + The integer identifier for the specified resource. + The value of the specified resource for the current system culture. + + + Sets the information for the current system culture. + The object that contains the information for the current system culture. + + + Helps .NET Micro Framework applications manage resources. + + + Initializes a new instance of the RoutedEvent class. + + The identifying name of the routed event. + The routing strategy of the routed event. + The handler type of the routed event. + + + Retrieves a string representation of the current RoutedEvent object. + A string representation of the current routed event. Note that this string is identical to the value returned by the Name property. + + + Gets the handler type of the current RoutedEvent object. + + The handler type of the current routed event. + + + Gets the identifying name of the current RoutedEvent object. + The identifying name of the current routed event. + + + Gets the routing strategy of the current Routed Event object. + A value from the RoutingStrategy enumeration. + + + + Represents and identifies a routed event and declares its characteristics. + + + + Initializes a new instance of the RoutedEventArgs class, using the routed event identifier that is provided. + + + The routed event identifier for the current RoutedEventArgs object. + + + + Initializes a new instance of the RoutedEventArgs class, using the routed event identifier that is provided. Note that using this constructor also gives you the opportunity to declare a different source for the event. + The routed event identifier for the current RoutedEventArgs object. + An alternate source that will be reported when the routed event is handled. + + + + Initializes a new instance of the RoutedEventArgs class. + + + + Provides a notification callback entry point whenever the value of the Source property of an object changes. + Contains the object whose Source property triggers the invocation of this method. + + + Gets or sets a value that indicates the present state of the event handling for a particular routed event as it travels the route. + When setting this property: true if the routed event is to be marked handled; otherwise false. When reading this property: true if a class handler or some instance handler along the route has already marked this event handled; otherwise, false. Note that the default value is false. + + + + Gets the orginal object that raised the current routed event. + + The original reporting source, before any possible adjustments could be made to the Source property by class handling. + + + Gets or sets a routed event. + A routed event. + + + Gets or sets a reference to the object that raised the current routed event. + The object that raised the current routed event. + + + Provides state information and event data associated with a particular routed event. + + + Constructs event routes. + + + Creates a new RpcClient object. + The type to initialize the RPC client with. + The ID to initialize the RPC client with. + + + Invokes a specified method. + The name of the method to invoke. + The parameters to the method to invoke. + The return value of the method that is invoked. + + + Gets a Boolean value that indicates whether or not the RPC server is alive. + true if the server is alive; otherwise, false. + + + Provides a client for remote procedure calls. + + + Creates a new RpcServer object. + The type to initialize the RPC server with. + The ID to initialize the RPC server with. + The target of the RPC server. + The type of the remoted methods. + + + Starts the RPC Server. + + + Stops the RPC Server. + + + Provides a server for remote procedure calls. + + + Specifies the size of a particular serialized array. + + + Specifies the number of bits in which the current object is bit-packed. + + + Contains flags that describe the serialization of the current object. + + + Specifies the range bias adjustment for a particular serialized value. + + + Specifies the scale adjustment for a particular serialized value. + + + Indicates that a particular serializable object has known attributes and methods of serialization. + + + For internal use only. Please use Socket instead. + + + Constructs a SystemTime object. + + + Specifies the day of the week associated with a particular SystemTime object. + + + Specifies the day associated with a particular SystemTime object. + + + Specifies the hour associated with a particular SystemTime object. + + + Specifies the milliseconds associated with a particular SystemTime object. + + + Specifies the minute associated with a particular SystemTime object. + + + Specifies the month associated with a particular SystemTime object. + + + Specifies the second associated with a particular SystemTime object. + + + Specifies the year associated with a particular SystemTime object. + + + Sets or gets the current system time for the device. + + + Constructs a TimeZoneInformation object. + + + Specifies the current bias for local time translation on this operating system, in minutes. + + + Specifies the bias value to be used for local time translations that occur during daylight saving time in a particular time zone. + + + Specifies a date and a local time when the transition from standard time to daylight saving time will occur on this operating system. + + + Contains a description of daylight saving time. + + + Specifies the bias value to be used for local time translations that occur during standard time in a particular time zone. + + + Contains a date and a local time when the transition from daylight saving time to standard time will occur on this operating system. + + + Contains a description of standard time. + + + Contains information describing a specific time zone. + + + Contains a reference to the unknown type that caused the current exception. + + + The exception that is thown when an error occurs because of an unknown type. + + + Creates and initializes an instance of the WeakDelegate class. + + + Concatenates the invocation lists of two multicast (combinable) delegates. + The combinable delegate whose invocation list comes first. + The combinable delegate whose invocation list comes second. + A new combinable delegate with an invocation list that concatenates the invocation lists of the a and b parameters, in that order. If b is a null reference, this method returns a. If a is a null reference, this method returns b. If both a and b are null references, this method returns null. + + + + Removes the invocation list of a specified delegate from the invocation list of another specified delegate. + The delegate from which you want to remove the invocation list. + The delegate that supplies the invocation list you want to remove from the delegate specified in a. + A new delegate with an invocation list formed by taking the invocation list of the a parameter and removing the invocation list of the b parameter, if b's invocation list is found + in a's invocation list. If b is a null reference, or if b's invocation list is not found in a's invocation list, this method returns a. If b's invocation list is equal to a's invocation list, or if b is a null reference, this method returns null. + + + Provides methods for combining and removing "weak" delegates. + + + Initializes a new instance of the WindowCollection class. + + + + Copies Window objects from a window collection to a specified array, starting at a specified index number in the window collection. + + + An array of Window objects that is the destination of the copy operation. + + The starting index number in the window collection where copying is to begin. + + + Returns an IEnumerator interface that your program can use to enumerate the Window objects in a window collection. + An IEnumerator interface that your program can use to enumerate the Window objects in a window collection. + + + + Gets the number of Window objects in a particular window collection. + + An integer that specifies the number of Window objects in the window collection. + + + + Gets a value that indicates whether a particular window collection is thread-safe. + true if the window collection is thread-safe; otherwise, false. + + + Gets the Window object at a specified index number in a particular window collection. + The Window object at the specified index number in the window collection. + + + Gets an object that you can use to synchronize access to a particular window collection. + An object that you can use to synchronize access to the window collection. + + + Collects the window objects for an application into a single set, or window collection. + + + Provides the method that handles a cancelable event. + The sender (source) of the event. + Data describing the event. + + + Provides callback functionality for dispatcher operations. + An argument that is passed to the callback method. The value of this parameter can be null. + The object returned by the callback method. This value can be null. + + + + Provides a method that handles events that have no event data. + The sender (source) of the event. + A parameter that contains no event data. + + + Handles a property change event. + The object whose property is changing. + Information about the property change. + + + Represents an event handler that receive a routed event. + The sender of the routed event. + Event arguments that contain information about the routed event. + + + + Lists the available formats for bitmap images. + A bitmap in Windows BMP format. + A bitmap in GIF format. + A bitmap in JPEG format. + A bitmap in a format specific to the .NET Micro Framework common language runtine (CLR). + + + Describes the possible values for the status of a DispatcherOperation object. + A value indicating that the operation has aborted. + A value indicating that the operation has completed. + A value indicating that the operation has started executing but has not completed. + A value indicating that the operation is pending. + + + Defines a set of constants available for prioritizing extended weak references. + Critical data. + Data that is important, but not critical. + Data that is nice to have, but not critical. + Data that can be safely thrown away. + System data that is mandatory for basic operation. + + + Specifies the reason for the end of the session. + A value specifying that the user has logged off. + A value specifying that the user has shut the system down. + + + Indicates the routing strategy of a routed event. + A value indicating that the event uses a "bubbling" strategy, in which the event instance moves up the tree. + A value indicating that the event does not support routing. + A value indicating that the event uses a "tunneling" strategy, in which the event instance moves down the tree. + + + Defines a set of flags that indicate various attributes of a serialization (serialized object). + A value indicating that the serialized object is compressed. This flag is not currently implemented. + This flag is not currently implemented. + A value indicating that the elements in the serialized object are never null. + A value indicating that the serialized object is encrypted. This flag is not currently implemented. + A value indicating that the serialized object can only be an instance of the specified class, and not an instance of a derived class. + A value indicating that the serialized object can be skipped if it cannot be deserialized. This flag is not currently implemented. + A value indicating that the pointer to the serialized object is never null. + + + Defines how an application shuts down. + A value that causes the application to shut down when your program explicitly calls the Application.Shutdown method. + A value that causes the application to shut down when the last window closes or when your program explicitly calls the Application.Shutdown method. Note that this is the default setting. + A value that causes the application to shut down when the main window closes or when your program explicitly calls the Application.Shutdown method. + + + Defines the various timer events that are available in the .NET Micro Framework. + An event that occurs on a change in days. + An event that occurs on a change in hours. + An event that occurs on a change in minutes. + An event that occurs on a change in seconds. + An event that occurs at a specified time. + An event that occurs on a change in time zones. + + + Provides the set of time zones that are available for .NET Micro Framework applications. + Abu Dhabi + Adelaide + Alaska + Almaty + Arizona + Astana + Athens + Atlantic + Azores + Baghdad + Baku + Bangkok + Beijing + Berlin + Bogota + Brasilia + Brisbane + Bucharest + Buenos Aires + Cairo + Cape Verde + Caracas + Casablanca + Central America + Central + The total number of time zone IDs + The current time zone ID + Darwin + A string resource + Eastern + Fiji Islands + The first time zone ID + The first resource string + GMT, or Greenwich Mean Time (also known as UTC, or Coordinated Universal Time) + Greenland + Guam + Hawaii + Helsinki + Hobart + Indiana + Islamabad + Jerusalem + Kabul + Kathmandu + Krasnoyarsk + The last time zone ID + London + Magadan + Malaysia + Mexico City + Mid-Atlantic + Moscow + Mountain + Nairobi + The network-provided time zone + New Delhi + Newfoundland + New Zealand + Pacific + Paris + Perth + Prague + Pretoria + Riyadh + Samoa + Santiago + Saskatchewan + Seoul + Sri Lanka + Sydney + Taipei + Tehran + Tokyo + Tonga + Ulaanbataar + Vladivostok + Warsaw + West Central Africa + Yakutsk + Yangon + Yekaterinburg + + + Application クラスの新規インスタンスを初期化します。 + + + アプリケーションが終了する直前に発生します。 + + + Application オブジェクトの Run メソッドが呼び出されると発生します。 + + + Exit イベントを発生させます。 + Exit イベントのイベント データが含まれた EventArgs オブジェクト。 + + + Startup イベントを発生させます。 + Startup イベントのイベント データが含まれた EventArgs オブジェクト。 + + + Windows Presentation Foundation (WPF) アプリケーションを起動し、指定されたウィンドウを開きます。 + WPF アプリケーションの動作開始時に自動的に開くウィンドウを表す Window オブジェクト。 + + + Windows Presentation Foundation (WPF) アプリケーションを起動します。 + + + アプリケーションを終了させます。 + + + 現在のアプリケーション ドメインの Application オブジェクトを取得します。 + + +現在の AppDomain オブジェクトの Application オブジェクト。 + + + + 現在のアプリケーションのメイン ウィンドウを取得または設定します。 + 現在のアプリケーションのメイン ウィンドウを表す Window オブジェクト。 + + + Shutdown メソッドが呼び出される条件を取得または設定します。 + ShutdownMode 列挙型の値。既定の戻り値は、OnLastWindowClose です。 + + + 現在のアプリケーション内の、インスタンスが作成されたウィンドウを取得します。 + +現在のアプリケーション内のすべてのウィンドウ オブジェクトへの参照を含む WindowCollection オブジェクト。 + + + + Windows Presentation Foundation (WPF) アプリケーションをカプセル化します。 + + + Bitmap クラスの新規インスタンスを、指定された高さと幅に初期化します。 + 指定されたイメージのピクセル データの配列。 + 指定されたイメージのビットマップ型。 + + + Bitmap クラスの新規インスタンスを、指定された高さと幅に初期化します。 + 現在のビットマップの幅。ピクセル単位。 + 現在のビットマップの高さ。ピクセル単位。 + + + テキストがビットマップの周囲に中央揃えで表示されるように指定します。 + + + テキストがビットマップの周囲に左揃えで表示されるように指定します。 + + + マスクを使用して、ビットマップ周囲のテキスト揃えを取得または設定できるように指定します。 + + + テキストがビットマップの周囲に右揃えで表示されるように指定します。 + + + コピー先四角形を、物理パレットのインデックス番号 0 と関連付けられている色で塗りつぶします。 + + + 表示デバイスの中心となる x 座標の位置をピクセル単位で表します。 + + + 表示デバイスの中心となる y 座標の位置をピクセル単位で表します。 + + + コピー先の四角形を濃い灰色で塗りつぶします。 + + + コピー先の四角形を灰色で塗りつぶします。 + + + コピー先の四角形を反転させます。 + + + コピー先の四角形を薄い灰色で塗りつぶします。 + + + ビットマップのテキストが最も近い文字にトリムされ、トリムされた各行の末尾に省略記号が挿入されるよう指定します。 + + + ビットマップ テキストが提供されている領域より大きい場合、テキストを Height プロパティの値に合わせて縮小するのではなく、フル サイズで描画することを指定します。 + + + ディスプレイ デバイスの最大高さ (ピクセル単位) を指定します。 + + + ディスプレイ デバイスの最大幅 (ピクセル単位) を指定します。 + + + 書式ルールが存在しないことを指定します。 + + + 現在のビットマップが不透明であることを指定します。 + + + 現在のビットマップが透明であることを指定します。 + + + ソースの四角形を反転させます。 + + + コピー先の四角形または円を、ランダムに生成されたパターンで塗りつぶします。 + + + 円は単一ピクセルの境界線のみを持つべきであり、塗りつぶしパターンや色を持つべきでないことを指定します。 + + + コピー元四角形をコピー先四角形に直接コピーします。 + + + +ビットマップ テキストが提供された領域より大きい場合、テキストを末尾で切り捨てることを指定します。 + + + + コピー先四角形を、物理パレットのインデックス番号 1 と関連付けられている色で塗りつぶします。 + + + ビットマップ テキストの行がその最大幅に達したとき、次の行の先頭に単語を自動的にラップするかどうか指定します。 + + + 四角形のピクセルに対応するビットマップ データのビットブロック転送を実行し、指定されたコピー元ビットマップから現在のコピー先ビットマップにデータを転送します。 + コピー先四角形の左上隅の x 座標。 + コピー先四角形の左上隅の y 座標。 + コピー元およびコピー先の四角形の幅。 + コピー元およびコピー先の四角形の高さ。 + コピー元のビットマップ。 + コピー元四角形の左上隅の x 座標。 + コピー元四角形の左上隅の y 座標。 + ラスタ操作コード。このコードは、コピー元四角形のビットマップ データを、コピー先四角形のビットマップ データにどのように結合するかを定義します。 + + + 指定されたラスタ操作によって、座標の組 (x および y) と半径で指定された円を描画します。 + 円の中心の x 座標。 + 円の中心の y 座標。 + 円の半径。 + ラスタ操作コード。 + + + 描画サーフェイス全体をクリアします。 + + + ディスプレイ デバイス上に塗りつぶされた楕円を描画します。 + 輪郭線の色。 + 楕円の中心の x 座標の位置。 + 楕円の中心の y 座標の位置。 + x 座標方向の楕円の半径。 + y 座標方向の楕円の半径。 + + + 指定された色のグラデーションで塗りつぶされた楕円を描画します。 + 輪郭線の色。 + 楕円の輪郭線の太さ (ピクセル単位)。 + 楕円の中心の x 座標の位置。 + 楕円の中心の y 座標の位置。 + x 座標方向の楕円の半径。 + y 座標方向の楕円の半径。 + 色のグラデーションの開始色。 + 色のグラデーションの開始点の x 座標の位置。 + 色のグラデーションの開始点の y 座標の位置。 + 色のグラデーションの終了色。 + 色のグラデーションの終了点の x 座標の位置。 + 色のグラデーションの終了点の y 座標の位置。 + 楕円の不透明度。 + + + 指定された透明度で、四角形ブロックのピクセルを描画します。 + 指定されたピクセルがコピーされる、ディスプレイ上の四角形領域の左上隅 x 座標の位置。 + 指定されたピクセルがコピーされる、ディスプレイ上の四角形領域の左上隅 y 座標の位置。 + コピー元のビットマップ。 + 指定されたピクセルがコピーされる、コピー元ビットマップ内の四角形領域の左上隅 x 座標の位置。 + 指定されたピクセルがコピーされる、コピー元ビットマップ内の四角形領域の左上隅 x 座標の位置。 + コピーされるピクセルの四角形ブロックの幅。 + コピーされるピクセルの四角形ブロックの高さ。 + ビットマップの不透明度。値が 0 (ゼロ) の場合、ビットマップは完全に不透明 (まったく非透過的) になります。値が 255 の場合、ビットマップは完全に透過になります。 + + + ディスプレイ デバイス上に四角形ブロックのピクセルを描画します。 + 指定されたピクセルがコピーされる、ディスプレイ上の四角形領域の左上隅 x 座標の位置。 + 指定されたピクセルがコピーされる、ディスプレイ上の四角形領域の左上隅 y 座標の位置。 + コピー元のビットマップ。 + 指定されたピクセルがコピーされる、コピー元ビットマップ内の四角形領域の左上隅 x 座標の位置。 + 指定されたピクセルがコピーされる、コピー元ビットマップ内の四角形領域の左上隅 y 座標の位置。 + コピーされるピクセルの四角形ブロックの幅。 + コピーされるピクセルの四角形ブロックの高さ。 + + + ディスプレイ デバイス上に線を描画します。 + 線の色。 + 線の太さ。ピクセル単位。 + 線の開始点の x 座標の位置。 + 線の開始点の y 座標の位置。 + 線の終了点の x 座標の位置。 + 線の終了点の y 座標の位置。 + + + 2 つの座標 (x、y) の組で定義される四角形を描画します。 + 四角形の左上隅の x 座標。 + 四角形の左上隅の y 座標。 + 四角形の右下隅の x 座標。 + 四角形の右下隅の y 座標。 + + + ディスプレイ デバイス上に四角形を描画します。 + 四角形の輪郭線の色。 + 四角形の輪郭線の太さ (ピクセル単位)。 + 四角形の左上隅の x 座標。 + 四角形の左上隅の y 座標。 + 四角形の幅。ピクセル単位。 + 四角形の高さ。ピクセル単位。 + 四角形の角を丸くするために使用される角の半径の x 座標の値。 + 四角形の角を丸くするために使用される角の半径の y 座標の値。 + 色のグラデーションの開始色。 + 色のグラデーションの開始位置の x 座標を保持します。 + 色のグラデーションの開始位置の y 座標を保持します。 + 色のグラデーションの終了色を指定します。 + 色のグラデーションの終了位置の x 座標を保持します。 + 色のグラデーションの終了位置の y 座標を保持します。 + 塗りつぶしの色の不透明度を指定します。完全に透過的にするには、0x00 に設定します。完全に不透明にするには、0xFF に設定します。 + + + 指定された四角形内にテキストを描画します。 + 描画されるテキスト。完全な文字列が指定された四角形内に収まらなかった場合、このパラメータには、残りのテキスト、または空の文字列が含まれます。 + テキスト描画の開始場所の x 座標 (四角形との相対座標)。 + テキスト描画の開始場所の y 座標 (四角形との相対座標)。 + 四角形の左上隅の x 座標。 + 四角形の左上隅の y 座標。 + 四角形の幅。 + 四角形の高さ。 + テキストの形式を指定するフラグ。 + テキストに使用される色。 + テキストに使用されるフォント。 + 指定されたテキストがすべて描画された場合、true。それ以外の場合で、描画されなかった残りのテキストが text パラメータに含まれている場合、false。 + + + + 指定された四角形内にテキストを描画します。 + 描画されるテキスト。完全な文字列が指定された四角形内に収まらなかった場合、このパラメータには、残りのテキスト、または空の文字列が含まれます。 + 四角形の左上隅の x 座標。 + 四角形の左上隅の y 座標。 + 四角形の幅。 + 四角形の高さ。 + テキストの形式を指定するフラグ。 + テキストに使用される色。 + テキストに使用されるフォント。 + + + 指定された四角形内にテキストを描画します。 + 描画されるテキスト。完全な文字列が指定された四角形内に収まらなかった場合、このパラメータには、残りのテキスト、または空の文字列が含まれます。 + テキスト描画の開始場所の x 座標 (四角形との相対座標)。 + テキスト描画の開始場所の y 座標 (四角形との相対座標)。 + 四角形の左上隅の x 座標。 + 四角形の左上隅の y 座標。 + 四角形の幅。 + 四角形の高さ。 + テキストの形式を指定するフラグ。 + テキストに使用されるフォント。 + 指定されたテキストがすべて描画された場合、true。それ以外の場合で、描画されなかった残りのテキストが text パラメータに含まれている場合、false。 + + + 指定された四角形内にテキストを描画します。 + 描画されるテキスト。完全な文字列が指定された四角形内に収まらなかった場合、このパラメータには、残りのテキスト、または空の文字列が含まれます。 + 四角形の左上隅の x 座標。 + 四角形の左上隅の y 座標。 + 四角形の幅。 + 四角形の高さ。 + テキストの形式を指定するフラグ。 + テキストに使用されるフォント。 + + + 指定されたフォントと色を使用して、ディスプレイ デバイス上にテキストを描画します。 + 描画されるテキスト。 + テキストに使用されるフォント。 + テキストに使用される色。 + テキスト描画の開始場所の x 座標。 + テキスト描画の開始場所の y 座標。 + + + 指定されたフォントを使用して、ディスプレイ デバイス上にテキストを描画します。 + 描画されるテキスト。 + テキスト描画の開始場所の x 座標。 + テキスト描画の開始場所の y 座標。 + テキストに使用されるフォント。 + テキストの幅。ピクセル単位。 + + + 指定されたフォントとカーニングを使用して、ディスプレイ デバイス上にテキストを描画します。 + 描画されるテキスト。 + テキスト描画の開始場所の x 座標。 + テキスト描画の開始場所の y 座標。 + テキストに使用されるフォント。 + テキストに使用されるカーニング。ピクセル単位。 + テキストの幅。ピクセル単位。 + + + 現在のビットマップをディスプレイ デバイスにフラッシュします。 + + + 現在のビットマップをディスプレイ デバイスにフラッシュします。 + ビットマップの左上隅の x 座標。 + ビットマップの左上隅の y 座標。 + ビットマップの幅。 + ビットマップの高さ。 + + + ディスプレイ デバイス上の指定された位置にあるピクセルの色を取得します。 + ピクセルの色が取得される位置を表す x 座標。 + ピクセルの色が取得される位置を表す y 座標。 + 指定された位置のピクセルの色。 + + + 2 つの座標 (x、y) の組で指定される 2 つの点を結ぶ線を描画します。 + 1 つ目の点の x 座標。 + 1 つ目の点の y 座標。 + 2 つ目の点の x 座標。 + 2 つ目の点の y 座標。 + + + ビットマップの透明色を設定します。 + ビットマップの透明色として使用される色。 + + + 指定されたマスクを使用して、コピー元のビットマップと現在 (コピー先) のビットマップを結合します。 + コピー先四角形の左上隅の x 座標。 + コピー先四角形の左上隅の y 座標。 + コピー元およびコピー先の四角形の幅。 + コピー元およびコピー先の四角形の高さ。 + コピー元のビットマップ。 + コピー元四角形の左上隅の x 座標。 + コピー元四角形の左上隅の y 座標。 + ビットマップの結合方法を指定するマスク。 + + + ビットマップ上にビット パターンを作成します。 + コピー先四角形の左上隅の x 座標。 + コピー先四角形の左上隅の y 座標。 + コピー元およびコピー先の四角形の幅。 + コピー元およびコピー先の四角形の高さ。 + ビットマップ上に作成するパターンを定義するラスタ操作コード。 + + + ビットマップのクリッピング領域 (クリッピング四角形) をビットマップ全体にリセットします。 + + + 指定された点を中心に、指定された角度、ビットマップを回転させます。 + コピー元のビットマップ。 + 回転の角度。 + コピー元ビットマップの中心の x 座標。 + コピー元ビットマップの中心の y 座標。 + コピー先ビットマップの中心の x 座標。 + コピー先ビットマップの中心の y 座標。 + + + ビットマップのクリッピング領域 (クリッピング四角形) に、指定された座標の組 (x、y)、幅、および高さを設定します。 + クリッピング四角形の左上隅の x 座標。 + クリッピング四角形の左上隅の y 座標。 + クリッピング四角形の幅。 + クリッピング四角形の高さ。 + + + 指定されたピクセルの色を設定します。 + 色を設定するピクセルの x 座標。 + 色を設定するピクセルの y 座標。 + 指定されたピクセルに設定する色。 + + + 指定されたピクセルをオンまたはオフにします。 + 設定するピクセルの x 座標。 + 設定するピクセルの y 座標。 + true に設定してピクセルをオンにする、または false に設定してピクセルをオフにするパラメータ。 + + + 必要に応じ四角形の領域を伸縮して、ディスプレイ デバイス上でピクセルの四角形ブロックを描画します。 + ピクセルがコピーされる四角形領域の左上隅の x 座標。 + ピクセルがコピーされる四角形領域の左上隅の y 座標。 + コピー元のビットマップ。 + ピクセルがコピーされる四角形領域の幅。 + ピクセルがコピーされる四角形領域の高さ。 + ビットマップの不透明度。値が 0 (ゼロ) の場合、ビットマップは完全に不透明 (まったく非透過的) になります。値が 255 の場合、ビットマップは完全に透過になります。 + + + 定義された四角形領域内で、指定されたコピー元ビットマップと現在のビットマップ (コピー先ビットマップ) のデータを組み合わせます。 + コピー先四角形の左上隅の x 座標。 + コピー先四角形の左上隅の y 座標。 + コピー元およびコピー先の四角形の幅。 + コピー元およびコピー先の四角形の高さ。 + コピー元のビットマップ。 + コピー元四角形の左上隅の x 座標。 + コピー元四角形の左上隅の y 座標。 + + + 現在のビットマップの高さを取得します。 + 現在のビットマップの高さ。ピクセル単位。 + + + 現在のビットマップの幅を取得します。 + 現在のビットマップの幅。ピクセル単位。 + + + + CancelEventArgs クラスの新規インスタンスを初期化します。 + + + 特定のイベントをキャンセルするかどうかを示す値を含みます。 + + + キャンセル可能なイベントのデータを提供します。 + + + 制約に違反するアクションを実行しようとしたときにスローされる例外。 + + + 指定されたアサーション (条件) の評価結果が true の場合、実行が停止します。 + 評価される条件。戻り値が true の場合、プログラムの実行は停止します。 + アサーションが true の場合に表示されるテキスト。 + アサーションが true の場合に表示される詳細メッセージ。 + + + 指定されたアサーション (条件) の評価結果が true の場合、実行が停止します。 + 評価される条件。戻り値が true の場合、プログラムの実行は停止します。 + + + 指定されたアサーション (条件) の評価結果が true の場合、実行が停止します。 + 評価される条件。戻り値が true の場合、プログラムの実行は停止します。 + アサーションが true の場合に表示されるテキスト。 + + + 指定されたバッファのデータを標準出力ストリームに出力します。 + バッファ データのバイトを含む配列。 + データの各行に対して巡回冗長検査 (CRC) を計算および出力するかどうかを指定するブール値。 + データの各行に対してメモリ オフセット アドレスを出力するかどうかを指定するブール値。 + + + 利用可能なヒープ情報をすべて標準出力ストリームに出力します。 + + + パフォーマンス カウンタ情報を標準出力ストリームに出力します。 + + + 利用可能なスタック情報をすべて標準出力ストリームに出力します。 + + + 未使用のコンピュータ メモリを自動的に収集するサービスである、ガベージ コレクションを実行します。 + ガベージ コレクションが強制されるべきかどうかを指定するブール値。 + 空き (未使用) メモリの量 (バイト単位)。 + + + メッセージ (およびそれに続く現在の行ターミネータ) を標準出力ストリームに出力します。 + 標準出力ストリームに出力するメッセージ。 + + + コードをデバッグするのに役立つ一連のメソッドとプロパティを提供します。 + + + Dispatcher オブジェクト (ディスパッチャ) がシャットダウンを完了すると発生します。 + + + Dispatcher オブジェクト (ディスパッチャ) がシャットダウン プロセスを開始すると発生します。 + + + + 特定の Dispatcher オブジェクト (ディスパッチャ) が作成されたスレッドで、指定のデリゲートを実行します。 + +1 つの引数を受け取るメソッドへのデリゲート。このデリゲートは、ディスパッチャのイベント キューにプッシュされます。 + 引数としてデリゲートに渡されるオブジェクト。 + BeginInvoke メソッドが呼び出された直後に返されるオブジェクト。このオブジェクトは、イベント キュー内で実行が保留にされているデリゲートと対話するために使用できます。 + + + 呼び出しスレッドが現在の Dispatcher オブジェクト (ディスパッチャ) へのアクセス権限を持っているかどうか判定します。 + 呼び出しスレッドが現在のディスパッチャへのアクセス権限を持っている場合、true。それ以外の場合、false。 + + + 指定されたスレッドの Dispatcher オブジェクト (ディスパッチャ) を取得します。 + ディスパッチャが取得されるスレッド。 + 指定されたスレッドのディスパッチャ。 + + + Dispatcher オブジェクト (ディスパッチャ) のシャットダウン プロセスを同期的に開始します。 + + + 指定した引数を使用して指定したデリゲートを実行します。 + プログラムが当該操作の完了を待つ最大時間。 + 複数の引数を受け取るメソッドへのデリゲート。このデリゲートは、Dispatcher オブジェクトのイベント キューに格納されます。 + 引数として指定されたメソッドに渡されるオブジェクト。引数が不要な場合は、null 参照にすることができます。 + 呼び出されたデリゲートからの戻り値。デリゲートに戻り値がない場合は、null 参照。 + + + 実行フレームを、Dispatcher オブジェクト (ディスパッチャ) のイベント キュー上にプッシュします。 + ディスパッチャによって処理されるフレーム。 + + + Dispatcher オブジェクト (ディスパッチャ) のイベント キューを開始します。 + + + + 呼び出しスレッドが現在の Dispatcher オブジェクト (ディスパッチャ) へのアクセス権限を持っているかどうか判定します。 + + + 現在実行中のスレッドに対する Dispatcher オブジェクト (ディスパッチャ) を取得し、現在のスレッドに既に関連付けられたディスパッチャがない場合は、新規のディスパッチャを作成します。 + + 現在のスレッドと関連付けられたディスパッチャ。 + + + 現在の Dispatcher オブジェクト (ディスパッチャ) がシャットダウンを完了したかどうかを判定します。 + + 現在のディスパッチャがシャットダウンを完了した場合、true。それ以外の場合、false。 + + + +現在の Dispatcher オブジェクト (ディスパッチャ) がシャットダウン中かどうかを判定します。 + 現在のディスパッチャがシャットダウンを開始済みの場合、true。それ以外の場合、false。 + + + 現在の Dispatcher オブジェクト (ディスパッチャ) が関連付けられているスレッドを取得します。 + + 現在のディスパッチャと関連付けられたスレッド。 + + + スレッドのイベント キューを維持するためのサービスを提供します。 + + + +指定された終了要求フラグを使用して、DispatcherFrame クラスの新規インスタンスを初期化します。 + + すべてのフレームを終了するよう要求されたときに、現在のディスパッチャ フレームを終了するかどうかを示します。 + + + DispatcherFrame クラスの新規インスタンスを初期化します。 + + + + 現在のディスパッチャ フレームを継続するかどうかを示す値を取得または設定します。 + ディスパッチャ フレームを継続する場合、true。それ以外の場合、false。 + + + ディスパッチャのイベント キューの実行ループの反復を表します。 + + + DispatcherObject クラスの新規インスタンスを初期化します。 + + + 現在の DispatcherObject オブジェクトが関連付けられた Dispatcher オブジェクトを含みます。 + + + + 呼び出しスレッドが現在の DispatcherObject オブジェクトへのアクセス権限を持っているかどうか判定します。 + 呼び出しスレッドが現在のオブジェクトへのアクセス権限を持っている場合、true。それ以外の場合、false。 + + + + +呼び出しスレッドが現在の DispatcherObject オブジェクトへのアクセス権限を持っているかどうか判定します。 + + + + ディスパッチャと関連付けられたオブジェクトを表します。 + + + 現在の DispatcherOperation オブジェクトに関連付けられたディスパッチャ キューのデリゲートが中止されたときに発生します。 + + + +現在の DispatcherOperation オブジェクトに関連付けられたディスパッチャ キュー上のデリゲートが完了したときに発生します。 + + + + 現在の DispatcherOperation オブジェクトに関連付けられたディスパッチャ キューのデリゲートを中止します。 + 操作が中止された場合、true。それ以外の場合、false。 + + + ディスパッチャ キュー上のデリゲートが完了するまで、指定された長さの時間、待機します。 + このメソッドが待機する最大時間。 + 操作の状態。 + + + +現在の DispatcherOperation オブジェクトに関連付けられたディスパッチャ キュー上のデリゲートが完了するまで待機します。 + + 操作の状態。 + + + 現在の DispatcherOperation オブジェクトに関連付けられたデリゲートが渡されたディスパッチャを取得します。 + デリゲートが渡されたディスパッチャ。 + + + 操作が完了した場合、現在の DispatcherOperation オブジェクトと関連付けられたデリゲートの結果を取得します。 + 操作の結果。 + + + 現在の DispatcherOperation オブジェクトと関連付けられたディスパッチャ キュー上のデリゲートの現在の操作状態を取得します。 + 操作の状態。 + + + プログラムがディスパッチャ キューに渡されたデリゲートと対話できるようにします。 + + + DispatcherTimer クラスの新規インスタンスを初期化します。 + + + + DispatcherTimer クラスの新規インスタンスを初期化し、そのインスタンスを指定された Dispatcher オブジェクトに関連付けます。 + + 現在のタイマと関連付けられたディスパッチャ。 + + + 現在の DispatcherTimer オブジェクト (タイマ) の指定された時間間隔が経過し、タイマが有効な場合、発生します。 + + + DispatcherTimer オブジェクトのタイマを開始します。 + + + DispatcherTimer オブジェクトのタイマを停止します。 + + + + 現在の DispatcherTimer オブジェクト (タイマ) と関連付けられた Dispatcher オブジェクト (ディスパッチャ) を取得します。 + 現在のタイマと関連付けられたディスパッチャ。 + + + 現在の DispatcherTimer オブジェクトの定義済み時間間隔を取得または設定します。 + タイマのティック間の時間間隔。時間:分:秒の形式で表されます。既定値は、00:00:00 です。 + + + 特定の DispatcherTimer オブジェクト (タイマ) が有効である (現在実行中である) かどうかを示すブール値を取得または設定します。 + +タイマが現在実行中である場合、true。それ以外の場合、false。既定値は、false です。 + + + + Tick イベント ハンドラに渡されるユーザー定義のデータ オブジェクトを取得または設定します。 + Tick イベント ハンドラに渡されるユーザー定義のデータ オブジェクト。既定値は、null 参照です。 + + + ディスパッチャ キューに統合されたタイマ。 + + + EventArgs クラスの新規インスタンスを初期化します。 + + + 空の、読み取り専用の EventArgs オブジェクトを取得します。 + + 空の、読み取り専用の EventArgs オブジェクト。これは、EventArgs コンストラクタを呼び出すことと同じ結果になります。 + + + イベント データを含むクラスの基本クラスを構成します。 + + + EventHandlersStore クラスの新規インスタンスを初期化します。 + + + + 指定の RoutedEvent オブジェクトに対する指定のルーティング イベント ハンドラを、現在の EventHandlersStore オブジェクト内のイベント コレクションに追加します。 + ルーティング イベント。 + ルーティング イベントに対するハンドラ。 + ルーティング イベント ハンドラが、処理済みのイベントも処理するかどうかを示すブール値。 + + + イベント ハンドラのコンテナを提供します。 + + + EventRoute クラスの新規インスタンスを初期化します。 + + 現在のイベント ルートに関連付けられたイベント識別子 (ID)。このパラメータは null に設定できない点に注意してください。 + + + 指定のターゲットに対する指定のハンドラをイベント ルートに追加します。 + ハンドラをイベント ルートに追加するターゲット オブジェクト。 + イベント ルートに追加するハンドラ。 + 処理済みのイベントをリスナが検出するかどうかを示す値。 + + + ルーティング イベントが後に続くルートのコンテナを提供します。 + + + 呼び出しスレッド内にサブスレッドを作成します。このサブスレッドには、指定された時間間隔内に指定された優先度で操作を完了することを呼び出しスレッドに要求する制約が含まれます。 + ConstraintException 例外がスローされるまでのクロック ティック数。このパラメータの値が -1 の場合は、現在の制約例外が除去される点に注意してください。 + 呼び出しスレッドの優先度。 + + + 特定の制約内で操作を完了するためにスレッドを要求することを目的として使用できるメソッドを提供します。 + + + ExtendedTimer クラスの新規インスタンスを初期化します。 + タイマの終了時に実行されるコールバック メソッド。 + コールバック メソッドによって呼び出されたメソッドに関連するアプリケーション固有の情報を含むオブジェクト、または null 参照。 + コールバック メソッドを実行するイベント。 + + + System.DateTime および System.TimeSpan の値を使用して時間間隔を計測し、ExtendedTimer クラスの新規インスタンスを初期化します。 + タイマの終了時に実行されるコールバック メソッド。 + コールバック メソッドによって呼び出されたメソッドに関連するアプリケーション固有の情報を含むオブジェクト、または null 参照。 + コールバック メソッドがメソッドを呼び出す前にタイマが待機する時間の長さを表す System.DateTime の値。ミリ秒単位。タイマが開始しないようにするには、System.Threading.Timeout.Infinite を指定します。タイマをすぐに開始するには、0 (ゼロ) を指定します。 + コールバック メソッドによって参照されるメソッドの呼び出しの時間間隔を表す System.TimeSpan の値。ミリ秒単位。周期的なシグナルを無効にするには、System.Threading.Timeout.Infinite を指定します。 + + + System.TimeSpan の値を使用して時間間隔を計測し、ExtendedTimer クラスの新規インスタンスを初期化します。 + タイマの終了時に実行されるコールバック メソッド。 + コールバック メソッドによって呼び出されたメソッドに関連するアプリケーション固有の情報を含むオブジェクト、または null 参照。 + コールバック メソッドがメソッドを呼び出す前にタイマが待機する時間の長さ。ミリ秒単位。タイマが開始しないようにするには、System.Threading.Timeout.Infinite を指定します。タイマをすぐに開始するには、0 (ゼロ) を指定します。 + コールバック メソッドによって参照されるメソッドの呼び出しの時間間隔。ミリ秒単位。周期的なシグナルを無効にするには、System.Threading.Timeout.Infinite を指定します。 + + + 32 ビット符号付き整数を使用して時間間隔を計測し、ExtendedTimer クラスの新規インスタンスを初期化します。 + タイマの終了時に実行されるコールバック メソッド。 + コールバック メソッドによって呼び出されたメソッドに関連するアプリケーション固有の情報を含むオブジェクト、または null 参照。 + コールバック メソッドがメソッドを呼び出す前にタイマが待機する時間の長さ。ミリ秒単位。タイマが開始しないようにするには、System.Threading.Timeout.Infinite を指定します。タイマをすぐに開始するには、0 (ゼロ) を指定します。 + コールバック メソッドによって参照されるメソッドの呼び出しの時間間隔。ミリ秒単位。周期的なシグナルを無効にするには、System.Threading.Timeout.Infinite を指定します。 + + + 時間間隔を計るために System.TimeSpan 値を使用して、タイマの開始時刻とメソッド呼び出し間隔を変更します。 + コールバック パラメータがメソッドを呼び出す前にタイマが待機する時間の長さを表す System.TimeSpan の値。ミリ秒単位。タイマが開始しないようにするには、System.Threading.Timeout.Infinite を指定します。タイマをすぐに開始するには、0 (ゼロ) を指定します。 + + +System.Threading.Timer の構築時に指定されたデリゲートによって参照されるメソッドの呼び出し間隔を表す System.TimeSpan の値。ミリ秒単位。周期的なシグナルを無効にするには、System.Threading.Timeout.Infinite を指定します。 + + + 時間間隔を計るために System.DateTime および System.TimeSpan 値を使用して、タイマの開始時刻とメソッド呼び出し間隔を変更します。 + コールバック パラメータがメソッドを呼び出す前にタイマが待機する時間の長さを表す System.DateTime の値。ミリ秒単位。タイマが開始しないようにするには、System.Threading.Timeout.Infinite を指定します。タイマをすぐに開始するには、0 (ゼロ) を指定します。 + + +System.Threading.Timer の構築時に指定されたデリゲートによって参照されるメソッドの呼び出し間隔を表す System.TimeSpan の値。ミリ秒単位。周期的なシグナルを無効にするには、System.Threading.Timeout.Infinite を指定します。 + + + 時間間隔を計るために 32 ビット符号付き整数を使用して、タイマの開始時刻とメソッド呼び出し間隔を変更します。 + System.Threading.Timer の構築時に指定されたデリゲートがメソッドを呼び出す前に、タイマが待機する時間の長さ。ミリ秒単位。タイマが再開しないようにするには、System.Threading.Timeout.Infinite を指定します。タイマをすぐに再開するには、0 (ゼロ) を指定します。 + System.Threading.Timer の構築時に指定されたデリゲートによって参照されるメソッドの呼び出し間隔。ミリ秒単位。周期的なシグナルを無効にするには、System.Threading.Timeout.Infinite を指定します。 + + + 現在の ExtendedTimer クラスのインスタンスで使用されているリソースをすべて解放します。 + + + 現在のタイマの最後の時間切れの System.TimeSpan の値を取得します。 + 現在のタイマの最後の時間切れの System.TimeSpan の値。 + + + 指定された間隔で、または特定のイベントの発生時に、メソッドを実行する機構を提供します。 + + + 2003/01/01 00:00:00 の DateTime 値に等しいクロックのティック数を指定します。 + + + 指定された年の夏時間の期間を検索します。 + 夏時間の期間が適用される年。 + 指定された年の夏時間の開始日と終了日を含む System.Globalization.DaylightTime 値。 + + + 指定したタイム ゾーン ID のタイム ゾーンを返します。 + 取得するタイム ゾーンの ID。 + 指定したタイム ゾーン ID に設定された System.TimeZone の値。 + + + 指定したローカル時間の協定世界時 (UTC) オフセットを検索します。 + 現地の日付と時刻。 + クロックのティック単位で計測した、UTC オフセットを検索するローカル時間。 + + + 時間のオフセットを設定します。 + デバイスのシステム時刻を真の時刻より進ませる場合の時間差。分単位。 + + + 特定のタイム ゾーンの拡張情報を設定します。 + 特定のタイム ゾーンの拡張情報。 + + + タイム ゾーンを設定します。 + デバイス システムに設定するタイム ゾーン。 + + + デバイス システムに現在ネットワーク接続が存在する場合、デバイス システム時刻とネットワーク時刻を同期します。 + ネットワークから取得した UTC 時刻。 + デバイス システム時刻。 + 同期するタイム ゾーンの ID。 + ネットワーク時刻とデバイス システム時刻の差に設定される System.TimeSpan の値。 + + + 特定のタイム ゾーンの夏時間の名前を取得します。 + 特定のタイム ゾーンの夏時間の名前。 + + + 特定のタイム ゾーンの標準名を取得します。 + 特定のタイム ゾーンの標準名。 + + + 特定のタイム ゾーンの ID を取得します。 + 特定のタイム ゾーンの ID。 + + + タイム ゾーンを表します。 + + + ExtendedWeakReference クラスの新規インスタンスを初期化し、指定されたオブジェクトを参照するようにします。 + 現在の弱い参照を対象とするオブジェクト。 + 現在の弱い参照に関連付ける型。 + 現在の弱い参照に関連付ける識別子。 + 現在の弱い参照がどのような状態から回復可能であるべきかを指定するフラグ。 + + + 現在の弱い参照がデバイス再起動後に回復可能となることを指定するフラグを含みます。 + + + デバイスの電源を切って再起動した後、現在の弱い参照が回復可能となることを指定するフラグを含みます。 + + + デバイスを再起動した後、または電源を切って再起動した後、回復の候補として ExtendedWeakReference オブジェクトにフラグを立てます。 + + + 特定の ExtendedWeakReference オブジェクトの回復を試みます。復旧が失敗した場合、このクラスの新規インスタンスを作成します。 + 回復する弱い参照と関連付けられた型。 + 回復する弱い参照と関連付けられた ID。 + 指定したオブジェクトが回復しなかった場合に作成される、新規 ExtendedWeakReference オブジェクト用のフラグ。 + 指定した弱い参照。または、復旧が失敗した場合、null 参照。 + + + 特定の ExtendedWeakReference オブジェクトを回復します。 + 回復する弱い参照と関連付けられた型。 + 回復する弱い参照と関連付けられた ID。 + 弱い参照。または、回復する弱い参照が既にない場合、null 参照。 + + + 現在の弱い参照の回復が必要な状態を指定するフラグを取得します。 + 現在の弱い参照の回復フラグ。 + + + 現在の ExtendedWeakReference オブジェクトに関連付けられた ID を取得します。 + 現在の弱い参照の ID。 + + + 現在の ExtendedWeakReference オブジェクトの優先度を取得または設定します。 + 現在の弱い参照の優先度。 + + + +現在の ExtendedWeakReference オブジェクトのセレクタを取得します。 + 現在の弱い参照のセレクタ。 + + + 拡張された弱い参照を表します。弱い参照は、オブジェクトを参照しますが、そのオブジェクトがガベージ コレクションの対象となりやすいことに変わりはありません。 + + + FieldNoReflectionAttribute オブジェクトを構築します。 + + + リフレクションが存在しないようにフィールドを設定します。 + + + 特定のフォントの既定のカーニングを含みます。 + + + 指定された文字の幅 (ピクセル単位) を取得します。 + 幅を取得する文字。 + 指定された文字の幅 (ピクセル単位)。 + + + 指定されたテキスト行の幅、高さ、およびカーニングを計算します。 + 測定するテキスト。 + 指定されたテキストの幅。 + 指定されたテキストの高さ。 + 連続する文字の間隔。 + + + 指定されたテキスト行の幅と高さを計算します。 + 測定するテキスト。 + 指定されたテキストの幅。 + 指定されたテキストの高さ。 + + + 指定された文字列の表示に必要な、ディスプレイ デバイス上の四角形領域のサイズを計算します。 + ディスプレイ デバイス上で表示するテキスト。 + テキストの表示に必要な四角形領域の幅。ピクセル単位。 + + テキストの表示に必要な四角形領域の高さ。ピクセル単位。 + + テキストの相対的な開始点の x 座標。 + テキストの相対的な開始点の y 座標。 + 定義された四角形領域に収まるテキストの最大幅。 + 定義された四角形領域に収まるテキストの最大の高さ。 + 配置などのさまざまなテキスト属性を指定するフラグ。 + + + 指定された文字列の表示に必要な、ディスプレイ デバイス上の四角形領域のサイズを計算します。 + ディスプレイ デバイス上で表示するテキスト。 + テキストの表示に必要な四角形領域の幅。ピクセル単位。 + テキストの表示に必要な四角形領域の高さ。ピクセル単位。 + 定義された四角形領域に収まるテキストの最大幅。 + + + 指定された文字列の表示に必要な、ディスプレイ デバイス上の四角形領域のサイズを計算します。 + ディスプレイ デバイス上で表示するテキスト。 + テキストの表示に必要な四角形領域の幅。ピクセル単位。 + + テキストの表示に必要な四角形領域の高さ。ピクセル単位。 + + + + 現在のフォントに対するアセントの測定値 (ピクセル単位) を取得します。 + 現在のフォントに対するアセントの測定値 (ピクセル単位)。 + + + 現在のフォントの平均文字幅 (ピクセル単位) を取得します。 + 現在のフォントの平均文字幅 (ピクセル単位)。 + + + 現在のフォントに対するディセントの測定値 (ピクセル単位) を取得します。 + 現在のフォントに対するディセントの測定値 (ピクセル単位)。 + + + 現在のフォントに対する外部レディングの測定値 (ピクセル単位) を取得します。 + 現在のフォントに対する外部レディングの測定値 (ピクセル単位)。 + + + 現在のフォントの高さ (ピクセル単位) を取得します。 + 現在のフォントの高さ。ピクセル単位。 + + + 現在のフォントに対する内部レディングの測定値 (ピクセル単位) を取得します。 + 現在のフォントに対する内部レディングの測定値 (ピクセル単位)。 + + + 現在のフォントに含まれる最大幅文字の幅 (ピクセル単位) を取得します。 + 現在のフォントに含まれる最大幅文字の幅 (ピクセル単位)。 + + + テキストの特定の書式 (フォント、フォント サイズ、スタイルの属性など) を定義します。 + + + 特定のメソッドがグローバルに同期化されていることを示します。 + + + ログ レコードのリストの末尾に Logging オブジェクト (ログ レコード) を追加します。 + リストの末尾に追加するログ レコード。 + + + すべての Logging オブジェクト (ログ レコード) を消去します。 + + + インデックスが 0 から始まるログ レコードのリストから、Logging オブジェクト (ログ レコード) を取得します。 + 取得するログ レコードのインデックス番号。 + ログ レコード。 + + + 現在のレコード リストにある Logging オブジェクト (ログ レコード) の数を取得します。 + 現在のレコード リストにあるログ レコードの数。 + + + ログに記録したデータの管理に使用するメソッドとプロパティを提供します。 + + + 指定された角度のコサインを取得します。 + 角度 (単位は度)。 + 指定された角度のコサインを 1000 で乗算した値。 + + + 乱数生成のランダムな起点を設定します。 + + + 擬似乱数を生成します。 + 生成される擬似乱数に適用するモジュロ。生成される数値は、0 (ゼロ) 以上モジュロ未満である点に注意してください。 + 擬似乱数。 + + + 指定された角度のサインを取得します。 + 角度 (単位は度)。 + 指定された角度のサインを、1000 で乗算した値。 + + + 一般的な数学関数用の静的メソッドを提供します。 + + + PropertyChangedEventArgs クラスの新規インスタンスを初期化します。 + + 変更されたプロパティの名前。 + 指定されたプロパティの変更前の値。 + 指定されたプロパティの変更後の値。 + + + 指定されたプロパティの変更後の値を含みます。 + + + 指定されたプロパティの変更前の値を含みます。 + + + 変更されるプロパティの名前を含みます。 + + + プロパティ変更イベントのデータを提供します。 + + + PublishInApplicationDirectoryAttribute クラスの新規インスタンスを作成および初期化します。 + + + アプリケーションがそのアプリケーション ディレクトリで公開されるべきであることを示します。 + + + Reflection クラスの新規インスタンスを作成および初期化します。 + + + データのバイト配列を、指定した型のオブジェクトにシリアル化解除します。 + シリアル化されたオブジェクトを表すデータを含むバイト配列。 + シリアル化解除されるオブジェクトの型。v パラメータ内の配列にその型が含まれる場合、これは null 参照である必要があります。 + バイト配列によってよって表される、シリアル化解除されるオブジェクト。 + + + すべての既知のアセンブリを取得します。 + すべての既知のアセンブリを含む配列。 + + + 指定されたハッシュ属性を持つアセンブリを取得します。 + 取得するアセンブリのハッシュ属性。 + 指定されたハッシュ属性を持つアセンブリ。 + + + 指定されたアセンブリからハッシュ属性を取得します。 + ハッシュ属性の取得元となるアセンブリ。 + 指定されたアセンブリを表すハッシュ属性。 + + + 指定されたアセンブリを表すバイト配列から AssemblyInfo オブジェクトを取得します。 + 指定されたアセンブリを表すバイト配列。 + バイト配列から抽出したデータで事前設定されるアセンブリ。 + AssemblyInfo オブジェクトに、バイト配列から抽出された有効なデータが含まれる場合、true。それ以外の場合、false。 + + + 指定されたアセンブリが最後に使用された時間を表す System.TimeSpan 値を取得します。 + 指定されたアセンブリ。 + 指定されたアセンブリが最後に使用された時間。 + + + 指定されたアセンブリに対する修正プログラムとして指定されたアセンブリを取得します。 + 指定されたアセンブリ。 + 指定されたアセンブリの修正プログラムであるアセンブリ。 + + + 指定したハッシュ属性を使用する型を取得します。 + 指定した型のハッシュ属性。 + 指定したハッシュ属性に対応する型。 + + + 指定した型のハッシュ属性を取得します。 + ハッシュ属性の取得元となる型。 + 指定した型を表すハッシュ属性。 + + + 指定したインターフェイスを実装する System.Type オブジェクト (型) を取得します。 + 検索対象のインターフェイスの型。 + 型の配列。 + + + 指定した型が読み込まれているかどうかを示します。 + 確認対象の型。 + 指定した型が読み込まれている場合、true。それ以外の場合、false。 + + + データのバイト配列を、指定された型のオブジェクトにシリアル化します。 + シリアル化するオブジェクト。 + シリアル化するオブジェクトの型。このパラメータは、null 参照の場合もあります。 + シリアル化されたオブジェクトを表すデータを含むバイト配列。 + + + アセンブリ、およびアセンブリ内で定義された型について説明します。また、実行時の、型インスタンスの作成、呼び出し、およびアクセスについても説明します。 + + + AssemblyInfo クラスの新規インスタンスを作成および初期化します。 + + + 現在のアセンブリについて説明するフラグを含みます。 + + + 現在のアセンブリの一意識別子である、アセンブリのハッシュ属性を含みます。 + + + 現在のアセンブリの名前を含みます。 + + + 現在のアセンブリのインストール後にデバイスの再起動が必要なことを指定するフラグを含みます。 + + + 現在のアセンブリが別のアセンブリの修正プログラムであることを示すフラグを含みます。 + + + 現在のアセンブリが依存しているアセンブリへの参照の配列を含みます。 + + + 現在のアセンブリのサイズを指定します。 + + + AssemblyInfo オブジェクト (アセンブリ) を定義します。これは、自己記述型の、共通言語ランタイム (CLR) アプリケーションのビルド ブロックです。 + + + ResourceUtility オブジェクトを構築します。 + + + 指定されたリソースの文字列表記を取得します。 + 指定されたリソースを含むリソース マネージャ。 + 文字列表記を取得する対象のリソースの型を指定する列挙値。 + 指定されたリソースの文字列表記。 + + + 指定されたリソースの文字列表記を取得します。 + 指定されたリソースを含むリソース マネージャ。 + 指定されたリソースの型を示す列挙値。 + 文字列表記を取得する対象のリソースのインデックス番号 (リソース マネージャのリソース コレクション内)。 + 指定されたリソースの文字列表記。 + + + 現在のシステム カルチャの、指定された Object リソースの値を取得します。 + 指定されたリソースを含むリソース マネージャ。 + 指定されたリソースの整数の識別子。 + 現在のシステム カルチャの、指定されたリソースの値。 + + + 現在のシステム カルチャの情報を設定します。 + 現在のシステム カルチャの情報を含むオブジェクト。 + + + .NET Micro Framework アプリケーションがリソースを管理するのを支援します。 + + + RoutedEvent クラスの新規インスタンスを初期化します。 + + ルーティング イベントの識別名。 + ルーティング イベントのルーティング戦略。 + ルーティング イベントのハンドラの型。 + + + 現在の RoutedEvent オブジェクトの文字列表記を取得します。 + 現在のルーティング イベントの文字列表記。この文字列は、Name プロパティによって返される値と同一である点に注意してください。 + + + 現在の RoutedEvent オブジェクトのハンドラの型を取得します。 + + 現在のルーティング イベントのハンドラの型。 + + + 現在の RoutedEvent オブジェクトの識別名を取得します。 + 現在のルーティング イベントの識別名。 + + + 現在の Routed Event オブジェクトのルーティング戦略を取得します。 + RoutingStrategy 列挙型の値。 + + + + ルーティング イベントを表して識別し、その特性を宣言します。 + + + +提供されるルーティング イベント識別子を使用して、RoutedEventArgs クラスの新規インスタンスを初期化します。 + + +現在の RoutedEventArgs オブジェクトのルーティング イベント識別子。 + + + + 提供されるルーティング イベント識別子を使用して、RoutedEventArgs クラスの新規インスタンスを初期化します。このコンストラクタを使用することで、イベントに異なるソースを宣言できるようにもなる点に注意してください。 + 現在の RoutedEventArgs オブジェクトのルーティング イベント識別子。 + ルーティング イベントが処理される際に報告される別のソース。 + + + RoutedEventArgs クラスの新規インスタンスを初期化します。 + + + + オブジェクトの Source プロパティの値が変化するたびに、通知コールバックのエントリ ポイントを提供します。 + このメソッドの呼び出しをトリガする Source プロパティを持つオブジェクトを含みます。 + + + 特定のルーティング イベントがルートを通過するとき、そのイベントのイベント処理の現在の状態を示す値を取得または設定します。 + このプロパティを設定する場合 :ルーティング イベントが処理済みとしてマークされる場合、true。それ以外の場合、false。このプロパティを読み取る場合 :ルートに沿って、クラス ハンドラまたは一部のインスタンス ハンドラが既にこのイベントを処理済みとしてマークしている場合、true。それ以外の場合、false。既定値は、false です。 + + + +現在のルーティング イベントを発生させた元のオブジェクトを取得します。 + + クラス処理によって Source プロパティに対して何らかの調整を加えることが可能になる前の、元のレポート ソース。 + + + ルーティング イベントを取得または設定します。 + ルーティング イベント。 + + + 現在のルーティング イベントを発生させたオブジェクトへの参照を取得または設定します。 + 現在のルーティング イベントを発生させたオブジェクト。 + + + 特定のルーティング イベントと関連付けられた状態情報とイベント データを提供します。 + + + イベント ルートを構築します。 + + + リモート プロシージャ コール用のクライアントを提供します。 + + + リモート プロシージャ コール用のサーバーを提供します。 + + + シリアル化された特定の配列のサイズを指定します。 + + + 現在のオブジェクトを何ビットにビット圧縮するかを指定します。 + + + 現在のオブジェクトのシリアル化について説明するフラグを含みます。 + + + 特定のシリアル化された値の範囲バイアス調整を指定します。 + + + 特定のシリアル化された値のスケール調整を指定します。 + + + 特定のシリアル化できるオブジェクトに、シリアル化の既知の属性とメソッドが存在することを示します。 + + + 内部使用専用。代わりに、System.Net.Sockets を使用してください。 + + + SystemTime オブジェクトを構築します。 + + + 特定の SystemTime オブジェクトに関連付けられる曜日を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられる日を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられた時間を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられたミリ秒を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられた分を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられた月を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられた秒を指定します。 + + + 特定の SystemTime オブジェクトに関連付けられた年を指定します。 + + + デバイスの現在のシステム時刻を設定または取得します。 + + + TimeZoneInformation オブジェクトを構築します。 + + + このオペレーティング システムで変換されるローカル時間に対し、現在の時差を分単位で指定します。 + + + 特定のタイム ゾーンの夏時間の間に行われるローカル時間の変換で使用される時差の値を指定します。 + + + このオペレーティング システムで標準時間から夏時間に切り替わるときの日付とローカル時間を指定します。 + + + 夏時間の説明を含みます。 + + + 特定のタイム ゾーンの標準時で生じるローカル時間の変換用に使用するバイアス値を指定します。 + + + 夏時間から標準時への切り替えがこのオペレーティング システムで起こる日付とローカル時間を含みます。 + + + 標準時の説明を含みます。 + + + 特定のタイム ゾーンを説明する情報を含みます。 + + + TinySystem クラスのインスタンスを初期化します。 + + + ハードウェアを初期化し、ウィンドウ マネージャを作成します。 + + + ハードウェア初期化用のコンストラクタとメソッドを提供します。 + + + 現在の例外を発生させた未知の型への参照を含みます。 + + + 未知の型が原因でエラーが発生した場合、スローされる例外。 + + + WeakDelegate クラスのインスタンスを作成および初期化します。 + + + 2 つのマルチキャスト (結合可能) デリゲートの呼び出しリストを連結します。 + 1 番目に配置する呼び出しリストを持つ結合可能デリゲート。 + 2 番目に配置する呼び出しリストを持つ結合可能デリゲート。 + a および b パラメータの呼び出しリストをこの順番で連結した呼び出しリストを持つ新規の結合可能デリゲート。このメソッドは、b が null 参照の場合は a を返します。a が null 参照の場合は b を返します。a と b のどちらも null 参照の場合は、null を返します。 + + + + 指定されたデリゲートの呼び出しリストを、別の指定されたデリゲートの呼び出しリストから削除します。 + 呼び出しリストの削除元のデリゲート。 + a で指定されたデリゲートから削除する呼び出しリストを持つデリゲート。 + b の呼び出しリストが a の呼び出しリスト内にある場合、a パラメータの呼び出しリストから、b パラメータの呼び出しリストを削除して得られる呼び出しリストを持つ新しいデリゲート。b が null 参照の場合、または b の呼び出しリストが a の呼び出しリスト内に存在しない場合、このメソッドは a を返します。b の呼び出しリストが a の呼び出しリストと同じ場合、または b が null 参照の場合、このメソッドは null を返します。 + + + "弱い" デリゲートを組み合わせて削除するメソッドを提供します。 + + + WindowCollection クラスの新規インスタンスを初期化します。 + + + ウィンドウ コレクションから Window オブジェクトを指定された配列にコピーします。ウィンドウ コレクション内の指定されたインデックス番号がコピーの開始位置となります。 + + +コピー先となる Window オブジェクト配列。 + + コピーの開始位置となる、現在のウィンドウ コレクション内のインデックス番号。 + + + ウィンドウ コレクション内の Window オブジェクトを列挙処理するためにプログラムで使用できる IEnumerator インターフェイスを返します。 + ウィンドウ コレクション内の Window オブジェクトを列挙処理するためにプログラムで使用できる IEnumerator インターフェイス。 + + + +特定のウィンドウ コレクション内の Window オブジェクト数を取得します。 + + ウィンドウ コレクション内の Window オブジェクト数を指定する整数。 + + + + 特定のウィンドウ コレクションがスレッド セーフであるかどうかを示す値を取得します。 + +ウィンドウ コレクションがスレッド セーフである場合、true。それ以外の場合、false。 + + + 特定のウィンドウ コレクション内の指定されたインデックス番号の Window オブジェクトを取得します。 + ウィンドウ コレクション内の指定されたインデックス番号の Window オブジェクト。 + + + 特定の WindowCollection へのアクセスの同期に使用できるオブジェクトを取得します。 + ウィンドウ コレクションへのアクセスの同期に使用できるオブジェクト。 + + + アプリケーションの各ウィンドウ オブジェクトを、単一のセットまたはウィンドウ コレクションに収集します。 + + + キャンセル可能なイベントを処理するメソッドを提供します。 + イベントの送信元 (ソース)。 + イベントを説明するデータ。 + + + ディスパッチャを操作するためのコールバック機能を提供します。 + コールバック メソッドに渡される引数。このパラメータの値は null の場合もあります。 + コールバック メソッドにより返されるオブジェクト。この値は null の場合もあります。 + + + + イベント データを持たないイベントを処理するメソッドを提供します。 + イベントの送信元 (ソース)。 + イベント データを含まないパラメータ。 + + + プロパティ変更イベントを処理します。 + 変更されるプロパティを持つオブジェクト。 + プロパティの変更に関する情報。 + + + ルーティング イベントを受信するイベント ハンドラを表します。 + ルーティング イベントの送信元。 + ルーティング イベントに関する情報を含むイベント引数。 + + + + ビットマップ イメージとして使用可能な形式をリストします。 + Windows BMP 形式のビットマップ。 + GIF 形式のビットマップ。 + JPEG 形式のビットマップ。 + .NET Micro Framework 共通言語ランタイム (CLR) に固有の形式のビットマップ。 + + + DispatcherOperation オブジェクトのステータスを示すための値を記述します。 + 操作が中止されたことを示す値。 + 操作が完了したことを示す値。 + 操作が実行を開始したが、完了していないことを示す値。 + 操作が保留中であることを示す値。 + + + 拡張された弱い参照の優先度を設定するための一連の定数を定義します。 + きわめて重要なデータ。 + 重要ではあるが、きわめて重要ではないデータ。 + 保持するのが望ましいが、きわめて重要ではないデータ。 + 破棄しても問題のないデータ。 + 基本動作に必須のシステム データ。 + + + セッション終了の理由を指定します。 + ユーザーがログオフしたことを指定する値。 + ユーザーがシステムをシャットダウンしたことを指定する値。 + + + ルーティング イベントのルーティング戦略を示します。 + イベントが "バブル" 戦略を使用することを示す値。イベント インスタンスは、この戦略でツリーを上昇します。 + イベントがルーティングをサポートしないことを示す値。 + イベントが "トンネリング" 戦略を使用することを示す値。イベント インスタンスは、この戦略でツリーを下降します。 + + + シリアル化 (シリアル化されたオブジェクト) のさまざまな属性を示す一連のフラグを定義します。 + シリアル化されたオブジェクトが圧縮されていることを示す値。このフラグは、現在実装されていません。 + このフラグは、現在実装されていません。 + シリアル化されたオブジェクト内の各要素が null であってはならないことを示す値。 + シリアル化されたオブジェクトが暗号化されていることを示す値。このフラグは、現在実装されていません。 + シリアル化されたオブジェクトが、派生クラスのインスタンスではなく、指定されたクラスのインスタンスでなければならないことを示す値。 + シリアル化を解除できない場合、シリアル化されたオブジェクトをスキップできることを示す値。このフラグは、現在実装されていません。 + シリアル化されたオブジェクトへのポインタが null であってはならないことを示す値。 + + + アプリケーションがどうシャットダウンするかを定義します。 + Application.Shutdown メソッドをプログラムが明示的に呼び出したとき、アプリケーションをシャットダウンさせる値。 + 最後のウィンドウが閉じたとき、または Application.Shutdown メソッドをプログラムが明示的に呼び出したとき、アプリケーションをシャットダウンさせる値。これが既定の設定である点に注意してください。 + メイン ウィンドウが閉じたとき、または Application.Shutdown メソッドをプログラムが明示的に呼び出したとき、アプリケーションをシャットダウンさせる値。 + + + .NET Micro Framework で利用可能なさまざまなタイマ イベントを定義します。 + 日の変更で発生するイベント。 + 時間の変更で発生するイベント。 + 分の変更で発生するイベント。 + 秒の変更で発生するイベント。 + 指定された時間で発生するイベント。 + タイム ゾーンの変更で発生するイベント。 + + + .NET Micro Framework アプリケーションで利用可能なタイム ゾーンのセットを提供します。 + アブダビ + アデレード + アラスカ + アルマトイ + アリゾナ + アスタナ + アテネ + 大西洋標準時 + アゾレス諸島 + バグダッド + バクー + バンコク + 北京 + ベルリン + ボゴタ + ブラジリア + ブリズベン + ブカレスト + ブエノスアイレス + カイロ + カーボベルデ + カラカス + カサブランカ + 中央アメリカ + 中央アメリカ + タイム ゾーン ID の総数 + 現在のタイム ゾーン ID + ダーウィン + 文字列リソース + 東部標準時 + フィジー諸島 + 最初のタイム ゾーン ID + 最初のリソース文字列 + GMT、グリニッジ標準時 (UTC、協定世界時) + グリーンランド + グアム + ハワイ + ヘルシンキ + ホバート + インディアナ + イスラマバード + エルサレム + カブール + カトマンズ + クラスノヤルスク + 最後のタイム ゾーン ID + ロンドン + マガダン + マレーシア + メキシコシティ + 中央大西洋 + モスクワ + 山地標準時 + ナイロビ + ネットワーク提供のタイム ゾーン + ニューデリー + ニューファンドランド + ニュージーランド + 太平洋標準時 + パリ + パース + プラハ + プレトリア + リヤド + サモア + サンティアゴ + サスカチュワン + ソウル + スリランカ + シドニー + 台北 + テヘラン + 東京 + トンガ + ウランバートル + ウラジオストク + ワルシャワ + 西中央アフリカ + ヤクーツク + ヤンゴン + エカテリンブルク + + + \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf/Native/Bitmap.cs b/source/nanoFramework.Graphics.Wpf/Native/Bitmap.cs new file mode 100644 index 0000000..6294293 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/Bitmap.cs @@ -0,0 +1,536 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.CompilerServices; +using nanoFramework.UI; +using nanoFramework.Presentation.Media; + +namespace nanoFramework.UI +{ + /// + /// Encapsulates a bitmap, which consists of the pixel data for a graphics image and its methods and attributes + /// This class cannot be inherited.The.NET Micro Framework provides the + /// + public sealed class Bitmap : MarshalByRefObject, IDisposable + { + /// + /// Specifies the maximum width of the display device, in pixels. + /// + public static readonly int MaxWidth;// = 220; + + /// + /// Specifies the maximum height of the display device, in pixels. + /// + public static readonly int MaxHeight;// = 176; + + /// + /// Represents the x-coordinate location of the center of the display device, in pixels. + /// + public static readonly int CenterX;// = (MaxWidth - 1) / 2; + + /// + /// Represents the y-coordinate location of the center of the display device, in pixels. + /// + public static readonly int CenterY;// = (MaxHeight - 1) / 2; + + static Bitmap() + { +// int bpp, orientation; +// Netmf Microsoft.SPOT.Hardware.HardwareProvider hwProvider1 = Microsoft.SPOT.Hardware.HardwareProvider.HwProvider; +// hwProvider1.GetLCDMetrics(out MaxWidth, out MaxHeight, out bpp, out orientation); + +// New + MaxWidth = SystemMetrics.ScreenWidth; + MaxHeight = SystemMetrics.ScreenHeight; + + CenterX = (MaxWidth - 1) / 2; + CenterY = (MaxHeight - 1) / 2; + } + + /// These have to be kept in sync with the CLR_GFX_Bitmap::c_DrawText_ flags. + /// + /// Specifies that the current bitmap is opaque. + /// + public const ushort OpacityOpaque = 256; + /// + /// Specifies that the current bitmap is transparent. + /// + public const ushort OpacityTransparent = 0; + /// + /// Copies the source rectangle directly to the destination rectangle. + /// + public const int SRCCOPY = 0x00000001; + /// + /// Inverts the source rectangle. + /// + public const int PATINVERT = 0x00000002; + /// + /// Inverts the destination rectangle. + /// + public const int DSTINVERT = 0x00000003; + /// + /// Fills the destination rectangle with the color associated with index number 0 in the physical palette. + /// + public const int BLACKNESS = 0x00000004; + /// + /// Fills the destination rectangle with the color associated with index number 1 in the physical palette. + /// + public const int WHITENESS = 0x00000005; + /// + /// Fills the destination rectangle with a gray color. + /// + public const int DSTGRAY = 0x00000006; + /// + /// Fills the destination rectangle with a light gray color. + /// + public const int DSTLTGRAY = 0x00000007; + /// + /// Fills the destination rectangle with a dark gray color. + /// + public const int DSTDKGRAY = 0x00000008; + /// + /// Specifies that a circle should have only a single-pixel border and no fill pattern or color. + /// + public const int SINGLEPIXEL = 0x00000009; + /// + /// Fills the destination rectangle or circle with a randomly generated pattern. + /// + public const int RANDOM = 0x0000000a; + /// + /// Specifies that there are no format rules. + /// + public const uint DT_None = 0x00000000; + /// + /// Specifies whether a line of bitmap text automatically wraps words to the beginning of the next line when the line reaches its maximum width. + /// + public const uint DT_WordWrap = 0x00000001; + /// + /// Specifies that if the bitmap text is larger than the space provided, the text is truncated at the bottom. + /// + public const uint DT_TruncateAtBottom = 0x00000004; + // [Obsolete("Use DT_TrimmingWordEllipsis or DT_TrimmingCharacterEllipsis to specify the type of trimming needed.", false)] + + /// + /// Specifies that the bitmap text is trimmed to the nearest character, and an ellipsis is inserted at the end of each trimmed line. + /// + public const uint DT_Ellipsis = 0x00000008; + /// + /// Specifies that if the bitmap text is larger than the space provided, the text is drawn in its full size, rather than being scaled down to fit the value in the Height property. + /// + public const uint DT_IgnoreHeight = 0x00000010; + /// + /// Specifies that text is left-aligned as it flows around a bitmap. + /// + public const uint DT_AlignmentLeft = 0x00000000; + /// + /// Specifies that text is center-aligned as it flows around a bitmap. + /// + public const uint DT_AlignmentCenter = 0x00000002; + /// + /// Specifies that text is right-aligned as it flows around a bitmap. + /// + public const uint DT_AlignmentRight = 0x00000020; + /// + /// Specifies that you can use a mask to get or set text alignment around a bitmap. + /// + public const uint DT_AlignmentMask = 0x00000022; + /// + /// Not yet documented. + /// + public const uint DT_TrimmingNone = 0x00000000; + /// + /// Not yet documented. + /// + public const uint DT_TrimmingWordEllipsis = 0x00000008; + /// + /// Not yet documented. + /// + public const uint DT_TrimmingCharacterEllipsis = 0x00000040; + /// + /// Not yet documented. + /// + public const uint DT_TrimmingMask = 0x00000048; + /// + /// Note that these values have to match the c_Type* consts in CLR_GFX_BitmapDescription + /// + public enum BitmapImageType : byte + { + /// + /// A bitmap in a format specific to the nano Framework common language runtine (CLR). + /// + NanoCLRBitmap = 0, + + /// + /// A bitmap in GIF format. + /// + Gif = 1, + + /// + /// A bitmap in JPEG format. + /// + Jpeg = 2, + + /// + /// A bitmap in Windows BMP format. + /// + Bmp = 3 // The windows .bmp format + } + + +#pragma warning disable 169 + private object m_bitmap; // Do not delete m_bitmap, this is linked to the underlying C/C++ code via magic +#pragma warning restore + /// + /// Encapsulates a bitmap, which consists of the pixel data for a graphics image and its methods and attributes. + /// + /// The width of the bitmap. + /// The height of the bitmap. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public Bitmap(int width, int height); + + /// + /// Not docummented yet. + /// + /// An array of pixel data for the specified image. + /// The bitmap type for the specified image. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public Bitmap(byte[] imageData, BitmapImageType type); + + /// + /// Flushes the current bitmap to the display device. + /// The bitmap must have the same dimensions as the display device.The.NET Micro Framework provides the + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void Flush(); + + /// + /// Flushes the current bitmap to the display device. + /// + /// The x-coordinate of the bitmap's upper-left corner. + /// The y-coordinate of the bitmap's upper-left corner. + /// The width of the bitmap. + /// The height of the bitmap. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void Flush(int x, int y, int width, int height); + + /// + /// Clears the entire drawing surface. + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void Clear(); + + /// + /// Draws text in a specified rectangle. + /// Sets the clipping region (clipping rectangle) of a bitmap with a specified coordinate pair (x, y), width, and height. + /// + /// The text to be drawn. This parameter contains the remaining text, or an empty string, if the complete text string did not fit in the specified rectangle. + /// The x-coordinate, relative to the rectangle, at which text drawing is to begin. + /// The y-coordinate, relative to the rectangle, at which text drawing is to begin. + /// The x-coordinate of the upper-left corner of the rectangle. + /// The y-coordinate of the upper-left corner of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + /// Flags that specify the format of the text. + /// The color to be used for the text. + /// The font to be used for the text. + /// + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public bool DrawTextInRect(ref string text, ref int xRelStart, ref int yRelStart, int x, int y, int width, int height, uint dtFlags, Color color, Font font); + + /// + /// The text to be drawn. This parameter contains the remaining text, or an empty string, if the complete text string did not fit in the specified rectangle. + /// The x-coordinate of the upper-left corner of the rectangle. + /// The y-coordinate of the upper-left corner of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + /// Flags that specify the format of the text. + /// The color to be used for the text. + /// The font to be used for the text. + /// + public void DrawTextInRect(string text, int x, int y, int width, int height, uint dtFlags, Color color, Font font) + { + int xRelStart = 0; + int yRelStart = 0; + + DrawTextInRect(ref text, ref xRelStart, ref yRelStart, x, y, width, height, dtFlags, color, font); + } + + /// + /// + /// + /// The x-coordinate of the upper-left corner of the clipping rectangle. + /// The y-coordinate of the upper-left corner of the clipping rectangle. + /// The width of the clipping rectangle. + /// The height of the clipping rectangle. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void SetClippingRectangle(int x, int y, int width, int height); + + /// + /// Gets the width of the current bitmap. + /// + extern public int Width + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the height of the current bitmap. + /// + extern public int Height + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Draws an ellipse filled with a specified color gradient. + /// + /// The outline color. + /// The thickness of the ellipse's outline, in pixels. + /// The x-coordinate location of the center of the ellipse. + /// The y-coordinate location of the center of the ellipse. + /// The radius of the ellipse in the x-coordinate direction. + /// The radius of the ellipse in the y-coordinate direction. + /// The starting color of the color gradient. + /// The x-coordinate location of the starting point of the color gradient. + /// The y-coordinate location of the starting point of the color gradient. + /// The ending color of the color gradient. + /// The x-coordinate location of the ending point of the color gradient. + /// The y-coordinate location of the ending point of the color gradient. + /// The opacity of the ellipse. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void DrawEllipse( + Color colorOutline, int thicknessOutline, + int x, int y, int xRadius, int yRadius, + Color colorGradientStart, int xGradientStart, int yGradientStart, + Color colorGradientEnd, int xGradientEnd, int yGradientEnd, + ushort opacity); + + /// + /// Draw and Ellipse + /// + /// The outline color. + /// The x-coordinate location of the center of the ellipse. + /// The y-coordinate location of the center of the ellipse. + /// The radius of the ellipse in the x-coordinate direction. + /// The radius of the ellipse in the y-coordinate direction. + public void DrawEllipse(Color colorOutline, int x, int y, int xRadius, int yRadius) + { + DrawEllipse(colorOutline, 1, x, y, xRadius, yRadius, Color.Black, 0, 0, Color.Black, 0, 0, OpacityOpaque); + } + + /// + /// Draws a rectangular block of pixels with a specified degree of transparency. + /// + /// The x-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + /// The y-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + /// The source bitmap. + /// The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + /// The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + /// The width of the rectangular block of pixels to be copied. + /// The height of the rectangular block of pixels to be copied. + public void DrawImage(int xDst, int yDst, Bitmap bitmap, int xSrc, int ySrc, int width, int height) + { + DrawImage(xDst, yDst, bitmap, xSrc, ySrc, width, height, OpacityOpaque); + } + + /// + /// Draws a rectangular block of pixels with a specified degree of transparency. + /// + /// The x-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + /// The y-coordinate location of the upper-left corner of the rectangular area on the display to which the specified pixels are to be copied. + /// The source bitmap. + /// The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + /// The x-coordinate location of the upper-left corner of the rectangular area in the source bitmap from which the specified pixels are to be copied. + /// The width of the rectangular block of pixels to be copied. + /// The height of the rectangular block of pixels to be copied. + /// The degree of opacity of the bitmap. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void DrawImage(int xDst, int yDst, Bitmap bitmap, int xSrc, int ySrc, int width, int height, ushort opacity); + + /// + /// + /// + /// The degree of rotation. + /// The x-coordinate of the center? of the destination bitmap. + /// The y-coordinate of the center? of the destination bitmap. + /// ? + /// The x-coordinate of the center? of the source bitmap. + /// The y-coordinate of the center? of the source bitmap. + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void RotateImage(int angle, int xDst, int yDst, Bitmap bitmap, int xSrc, int ySrc, int width, int height, ushort opacity); + + /// + /// Sets a bitmap's transparent color. + /// + /// The color to be used as the bitmap's transparent color. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void MakeTransparent(Color color); + + /// + /// Draws a rectangular block of pixels on the display device, stretching or shrinking the rectangular area as necessary. + /// + /// The x-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The y-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The source bitmap. + /// The width of the rectangluar area to which the pixels are to be copied. + /// The height of the rectangluar area to which the pixels are to be copied. + /// The bitmap's degree of opacity. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void StretchImage(int xDst, int yDst, Bitmap bitmap, int width, int height, ushort opacity); + + /// + /// Draws a line on the display device. + /// + /// The color of the line. + /// The thickness of the line, in pixels.Remark: The thickness parameter is not currently available.For now, all lines are one pixel thick. + /// The x-coordinate location of the line's starting point. + /// The y-coordinate location of the line's starting point. + /// The x-coordinate location of the line's ending point. + /// The y-coordinate location of the line's ending point. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void DrawLine(Color color, int thickness, int x0, int y0, int x1, int y1); + + /// + /// Draws a rectangle on the display device. + /// + /// The color of the rectangle's outline. + /// The thickness of the rectangle's outline, in pixels. + /// The x-coordinate of the rectangle's upper-left corner. + /// The y-coordinate of the rectangle's upper-left corner. + /// The width of the rectangle, in pixels. + /// The height of the rectangle, in pixels. + /// The x-coordinate value of the corner radius used to produce rounded corners on the rectangle. + /// The y-coordinate value of the corner radius used to produce rounded corners on the rectangle. + /// The starting color for a color gradient. + /// Holds the x coordinate of the starting location of the color gradient. + /// Holds the y coordinate of the starting location of the color gradient. + /// Specifies the ending color of the color gradient. + /// Holds the x coordinate of the ending location of the color gradient. + /// Holds the y coordinate of the ending location of the color gradient. + /// Specifies the opacity of the fill color. Set to 0x00 for completely transparent. Set to 0xFF for completely opague. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void DrawRectangle( + Color colorOutline, int thicknessOutline, + int x, int y, int width, int height, int xCornerRadius, int yCornerRadius, + Color colorGradientStart, int xGradientStart, int yGradientStart, + Color colorGradientEnd, int xGradientEnd, int yGradientEnd, + ushort opacity + ); + + /// + /// Draws text on the display device, using a specified font and color. + /// + /// The text to be drawn. + /// The font to be used for the text. + /// The color to be used for the text. + /// The x-coordinate of the location where text drawing is to begin. + /// The y-coordinate of the location where text drawing is to begin. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void DrawText(string text, Font font, Color color, int x, int y); + + /// + /// Sets the color for a specified pixel. + /// + /// The x-coordinate of the pixel whose color you want to set. + /// The y-coordinate of the pixel whose color you want to set. + /// The color you want to set for the specified pixel. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void SetPixel(int xPos, int yPos, Color color); + + /// + /// Retrieves the pixel color at a specified location on the display device. + /// + /// The x-coordinate of the pixel whose color you want to get. + /// The y-coordinate of the pixel whose color you want to get. + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public Color GetPixel(int xPos, int yPos); + + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public byte[] GetBitmap(); + + /// + /// + /// + /// The x-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The y-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The width of the rectangluar area to which the pixels are to be copied. + /// The height of the rectangluar area to which the pixels are to be copied. + /// The source bitmap. + /// + /// + /// + /// + /// The bitmap's degree of opacity. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void StretchImage(int xDst, int yDst, int widthDst, int heightDst, Bitmap bitmap, int xSrc, int ySrc, int widthSrc, int heightSrc, ushort opacity); + + /// + /// + /// + /// The x-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The y-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The source bitmap. + /// + /// + /// The bitmap's degree of opacity. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void TileImage(int xDst, int yDst, Bitmap bitmap, int width, int height, ushort opacity); + + /// + /// + /// + /// The x-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// The y-coordinate of the upper-left corner of the rectangular area to which the pixels are to be copied. + /// + /// + /// + /// + /// + /// + /// + /// The bitmap's degree of opacity. A value of 0 (zero) makes the bitmap completely opaque (not transparent at all); a value of 255 makes the bitmap completely transparent. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void Scale9Image(int xDst, int yDst, int widthDst, int heightDst, Bitmap bitmap, int leftBorder, int topBorder, int rightBorder, int bottomBorder, ushort opacity); + + /// + /// + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern private void Dispose(bool disposing); + + /// + /// + /// + ~Bitmap() + { + Dispose(false); + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/ButtonEnum.cs b/source/nanoFramework.Graphics.Wpf/Native/ButtonEnum.cs new file mode 100644 index 0000000..0ed29eb --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/ButtonEnum.cs @@ -0,0 +1,433 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// + /// + public enum Button + { + /// + None = 0, + /// + VK_LBUTTON = 0x01, + /// + VK_RBUTTON = 0x02, + /// + VK_CANCEL = 0x03, + /// + VK_MBUTTON = 0x04, /* NOT contiguous with L & RBUTTON */ + + /// + VK_BACK = 0x08, + /// + VK_TAB = 0x09, + + /// + VK_CLEAR = 0x0C, + /// + VK_RETURN = 0x0D, + + /// + VK_SHIFT = 0x10, + /// + VK_CONTROL = 0x11, + /// + VK_MENU = 0x12, + /// + VK_PAUSE = 0x13, + /// + VK_CAPITAL = 0x14, + + /// + VK_KANA = 0x15, + /// + VK_HANGEUL = 0x15, /* old name - should be here for compatibility */ + /// + VK_HANGUL = 0x15, + + /// + VK_JUNJA = 0x17, + /// + VK_FINAL = 0x18, + /// + VK_HANJA = 0x19, + /// + VK_KANJI = 0x19, + + /// + VK_ESCAPE = 0x1B, + + /// + VK_CONVERT = 0x1c, + /// + VK_NOCONVERT = 0x1d, + + /// + VK_SPACE = 0x20, + /// + VK_PRIOR = 0x21, + /// + VK_NEXT = 0x22, + /// + VK_END = 0x23, + /// + VK_HOME = 0x24, + /// The LEFT button. + VK_LEFT = 0x25, + /// The UP button. + VK_UP = 0x26, + /// The RIGHT button. + VK_RIGHT = 0x27, + /// The DOWN button. + VK_DOWN = 0x28, + /// + VK_SELECT = 0x29, + /// + VK_PRINT = 0x2A, + /// + VK_EXECUTE = 0x2B, + /// + VK_SNAPSHOT = 0x2C, + /// + VK_INSERT = 0x2D, + /// + VK_DELETE = 0x2E, + /// + VK_HELP = 0x2F, + + /* VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ + /// + VK_0 = 0x30, + /// + VK_1 = 0x31, + /// + VK_2 = 0x32, + /// + VK_3 = 0x33, + /// + VK_4 = 0x34, + /// + VK_5 = 0x35, + /// + VK_6 = 0x36, + /// + VK_7 = 0x37, + /// + VK_8 = 0x38, + /// + VK_9 = 0x39, + + /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + /// + VK_A = 0x41, + /// + VK_B = 0x42, + /// + VK_C = 0x43, + /// + VK_D = 0x44, + /// + VK_E = 0x45, + /// + VK_F = 0x46, + /// + VK_G = 0x47, + /// + VK_H = 0x48, + /// + VK_I = 0x49, + /// + VK_J = 0x4A, + /// + VK_K = 0x4B, + /// + VK_L = 0x4C, + /// + VK_M = 0x4D, + /// + VK_N = 0x4E, + /// + VK_O = 0x4F, + /// + VK_P = 0x50, + /// + VK_Q = 0x51, + /// + VK_R = 0x52, + /// + VK_S = 0x53, + /// + VK_T = 0x54, + /// + VK_U = 0x55, + /// + VK_V = 0x56, + /// + VK_W = 0x57, + /// + VK_X = 0x58, + /// + VK_Y = 0x59, + /// + VK_Z = 0x5A, + /// + VK_LWIN = 0x5B, + /// + VK_RWIN = 0x5C, + /// + VK_APPS = 0x5D, + + /// + VK_SLEEP = 0x5F, + + /// + VK_NUMPAD0 = 0x60, + /// + VK_NUMPAD1 = 0x61, + /// + VK_NUMPAD2 = 0x62, + /// + VK_NUMPAD3 = 0x63, + /// + VK_NUMPAD4 = 0x64, + /// + VK_NUMPAD5 = 0x65, + /// + VK_NUMPAD6 = 0x66, + /// + VK_NUMPAD7 = 0x67, + /// + VK_NUMPAD8 = 0x68, + /// + VK_NUMPAD9 = 0x69, + /// + VK_MULTIPLY = 0x6A, + /// + VK_ADD = 0x6B, + /// + VK_SEPARATOR = 0x6C, + /// + VK_SUBTRACT = 0x6D, + /// + VK_DECIMAL = 0x6E, + /// + VK_DIVIDE = 0x6F, + /// + VK_F1 = 0x70, + /// + VK_F2 = 0x71, + /// + VK_F3 = 0x72, + /// + VK_F4 = 0x73, + /// + VK_F5 = 0x74, + /// + VK_F6 = 0x75, + /// + VK_F7 = 0x76, + /// + VK_F8 = 0x77, + /// + VK_F9 = 0x78, + /// + VK_F10 = 0x79, + /// + VK_F11 = 0x7A, + /// + VK_F12 = 0x7B, + /// + VK_F13 = 0x7C, + /// + VK_F14 = 0x7D, + /// + VK_F15 = 0x7E, + /// + VK_F16 = 0x7F, + /// + VK_F17 = 0x80, + /// + VK_F18 = 0x81, + /// + VK_F19 = 0x82, + /// + VK_F20 = 0x83, + /// + VK_F21 = 0x84, + /// + VK_F22 = 0x85, + /// + VK_F23 = 0x86, + /// + VK_F24 = 0x87, + /// + + VK_NUMLOCK = 0x90, + /// + VK_SCROLL = 0x91, + + /* + * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. + * Used only as parameters to GetAsyncKeyState() and GetKeyState(). + * No other API or message will distinguish left and right keys in this way. + */ + /// + VK_LSHIFT = 0xA0, + /// + VK_RSHIFT = 0xA1, + /// + VK_LCONTROL = 0xA2, + /// + VK_RCONTROL = 0xA3, + /// + VK_LMENU = 0xA4, + /// + VK_RMENU = 0xA5, + /// + VK_EXTEND_BSLASH = 0xE2, + /// + VK_OEM_102 = 0xE2, + /// + VK_PROCESSKEY = 0xE5, + /// + VK_ATTN = 0xF6, + /// + VK_CRSEL = 0xF7, + /// + VK_EXSEL = 0xF8, + /// + VK_EREOF = 0xF9, + /// + VK_PLAY = 0xFA, + /// + VK_ZOOM = 0xFB, + /// + VK_NONAME = 0xFC, + /// + VK_PA1 = 0xFD, + /// + VK_OEM_CLEAR = 0xFE, + /// + VK_SEMICOLON = 0xBA, + /// + VK_EQUAL = 0xBB, + /// + VK_COMMA = 0xBC, + /// + VK_HYPHEN = 0xBD, + /// + VK_PERIOD = 0xBE, + /// + VK_SLASH = 0xBF, + /// + VK_BACKQUOTE = 0xC0, + /// + VK_BROWSER_BACK = 0xA6, + /// + VK_BROWSER_FORWARD = 0xA7, + /// + VK_BROWSER_REFRESH = 0xA8, + /// + VK_BROWSER_STOP = 0xA9, + /// + VK_BROWSER_SEARCH = 0xAA, + /// + VK_BROWSER_FAVORITES = 0xAB, + /// + VK_BROWSER_HOME = 0xAC, + /// + VK_VOLUME_MUTE = 0xAD, + /// + VK_VOLUME_DOWN = 0xAE, + /// + VK_VOLUME_UP = 0xAF, + /// + VK_MEDIA_NEXT_TRACK = 0xB0, + /// + VK_MEDIA_PREV_TRACK = 0xB1, + /// + VK_MEDIA_STOP = 0xB2, + /// + VK_MEDIA_PLAY_PAUSE = 0xB3, + /// + VK_LAUNCH_MAIL = 0xB4, + /// + VK_LAUNCH_MEDIA_SELECT = 0xB5, + /// + VK_LAUNCH_APP1 = 0xB6, + /// + VK_LAUNCH_APP2 = 0xB7, + /// + VK_LBRACKET = 0xDB, + /// + VK_BACKSLASH = 0xDC, + /// + VK_RBRACKET = 0xDD, + /// + VK_APOSTROPHE = 0xDE, + /// + VK_OFF = 0xDF, + /// + VK_DBE_ALPHANUMERIC = 0x0f0, + /// + VK_DBE_KATAKANA = 0x0f1, + /// + VK_DBE_HIRAGANA = 0x0f2, + /// + VK_DBE_SBCSCHAR = 0x0f3, + /// + VK_DBE_DBCSCHAR = 0x0f4, + /// + VK_DBE_ROMAN = 0x0f5, + /// + VK_DBE_NOROMAN = 0x0f6, + /// + VK_DBE_ENTERWORDREGISTERMODE = 0x0f7, + /// + VK_DBE_ENTERIMECONFIGMODE = 0x0f8, + /// + VK_DBE_FLUSHSTRING = 0x0f9, + /// + VK_DBE_CODEINPUT = 0x0fa, + /// + VK_DBE_NOCODEINPUT = 0x0fb, + /// + VK_DBE_DETERMINESTRING = 0x0fc, + /// + VK_DBE_ENTERDLGCONVERSIONMODE = 0x0fd, + + /// + /// Last in the standard MF buttons enumeration + /// + LastSystemDefinedButton = 0x110, + + // Users may define their button definitions with values larger than + // Button.LastSystemDefinedButton + // Values less that Button.LastSystemDefinedButton are reserved for standard buttons. + // Values above Button.LastSystemDefinedButton are for third party extensions. + /// + AppDefined1 = LastSystemDefinedButton + 1, + /// + AppDefined2 = LastSystemDefinedButton + 2, + /// + AppDefined3 = LastSystemDefinedButton + 3, + /// + AppDefined4 = LastSystemDefinedButton + 4, + /// + AppDefined5 = LastSystemDefinedButton + 5, + /// + AppDefined6 = LastSystemDefinedButton + 6, + /// + AppDefined7 = LastSystemDefinedButton + 7, + /// + AppDefined8 = LastSystemDefinedButton + 8, + + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/DisplayControl.cs b/source/nanoFramework.Graphics.Wpf/Native/DisplayControl.cs new file mode 100644 index 0000000..63e8214 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/DisplayControl.cs @@ -0,0 +1,106 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.CompilerServices; + +namespace nanoFramework.UI +{ + /// + /// + /// + public enum DisplayOrientation : int + { + /// + /// Portrain + /// + PORTRAIT, + /// + /// Portrait 180 + /// + PORTRAIT180, + /// + /// Landscape + /// + LANDSCAPE, + /// + /// Landscape 180 + /// + LANDSCAPE180 + }; + + /// + /// + /// + public sealed class DisplayControl : MarshalByRefObject, IDisposable + { + static DisplayControl() + { + + } + /// + /// + /// + extern public int LongerSide + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// + /// + extern public int ShorterSide + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// + /// + extern public int BitsPerPixel + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// + /// + extern public int Orientation + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public bool ChangeOrientation(ref int Orientation); + + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern private void Dispose(bool disposing); + + /// + /// + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + } +} + diff --git a/source/nanoFramework.Graphics.Wpf/Native/Font.cs b/source/nanoFramework.Graphics.Wpf/Native/Font.cs new file mode 100644 index 0000000..933fe5d --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/Font.cs @@ -0,0 +1,170 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.CompilerServices; + +namespace nanoFramework.UI +{ + /// + /// + /// + public sealed class Font : MarshalByRefObject + { + +#pragma warning disable 169 + private object m_font; // Do not delete m_font, this is linked to the underlying C/C++ code via magic +#pragma warning restore + + // Must keep in sync with CLR_GFX_Font::c_DefaultKerning + /// + /// Contains the default kerning for a particular font. + /// The kerning controls the amount of space between consecutive characters in a particular font. + /// + public const int DefaultKerning = 1024; + + private Font() + { + } + + /// + /// Gets the width of the specified character, in pixels. + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public int CharWidth(char c); + + /// + /// Gets the height of the current font, in pixels. + /// + extern public int Height + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the average width of the characters in the current font, in pixels. + /// + extern public int AverageWidth + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the width of the widest character in the current font, in pixels. + /// + extern public int MaxWidth + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the ascent measurement for the current font, in pixels. + /// A font's ascent is the vertical distance between the font baseline and the top of the font area. + /// The ascent measurement for the current font, in pixels. + /// + extern public int Ascent + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the descent measurement for the current font, in pixels. + /// A font's descent is the vertical distance between the font baseline and the bottom of the font area. + /// The descent measurement for the current font, in pixels. + /// + extern public int Descent + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the internal leading measurement for the current font, in pixels. + /// The internal leading measurement for the current font, in pixels. + /// + extern public int InternalLeading + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the external leading measurement for the current font, in pixels. + /// The external leading measurement for the current font, in pixels. + /// + extern public int ExternalLeading + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Computes the width, height, and kerning of a specified line of text. + /// + /// The text you want to measure. + /// The width of the specified text. + /// The height of the specified text. + public void ComputeExtent(string text, out int width, out int height) + { + ComputeExtent(text, out width, out height, DefaultKerning); + } + + /// + /// + /// + /// The text you want to measure. + /// The width of the specified text. + /// The height of the specified text. + /// The spacing between consecutive characters. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void ComputeExtent(string text, out int width, out int height, int kerning); + + /// + /// Computes the size of the rectangular area on the display device needed to render the specified text string. + /// + /// The text you want to render on the display device. + /// The width, in pixels, of the rectangular area needed to render the text. + /// The height, in pixels, of the rectangular area needed to render the text. + public void ComputeTextInRect(string text, out int renderWidth, out int renderHeight) + { + ComputeTextInRect(text, out renderWidth, out renderHeight, 0, 0, 65536, 0, Bitmap.DT_IgnoreHeight | Bitmap.DT_WordWrap); + } + + /// + /// Computes the size of the rectangular area on the display device needed to render the specified text string. + /// + /// The text you want to render on the display device. + /// The width, in pixels, of the rectangular area needed to render the text. + /// The height, in pixels, of the rectangular area needed to render the text. + /// The maximum width of text that will fit in the defined rectangular area. + public void ComputeTextInRect(string text, out int renderWidth, out int renderHeight, int availableWidth) + { + ComputeTextInRect(text, out renderWidth, out renderHeight, 0, 0, availableWidth, 0, Bitmap.DT_IgnoreHeight | Bitmap.DT_WordWrap); + } + + /// + /// Computes the size of the rectangular area on the display device needed to render the specified text string. + /// + /// The text you want to render on the display device. + /// The width, in pixels, of the rectangular area needed to render the text. + /// The height, in pixels, of the rectangular area needed to render the text. + /// The x-coordinate of the relative starting point for the text. + /// The y-coordinate of the relative starting point for the text. + /// The maximum width of text that will fit in the defined rectangular area. + /// The maximum height of text that will fit in the defined rectangular area. + /// Flags that specify various text characteristics, such as alignment. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public void ComputeTextInRect(string text, out int renderWidth, out int renderHeight, int xRelStart, int yRelStart, int availableWidth, int availableHeight, uint dtFlags); + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/GenericEventEx.cs b/source/nanoFramework.Graphics.Wpf/Native/GenericEventEx.cs new file mode 100644 index 0000000..42612ce --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/GenericEventEx.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Runtime.Events +{ + /// + /// Creates an instance of the class. + /// + public class GenericEventEx : GenericEvent + { + /// + /// Specifies additional position information. + /// + public int X; + + /// + /// Specifies additional position information. + /// + public int Y; + } +} diff --git a/source/nanoFramework.Graphics.Wpf/Native/Ink.cs b/source/nanoFramework.Graphics.Wpf/Native/Ink.cs new file mode 100644 index 0000000..cb0f92f --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/Ink.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System.Runtime.CompilerServices; +using nanoFramework.UI; + +namespace nanoFramework.Touch +{ + /// + /// + /// + public static class Ink + { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void SetInkRegion(uint flags, int x1, int y1, int x2, int y2, int borderWidth, int color, int penWidth, Bitmap bitmap); + + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void ResetInkRegion(); + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/Mathematics.cs b/source/nanoFramework.Graphics.Wpf/Native/Mathematics.cs new file mode 100644 index 0000000..b24bdc3 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/Mathematics.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// Simple Min/Max for the wpf measures + /// + public static class Mathematics + { + /// + /// + /// + /// + /// + /// + static public int Max(int a, int b) + { + return a > b ? a : b; + } + /// + /// + /// + /// + /// + /// + static public int Min(int a, int b) + { + return a < b ? a : b; + } + /// + /// + /// + /// + /// + static public int Abs(int a) + { + return a < 0 ? a*-1 : a; + } + } +} diff --git a/source/nanoFramework.Graphics.Wpf/Native/TouchCollector.cs b/source/nanoFramework.Graphics.Wpf/Native/TouchCollector.cs new file mode 100644 index 0000000..66708ce --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/TouchCollector.cs @@ -0,0 +1,250 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.CompilerServices; +using nanoFramework.UI; + +namespace nanoFramework.Touch +{ + /// + /// + /// + [Flags] + public enum CollectionMethod : int + { + /// + /// + /// + Managed = 0, + + /// + /// + /// + Native = 1, + } + + /// + /// + /// + [Flags] + public enum CollectionMode : int + { + /// + /// + /// + InkOnly = 2, + + /// + /// + /// + GestureOnly = 4, + + /// + /// + /// + InkAndGesture = InkOnly | GestureOnly, + } + + internal class TouchCollector + { + /// + /// + /// + public TouchCollector() + { + } + + TimeSpan lastTime = TimeSpan.Zero; + + internal void SetBuffer(uint bufferSize) + { + if (TouchCollectorConfiguration.CollectionMethod == CollectionMethod.Managed) + { + } + else if (TouchCollectorConfiguration.CollectionMethod == CollectionMethod.Native) + { + // Not needed at this moment, we are using static buffer. + // TouchCollectorConfiguration.SetNativeBufferSize(bufferSize, bufferSize); + _nativeBufferSize = bufferSize; + } + } + + private uint _nativeBufferSize = 200; + } + + /// + /// + /// + public static class TouchCollectorConfiguration + { + /// + /// + /// + public static CollectionMode CollectionMode + { + get + { + return _collectionMode; + } + + set + { + _collectionMode = value; + } + } + + /// + /// + /// + public static CollectionMethod CollectionMethod + { + get + { + return _collectionMethod; + } + + set + { + if (_collectionMethod != value) + { + _collectionMethod = value; + _touchCollector.SetBuffer(_collectionBufferSize); + } + } + } + + /// + /// Sampling rate per second. Setting 50 will result 50 touch samples in a second. + /// + public static int SamplingFrequency + { + get + { + int param1 = 0; + int param2 = 0; + int param3 = 0; + + GetTouchInput(TouchInput.SamplingDistance, ref param1, ref param2, ref param3); + + if (param1 <= 0) + return 0; + + return (1000000 / param1); + } + + set + { + int param1 = 0; + int param2 = 0; + int param3 = 0; + + // Negative or zero is not acceptable frequency. + if (value <= 0) + throw new ArgumentException(); + + param1 = 1000000 / value; + + // param1 == 0 means more than one sample is requested per microsecond, + // which is not attainable. + if (param1 <= 0) + throw new ArgumentException(); + + SetTouchInput(TouchInput.SamplingDistance, param1, param2, param3); + } + } + + /// + /// + /// + /// + /// + public static void GetLastTouchPoint(ref int x, ref int y) + { + int param3 = 0; + GetTouchInput(TouchInput.LastTouchPoint, ref x, ref y, ref param3); + } + + /// + /// + /// + public static int TouchMoveFrequency + { + get + { + int param1 = 0; + int param2 = 0; + int param3 = 0; + GetTouchInput(TouchInput.TouchMoveFrequency, ref param1, ref param2, ref param3); + + return param1; + } + + set + { + int param1 = value; + int param2 = 0; + int param3 = 0; + + SetTouchInput(TouchInput.TouchMoveFrequency, param1, param2, param3); + } + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void EnableTouchCollection(int flags, int x1, int x2, int y1, int y2, Bitmap bitmap); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void GetTouchPoints(ref int pointCount, short[] sx, short[] sy); + + /// + /// + /// + [Flags] + public enum TouchInput + { + /// + /// param1- X, param2-Y, param3-unused. + /// + LastTouchPoint = 0x2, + /// + /// param1- Distance in micro seconds. + /// + SamplingDistance = 0x4, + /// + /// param1- frequency per second. + /// + TouchMoveFrequency = 0x8, + } + + /// + /// + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void GetTouchInput(TouchInput flag, ref int param1, ref int param2, ref int param3); + + /// + /// + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void SetTouchInput(TouchInput flag, int param1, int param2, int param3); + + internal static CollectionMode _collectionMode = CollectionMode.GestureOnly; + internal static CollectionMethod _collectionMethod = CollectionMethod.Managed; + + internal static TouchCollector _touchCollector = new TouchCollector(); + internal static uint _collectionBufferSize = 200; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/TouchInterface.cs b/source/nanoFramework.Graphics.Wpf/Native/TouchInterface.cs new file mode 100644 index 0000000..8266fad --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/TouchInterface.cs @@ -0,0 +1,277 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.CompilerServices; +using nanoFramework.Runtime.Events; +using static nanoFramework.UI.Temporary; + +namespace nanoFramework.Touch +{ + /// + [FlagsAttribute] + public enum TouchInputFlags : uint + { + /// + + None = 0x00, + + /// + Primary = 0x0010, //The Primary flag denotes the input that is passed to the single-touch Stylus events provided + + //no controls handle the Touch events. This flag should be set on the TouchInput structure that represents + //the first finger down as it moves around up to and including the point it is released. + + /// + Pen = 0x0040, //Hardware support is optional, but providing it allows for potentially richer applications. + + /// + Palm = 0x0080, //Hardware support is optional, but providing it allows for potentially richer applications. + } + + /// + /// IMPORTANT - This must be in sync with code in PAL and also nanoFramework + /// + /// + public enum TouchMessages : byte + { + /// + Down = 1, + /// + Up = 2, + /// + Move = 3, + } + + /// + public class TouchInput + { + /// + public int X; + /// + public int Y; + /// + public byte SourceID; + /// + public TouchInputFlags Flags; + /// + public uint ContactWidth; + /// + public uint ContactHeight; + } + + /// + public class TouchEvent : BaseEvent + { + /// + public DateTime Time; + /// + public TouchInput[] Touches; + } + + /// + /// + /// + public class TouchScreenEventArgs : nanoFramework.Runtime.Events.EventArgs + { + // Fields + /// + public TouchInput[] Touches; + /// + public DateTime TimeStamp; + /// + public object Target; + + // Methods + /// + /// + /// + /// + /// + /// + public TouchScreenEventArgs(DateTime timestamp, TouchInput[] touches, object target) + { + this.Touches = touches; + this.TimeStamp = timestamp; + this.Target = target; + } + + /// + /// + /// + /// + /// + /// + public void GetPosition(int touchIndex, out int x, out int y) + { + x = Touches[touchIndex].X; + y = Touches[touchIndex].Y; + } + } + + //--// + + /// + public delegate void TouchScreenEventHandler(object sender, TouchScreenEventArgs e); + + //--// + + /// + public enum TouchGesture : uint + { + /// + NoGesture = 0, //Can be used to represent an error gesture or unknown gesture + + //Standard Win7 Gestures + /// + Begin = 1, //Used to identify the beginning of a Gesture Sequence; App can use this to highlight UIElement or some other sort of notification. + /// + End = 2, //Used to identify the end of a gesture sequence; Fired when last finger involved in a gesture is removed. + + // Standard stylus (single touch) gestues + /// + Right = 3, + /// + UpRight = 4, + /// + Up = 5, + /// + UpLeft = 6, + /// + Left = 7, + /// + DownLeft = 8, + /// + Down = 9, + /// + DownRight = 10, + /// + Tap = 11, + /// + DoubleTap = 12, + + // Multi-touch gestures + /// + Zoom = 114, //Equivalent to your "Pinch" gesture + /// + Pan = 115, //Equivalent to your "Scroll" gesture + /// + Rotate = 116, + /// + TwoFingerTap = 117, + /// + Rollover = 118, // Press and tap + + //Additional NetMF gestures + /// + UserDefined = 200, + } + + /// + public class TouchGestureEventArgs : EventArgs + { + /// + public readonly DateTime Timestamp; + + /// + public TouchGesture Gesture; + + /// X and Y form the center location of the gesture for multi-touch or the starting location for single touch + /// + public int X; + /// + public int Y; + + /// 2 bytes for gesture-specific arguments. + /// TouchGesture.Zoom: Arguments = distance between fingers + /// TouchGesture.Rotate: Arguments = angle in degrees (0-360) + /// + /// + public ushort Arguments; + + /// + /// + /// + public double Angle + { + get + { + return (double)(Arguments); + } + } + } + + //--// + + /// + /// + /// + /// + /// + public delegate void TouchGestureEventHandler(object sender, TouchGestureEventArgs e); + + internal class TouchEventProcessor : IEventProcessor + { + /// + /// + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern public BaseEvent ProcessEvent(uint data1, uint data2, DateTime time); + } + + /// + public static class Touch + { + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.Synchronized)] + public static void Initialize(IEventListener touchEventListener) + { + if (_initialized) + return; + + // Lack of constructors in native code, forces to + // initialize TouchPanel before we initialize event sink. + + // We have only one touch panel right now. + // But this is to keep the options open for future. + _activeTouchPanel = new TouchPanel(); + _activeTouchPanel.Enabled = true; + + // Add a touch event processor. + EventSink.AddEventProcessor((EventCategory)EventCategoryEx.Touch, new TouchEventProcessor()); + + // Start the event sink process. This will pump + // events neatly out of the other world. + EventSink.AddEventListener((EventCategory)EventCategoryEx.Touch, touchEventListener); + + // Also add generic for Gesture stuff. + EventSink.AddEventListener((EventCategory)EventCategoryEx.Gesture, touchEventListener); + + _initialized = true; + } + + /// + public static TouchPanel ActiveTouchPanel + { + get + { + return _activeTouchPanel; + } + } + + private static bool _initialized = false; + private static TouchPanel _activeTouchPanel = null; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/TouchPanel.cs b/source/nanoFramework.Graphics.Wpf/Native/TouchPanel.cs new file mode 100644 index 0000000..7c20dc0 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/TouchPanel.cs @@ -0,0 +1,81 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System.Runtime.CompilerServices; + +namespace nanoFramework.Touch +{ + /// + /// + /// + public class TouchPanel + { + /// + /// + /// + public bool Enabled + { + get + { + return _enabled; + } + + set + { + EnableInternal(value); + _enabled = value; + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void SetCalibration(int cCalibrationPoints, + short[] screenXBuffer, + short[] screenYBuffer, + short[] uncalXBuffer, + short[] uncalYBuffer); + + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void GetCalibrationPointCount(ref int count); + + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void StartCalibration(); + + /// + /// + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void GetCalibrationPoint(int index, ref int x, ref int y); + + /// + /// + /// + /// + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private extern void EnableInternal(bool enable); + + private bool _enabled = false; + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/TouchScreen.cs b/source/nanoFramework.Graphics.Wpf/Native/TouchScreen.cs new file mode 100644 index 0000000..f089c7c --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/TouchScreen.cs @@ -0,0 +1,267 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.CompilerServices; +using nanoFramework.Runtime.Events; +using nanoFramework.UI; +using static nanoFramework.UI.Temporary; + +namespace nanoFramework.Touch +{ + /// + /// + /// + public class TouchScreen : IEventListener + { + /// + /// + /// + public class ActiveRectangle + { + /// + /// + /// + /// + /// + /// + /// + /// + public ActiveRectangle(int x, int y, int width, int height, object target) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + this.Target = target; + } + + /// + /// + /// + /// + /// + public bool Contains(TouchInput input) + { + if ( + input.X >= this.X && + input.X < this.X + this.Width && + input.Y >= this.Y && + input.Y < this.Y + this.Height + ) + { + return true; + } + return false; + } + + //--// + + /// + /// + /// + public readonly int X; + + /// + /// + /// + public readonly int Y; + + /// + /// + /// + public readonly int Width; + + /// + /// + /// + public readonly int Height; + + /// + /// + /// + public readonly object Target; + } + + //--// + + private ActiveRectangle[] _activeRegions; + private readonly int _maxWidth; + private readonly int _maxHeight; + + //--// + + /// + /// + /// + /// + public TouchScreen(ActiveRectangle[] activeRectangles) + { +// int bpp, orientation; +// Netmf Microsoft.SPOT.Hardware.HardwareProvider hwProvider = Microsoft.SPOT.Hardware.HardwareProvider.HwProvider; +// hwProvider.GetLCDMetrics(out _maxWidth, out _maxHeight, out bpp, out orientation); + +// New + + _maxWidth = SystemMetrics.ScreenWidth; + _maxHeight = SystemMetrics.ScreenHeight; + + if (activeRectangles == null || activeRectangles.Length == 0) + { + this.ActiveRegions = new ActiveRectangle[] { new ActiveRectangle(0, 0, _maxWidth, _maxHeight, null) }; + } + else + { + this.ActiveRegions = activeRectangles; + } + } + + /// + /// + /// + public event TouchScreenEventHandler OnTouchDown; + + /// + /// + /// + public event TouchScreenEventHandler OnTouchMove; + + /// + /// + /// + public event TouchScreenEventHandler OnTouchUp; + + /// + /// + /// + public event TouchGestureEventHandler OnGestureStarted; + + /// + /// + /// + public event TouchGestureEventHandler OnGestureChanged; + + + /// + /// + /// + public event TouchGestureEventHandler OnGestureEnded; + //--// + + /// + /// + /// + public ActiveRectangle[] ActiveRegions + { + set + { + // check + for (int i = 0; i < value.Length; ++i) + { + ActiveRectangle ar = value[i]; + if (ar.X < 0 || ar.X >= _maxWidth || ar.Y < 0 || ar.Y >= _maxHeight) + { + throw new ArgumentException(); + } + } + + _activeRegions = value; + } + get + { + return _activeRegions; + } + } + + [MethodImplAttribute(MethodImplOptions.Synchronized)] + void IEventListener.InitializeForEventSource() + { + } + + [MethodImplAttribute(MethodImplOptions.Synchronized)] + bool IEventListener.OnEvent(BaseEvent ev) + { + // Process known events, otherwise forward as generic to MainWindow. + // + + TouchEvent touchEvent = ev as TouchEvent; + if (touchEvent != null) + { + // dispatch only when the event is in the active area + for (int i = 0; i < _activeRegions.Length; ++i) + { + ActiveRectangle ar = _activeRegions[i]; + + // only check the first + if (ar.Contains(touchEvent.Touches[0])) + { + TouchScreenEventArgs tea = new TouchScreenEventArgs(touchEvent.Time, touchEvent.Touches, ar.Target); + + switch ((TouchMessages)touchEvent.Message) + { + case TouchMessages.Down: + if (OnTouchDown != null) + { + OnTouchDown(this, tea); + } + break; + case TouchMessages.Up: + if (OnTouchUp != null) + { + OnTouchUp(this, tea); + } + break; + case TouchMessages.Move: + if (OnTouchMove != null) + { + OnTouchMove(this, tea); + } + break; + } + } + } + + return true; + } + else if (ev is GenericEventEx) + { + GenericEventEx genericEventEx = (GenericEventEx)ev; + switch ((EventCategory)genericEventEx.Category) + { + case (EventCategory)EventCategoryEx.Gesture: + { + TouchGestureEventArgs ge = new TouchGestureEventArgs(); + + ge.Gesture = (TouchGesture)genericEventEx.Message; + ge.X = genericEventEx.X; + ge.Y = genericEventEx.Y; + ge.Arguments = (ushort)genericEventEx.Data; + + if (ge.Gesture == TouchGesture.Begin && OnGestureStarted != null) + { + OnGestureStarted(this, ge); + } + else if (ge.Gesture == TouchGesture.End && OnGestureEnded != null) + { + OnGestureEnded(this, ge); + } + else if (OnGestureChanged != null) + { + OnGestureChanged(this, ge); + } + + break; + } + default: + break; + } + } + return false; + } + } +} + + diff --git a/source/nanoFramework.Graphics.Wpf/Native/temporary.cs b/source/nanoFramework.Graphics.Wpf/Native/temporary.cs new file mode 100644 index 0000000..d4556d8 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Native/temporary.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// Portions Copyright (c) Microsoft Corporation. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.UI +{ + /// + /// Temporary class to extend the EventCatory enum found in the nanoFramework.Runtime.Events + /// Need to merge these into this class update references and remove this file + /// + public static class Temporary + { + /// + /// Temporary enum with unique values within the EventCategory enum range found in nanoFramework.Runtime.Events + /// + public enum EventCategoryEx + { + /// + /// Specifies a Touch Event + /// + Touch = 80, + + /// + /// Specifies a Gesture Event + /// + Gesture = 90 + } + } +} diff --git a/source/nanoFramework.Graphics.Wpf/Properties/AssemblyInfo.cs b/source/nanoFramework.Graphics.Wpf/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c8006cf --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/Properties/AssemblyInfo.cs @@ -0,0 +1,15 @@ +using System.Reflection; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("nanoFramework.Graphics.Wpf")] +[assembly: AssemblyCompany("nanoFramework Contributors")] +[assembly: AssemblyProduct("nanoFramework.Graphics.Wpf")] +[assembly: AssemblyCopyright("Copyright © nanoFramework Contributors 2019")] + +//////////////////////////////////////////////////////////////// +// update this whenever the native assembly signature changes // +[assembly: AssemblyNativeVersion("1.0.0.0")] +//////////////////////////////////////////////////////////////// + diff --git a/source/nanoFramework.Graphics.Wpf/app.config b/source/nanoFramework.Graphics.Wpf/app.config new file mode 100644 index 0000000..a0f6add --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf/key.snk b/source/nanoFramework.Graphics.Wpf/key.snk new file mode 100644 index 0000000000000000000000000000000000000000..67c9bb0ad77fd9cfb31a5fe1f8e4f6537f8883f8 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096IAgVrqn?0oVUK<}}z@wqRV>QE=V9G(P zv&4>n;N)r`28$iZ}__~(k83t)%SuJd!((DT{8XK)X~rK64E`*F3l z_5^K&AM;wi;^44^3SyC?622Htvk|)gU+)k0iCdaqB2%u@FR*KYku>(Zol_KusD7d= zA40qu=-~L94PXsGJ#eO@FlCr&O~0PO!6o-*GQq`zy7)g4B)IFgLhI0vfH=+L6i;`a zn(iWy{`8{{oj}vGf9S9M=48~NC!$coEAg@m^H3NXd_JrnT}HOd0AR%m^g zLg4+2jC7g-iYgo9N=!v@nv=I99O}eXu|AyULz5*Z*LDuQt4pP~UX|Zf`1Y#v4}kU8;^ac8l5!?oeMbz%wA_dHw*Ud81*FK}w1r zw3t~`4#bY+anQY5XU!i9#lc>IiLAxX~!c5@{0(|Buf%3XVh2FE%G?X~1 z7e5uWO?|rx0b=vcr3gpq3{-d$I8fk&Qrx<8j#tO3HOsxjGMJn4IT0*|11Oqu{gBd3 z&+6?KA|)R~alAn@8a!Y9Eq$TwFM@mu$U=dJ?PzCQY8jWLBlv^YEFIb8Fvhr*vMTOc zG+a5pRFEs9BT_>6MCF{WdN&DTsp^F!N=e1<>cRNkafH_Fr3`v9f%5G2(}LQ$j`vCM iwz?1qR;-^j3D_^H<{zl%^r6ivhR`zlIvhi1+jkY8izavg literal 0 HcmV?d00001 diff --git a/source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj b/source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj new file mode 100644 index 0000000..b8af241 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj @@ -0,0 +1,212 @@ + + + + $(MSBuildToolsPath)..\..\..\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + e30ae2aa-bed0-4aa8-ac22-04c274898894 + Library + Properties + 512 + nanoFramework.Graphics.Wpf + nanoFramework.Graphics.Wpf + v1.0 + True + bin\$(Configuration)\nanoFramework.Graphics.Wpf.xml + + + true + + + key.snk + + + false + + + + + false + true + true + + false + true + + true + true + Stubs\nf_graphics_wpf_native + nf_graphics_wpf + false + nanoFramework.Graphics.Wpf + + + + false + + + + + false + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\packages\nanoFramework.CoreLibrary.1.5.1-preview.1\lib\mscorlib.dll + True + + + ..\packages\nanoFramework.Runtime.Events.1.4.1-preview.3\lib\nanoFramework.Runtime.Events.dll + True + + + ..\packages\nanoFramework.ResourceManager.1.0.0-preview.4\lib\nanoFramework.ResourceManager.dll + True + + + ..\packages\nanoFramework.System.Collections.1.0.0-preview.6\lib\nanoFramework.System.Collections.dll + True + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. + + + + \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj~HEAD b/source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj~HEAD new file mode 100644 index 0000000..2689488 --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj~HEAD @@ -0,0 +1,230 @@ + + + + $(MSBuildToolsPath)..\..\..\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + e30ae2aa-bed0-4aa8-ac22-04c274898894 + Library + Properties + 512 + Sytem + nanoFramework.Graphics.Wpf + v1.0 + True + bin\$(Configuration)\nanoFramework.Graphics.Wpf.xml + + + true + + + key.snk + + + false + + + + + false + true + true + + false + true + + true + true + Stubs\nf_graphics_wpf_native + nf_graphics_wpf + false + nanoFramework.Graphics.Wpf + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<<<<<<< HEAD:nanoFramework.Graphics.Wpf/nanoFramework.Graphics.Wpf.nfproj + + ..\packages\nanoFramework.CoreLibrary.1.5.1-preview.1\lib\mscorlib.dll + True + True + + + ..\packages\nanoFramework.ResourceManager.1.0.0-preview.4\lib\nanoFramework.ResourceManager.dll + True + True + + + ..\packages\nanoFramework.Runtime.Events.1.4.1-preview.3\lib\nanoFramework.Runtime.Events.dll + True + True + + + ..\packages\nanoFramework.Runtime.Native.1.2.0-preview.7\lib\nanoFramework.Runtime.Native.dll + True + True + + + ..\packages\nanoFramework.System.Collections.1.0.0-preview.6\lib\nanoFramework.System.Collections.dll +======= + + ..\packages\nanoFramework.CoreLibrary.1.4.0-preview.3\lib\mscorlib.dll + True + True + + + ..\packages\nanoFramework.ResourceManager.1.0.0-preview.1\lib\nanoFramework.ResourceManager.dll + True + True + + + ..\packages\nanoFramework.Runtime.Events.1.4.0-preview.3\lib\nanoFramework.Runtime.Events.dll + True + True + + + ..\packages\nanoFramework.Runtime.Native.1.2.0-preview.4\lib\nanoFramework.Runtime.Native.dll +>>>>>>> f6dba80... Added colours:nanoFramework.Graphics/nanoFramework.Graphics.Wpf.nfproj + True + True + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. + + + + \ No newline at end of file diff --git a/source/nanoFramework.Graphics.Wpf/packages.config b/source/nanoFramework.Graphics.Wpf/packages.config new file mode 100644 index 0000000..7eff7fb --- /dev/null +++ b/source/nanoFramework.Graphics.Wpf/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/source/readme.txt b/source/readme.txt new file mode 100644 index 0000000..c2ac4a9 --- /dev/null +++ b/source/readme.txt @@ -0,0 +1,21 @@ + _____ _ + _ __ __ _ _ __ ___ | ___| __ __ _ _ __ ___ _____ _____ _ __| | __ + | '_ \ / _` | '_ \ / _ \| |_ | '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / + | | | | (_| | | | | (_) | _|| | | (_| | | | | | | __/\ V V / (_) | | | < + |_| |_|\__,_|_| |_|\___/|_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ + +=================================================================================== + +API docs: https://docs.nanoframework.net/api/nanoFramework.Graphics.Wpf.html + +Browse our samples repository: https://github.com/nanoframework/samples + +Check our documentation online: https://docs.nanoframework.net/ + +Join our lively Discord community: https://discord.gg/gCyBu8T + +Report issues: https://github.com/nanoframework/Home/issues + +Follow us on Twitter: https://twitter.com/nanoframework + +Follow our YouTube channel: https://www.youtube.com/c/nanoFramework diff --git a/source/version.json b/source/version.json new file mode 100644 index 0000000..64fbc63 --- /dev/null +++ b/source/version.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "1.0.6-preview.{height}", + "release": { + "branchName" : "release-v{version}", + "versionIncrement" : "minor", + "firstUnstableTag" : "preview" + }, + "assemblyVersion": { + "precision": "revision" + }, + "semVer1NumericIdentifierPadding": 3, + "nugetPackageVersion": { + "semVer": 2 + }, + "publicReleaseRefSpec": [ + "^refs/heads/master$", + "^refs/heads/v\\d+(?:\\.\\d+)?$" + ], + "cloudBuild": { + "setVersionVariables": true, + "setAllVariables": true + } +} diff --git a/template.vssettings b/template.vssettings new file mode 100644 index 0000000..0485070 --- /dev/null +++ b/template.vssettings @@ -0,0 +1,74 @@ + + + + + + + 4 + false + 4 + true + + + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 2 + 1 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + + + +