Compare commits

...
Sign in to create a new pull request.

14 commits

Author SHA1 Message Date
Pat Hartl
8d8d9166fb GitHub action
Some checks failed
Build Photino.Native / Publish .nuspec (push) Has been cancelled
Build Photino.Native / Build_Win_x64 (push) Has been cancelled
Build Photino.Native / Build_Win_Arm64 (push) Has been cancelled
Build Photino.Native / Build_Linux_x64 (push) Has been cancelled
Build Photino.Native / Build_Mac_Universal (push) Has been cancelled
2025-10-24 17:31:31 -05:00
Pat Hartl
f0d71308ec Merge branch 'feature/window-manipulation' into development
# Conflicts:
#	Photino.Native/Exports.cpp
#	Photino.Native/Photino.Windows.cpp
#	Photino.Native/Photino.h
#	Photino.Test/Photino.Test.csproj
#	Photino.Test/Program.cs
#	Photino.Test/wwwroot/main.html
2025-10-24 17:17:41 -05:00
Pat Hartl
8f29187a67 Merge branch 'feature/taskbar-flash-progress' into development
# Conflicts:
#	Photino.Native/Exports.cpp
#	Photino.Native/Photino.Windows.cpp
#	Photino.Test/Photino.Test.csproj
2025-10-24 17:15:12 -05:00
Pat Hartl
602ca05732 Merge branch 'feature/focus-window' into development 2025-10-24 17:13:20 -05:00
Pat Hartl
639a82fc0e Merge branch 'feature/add-complex-notifications' into development
# Conflicts:
#	Photino.Native/Photino.Native.vcxproj
2025-10-24 17:13:09 -05:00
Pat Hartl
eb9ff6eef2 Copy output to test artifact directory 2025-10-24 17:12:45 -05:00
Pat Hartl
0117fe56a0 Add export 2025-10-22 19:00:00 -05:00
Pat Hartl
f59988bb55 Add function to focus the window 2025-10-22 18:58:35 -05:00
Pat Hartl
a9f781b289 Add functions for dragging and resizing windows 2025-10-18 20:54:35 -05:00
Pat Hartl
44b3ffb2b7 Fix image test path 2025-10-18 18:06:29 -05:00
Pat Hartl
762d13f4a4 Update WinToastLib, implement C#->C++ calls 2025-10-18 17:59:29 -05:00
Pat Hartl
a114a41e18 Add Notification type
Main definition of the Notification type. Each notification can have callbacks registered to it, so that activation/dismissal isn't handled globally. This will allow for more complex handling of notifications.
2025-10-17 20:50:36 -05:00
Pat Hartl
1b1ab615e7 Use setTextField 2025-09-07 18:54:25 -05:00
Pat Hartl
3e13e29af5 Add overload for showing complex notifications 2025-09-06 22:43:27 -05:00
18 changed files with 1602 additions and 400 deletions

View file

@ -0,0 +1,262 @@
name: Build Photino.Native
on:
push:
branches: [ master ]
workflow_dispatch:
env:
buildConfiguration: Release
MAJOR: 4
MINOR: 0
# Note: PATCH below is set per-job in "PublishPackage" using github.run_number
jobs:
Publish_Nuspec:
name: Publish .nuspec
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Upload .nuspec artifact
uses: actions/upload-artifact@v4
with:
name: .nuspec
path: ./Photino.Native/Photino.Native.nuspec
Build_Win_x64:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup NuGet
uses: NuGet/setup-nuget@v2
- name: Restore (Windows x64)
run: nuget restore Photino.Native.sln -NonInteractive
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Build (Windows x64)
run: >
msbuild ./Photino.Native/Photino.Native.vcxproj
/p:Configuration=${{ env.buildConfiguration }}
/p:Platform=x64
- name: Upload Windows x64 libs
uses: actions/upload-artifact@v4
with:
name: photino.Native-Windows-x64
path: ./Photino.Native/x64/${{ env.buildConfiguration }}
Build_Win_Arm64:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup NuGet
uses: NuGet/setup-nuget@v2
- name: Restore (Windows ARM64)
run: nuget restore Photino.Native.sln -NonInteractive
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Build (Windows ARM64)
run: >
msbuild ./Photino.Native/Photino.Native.vcxproj
/p:Configuration=${{ env.buildConfiguration }}
/p:Platform=ARM64
- name: Upload Windows ARM64 libs
uses: actions/upload-artifact@v4
with:
name: photino.Native-Windows-arm64
path: ./Photino.Native/arm64/${{ env.buildConfiguration }}
Build_Linux_x64:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Build (Linux x64)
shell: bash
run: |
mkdir -p ./Photino.Native/x64/${{ env.buildConfiguration }}
make linux-x64
mv ./lib/x64/Photino.Native.so ./Photino.Native/x64/${{ env.buildConfiguration }}/Photino.Native.so
- name: Upload Linux x64 libs
uses: actions/upload-artifact@v4
with:
name: photino.Native-Linux-x64
path: ./Photino.Native/x64/${{ env.buildConfiguration }}
# If/when you want to build Linux ARM64 on native hardware or via cross, uncomment and adapt:
# Build_Linux_Arm64:
# runs-on: ubuntu-24.04
# steps:
# - uses: actions/checkout@v4
# - name: Build (Linux ARM64)
# shell: bash
# run: |
# mkdir -p ./Photino.Native/arm64/${{ env.buildConfiguration }}
# make linux-dev
# mv ./lib/dev/Photino.Native.so ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.so
# - name: Upload Linux ARM64 libs
# uses: actions/upload-artifact@v4
# with:
# name: LinuxArm64Artifact
# path: ./Photino.Native/arm64/${{ env.buildConfiguration }}
Build_Mac_Universal:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build (macOS universal)
shell: bash
run: |
mkdir -p ./Photino.Native/x64/${{ env.buildConfiguration }}
mkdir -p ./Photino.Native/arm64/${{ env.buildConfiguration }}
make mac-universal
cp ./lib/x64/Photino.Native.dylib ./Photino.Native/x64/${{ env.buildConfiguration }}/Photino.Native.dylib
cp ./lib/x64/Photino.Native.dylib ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.dylib
- name: Upload macOS x64 dylib
uses: actions/upload-artifact@v4
with:
name: photino.Native-macOS-x64
path: ./Photino.Native/x64/${{ env.buildConfiguration }}/Photino.Native.dylib
- name: Upload macOS ARM64 dylib
uses: actions/upload-artifact@v4
with:
name: photino.Native-macOS-arm64
path: ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.dylib
# Copy_Manual_Arm64_Releases:
# runs-on: ubuntu-24.04
# steps:
# - uses: actions/checkout@v4
#
# - name: Copy ARM64 native assets
# shell: bash
# run: |
# mkdir -p ./Photino.Native/arm64/${{ env.buildConfiguration }}
# mv ./manual-arm-release/linux-arm64/Photino.Native.so ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.so
#
# - name: Upload Linux ARM64 libs
# uses: actions/upload-artifact@v4
# with:
# name: LinuxArm64Artifact
# path: ./Photino.Native/arm64/${{ env.buildConfiguration }}
# PublishPackage:
# runs-on: windows-latest
# needs:
# - Publish_Nuspec
# - Build_Win_x64
# - Build_Win_Arm64
# - Build_Linux_x64
# - Build_Mac_Universal
# - Copy_Manual_Arm64_Releases
# steps:
# - name: Prepare workspace directory
# run: mkdir $env:GITHUB_WORKSPACE\pkg
#
# - name: Download NuspecArtifact
# uses: actions/download-artifact@v4
# with:
# name: NuspecArtifact
# path: ${{ github.workspace }}\pkg
#
# - name: Download WindowsIntelArtifact
# uses: actions/download-artifact@v4
# with:
# name: WindowsIntelArtifact
# path: ${{ github.workspace }}\pkg\x64
#
# - name: Download WindowsArm64Artifact
# uses: actions/download-artifact@v4
# with:
# name: WindowsArm64Artifact
# path: ${{ github.workspace }}\pkg\arm64
#
# - name: Download LinuxIntelArtifact
# uses: actions/download-artifact@v4
# with:
# name: LinuxIntelArtifact
# path: ${{ github.workspace }}\pkg\x64
#
# - name: Download LinuxArm64Artifact
# uses: actions/download-artifact@v4
# with:
# name: LinuxArm64Artifact
# path: ${{ github.workspace }}\pkg\arm64
#
# - name: Download MacOsIntelArtifact
# uses: actions/download-artifact@v4
# with:
# name: MacOsIntelArtifact
# path: ${{ github.workspace }}\pkg\x64
#
# - name: Download MacOsArm64Artifact
# uses: actions/download-artifact@v4
# with:
# name: MacOsArm64Artifact
# path: ${{ github.workspace }}\pkg\arm64
#
# - name: List downloaded files (root)
# shell: pwsh
# run: Get-ChildItem -Recurse $env:GITHUB_WORKSPACE\pkg | Format-Table -AutoSize
#
# - name: List x64
# shell: pwsh
# run: Get-ChildItem -Recurse $env:GITHUB_WORKSPACE\pkg\x64 | Format-Table -AutoSize
#
# - name: List arm64
# shell: pwsh
# run: Get-ChildItem -Recurse $env:GITHUB_WORKSPACE\pkg\arm64 | Format-Table -AutoSize
#
# - name: Setup NuGet
# uses: NuGet/setup-nuget@v2
#
# - name: Compute version
# id: ver
# shell: pwsh
# run: |
# $patch = "${{ github.run_number }}"
# $version = "${{ env.MAJOR }}.${{ env.MINOR }}.$patch"
# "patch=$patch" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
# "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
# Write-Host "Version: $version"
#
# - name: Pack nupkg
# shell: pwsh
# working-directory: ${{ github.workspace }}\pkg
# run: |
# nuget pack Photino.Native.nuspec `
# -Version "${{ steps.ver.outputs.version }}" `
# -NonInteractive `
# -Properties "version=${{ steps.ver.outputs.version }}"
#
# - name: Upload nupkg as artifact (optional)
# uses: actions/upload-artifact@v4
# with:
# name: Photino.Native.nupkg
# path: ${{ github.workspace }}\pkg\**\*.nupkg
#
# - name: Push to nuget.org
# if: ${{ github.ref == 'refs/heads/master' }}
# shell: pwsh
# working-directory: ${{ github.workspace }}\pkg
# env:
# NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
# run: |
# nuget push **\*.nupkg `
# -Source "https://api.nuget.org/v3/index.json" `
# -ApiKey "$env:NUGET_API_KEY" `
# -NonInteractive

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,7 @@
/* * Copyright (C) 2016-2019 Mohammed Boujemaoui <mohabouje@gmail.com>
/**
* MIT License
*
* Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui <mohabouje@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
@ -20,6 +23,7 @@
#ifndef WINTOASTLIB_H
#define WINTOASTLIB_H
#include <Windows.h>
#include <sdkddkver.h>
#include <WinUser.h>
@ -38,27 +42,32 @@
#include <string.h>
#include <vector>
#include <map>
#include <memory>
using namespace Microsoft::WRL;
using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::UI::Notifications;
using namespace Windows::Foundation;
namespace WinToastLib {
void setDebugOutputEnabled(bool enabled);
class IWinToastHandler {
public:
enum WinToastDismissalReason {
UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden,
TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
};
virtual ~IWinToastHandler() = default;
virtual void toastActivated() const = 0;
virtual void toastActivated(int actionIndex) const = 0;
virtual ~IWinToastHandler() = default;
virtual void toastActivated() const = 0;
virtual void toastActivated(int actionIndex) const = 0;
virtual void toastActivated(std::wstring response) const = 0;
virtual void toastDismissed(WinToastDismissalReason state) const = 0;
virtual void toastFailed() const = 0;
virtual void toastFailed() const = 0;
};
class WinToastTemplate {
@ -67,15 +76,16 @@ namespace WinToastLib {
enum Duration { System, Short, Long };
enum AudioOption { Default = 0, Silent, Loop };
enum TextField { FirstLine = 0, SecondLine, ThirdLine };
enum WinToastTemplateType {
ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01,
ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02,
ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03,
ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04,
Text01 = ToastTemplateType::ToastTemplateType_ToastText01,
Text02 = ToastTemplateType::ToastTemplateType_ToastText02,
Text03 = ToastTemplateType::ToastTemplateType_ToastText03,
Text04 = ToastTemplateType::ToastTemplateType_ToastText04,
Text01 = ToastTemplateType::ToastTemplateType_ToastText01,
Text02 = ToastTemplateType::ToastTemplateType_ToastText02,
Text03 = ToastTemplateType::ToastTemplateType_ToastText03,
Text04 = ToastTemplateType::ToastTemplateType_ToastText04
};
enum AudioSystemFile {
@ -107,49 +117,67 @@ namespace WinToastLib {
Call10,
};
enum CropHint {
Square,
Circle,
};
WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02);
~WinToastTemplate();
void setFirstLine(_In_ const std::wstring& text);
void setSecondLine(_In_ const std::wstring& text);
void setThirdLine(_In_ const std::wstring& text);
void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
void setAttributionText(_In_ const std::wstring& attributionText);
void setImagePath(_In_ const std::wstring& imgPath);
void setFirstLine(_In_ std::wstring const& text);
void setSecondLine(_In_ std::wstring const& text);
void setThirdLine(_In_ std::wstring const& text);
void setTextField(_In_ std::wstring const& txt, _In_ TextField pos);
void setAttributionText(_In_ std::wstring const& attributionText);
void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square);
void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false);
void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
void setAudioPath(_In_ const std::wstring& audioPath);
void setAudioPath(_In_ std::wstring const& audioPath);
void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
void setDuration(_In_ Duration duration);
void setExpiration(_In_ INT64 millisecondsFromNow);
void setScenario(_In_ Scenario scenario);
void addAction(_In_ const std::wstring& label);
void addAction(_In_ std::wstring const& label);
void addInput();
std::size_t textFieldsCount() const;
std::size_t actionsCount() const;
bool hasImage() const;
const std::vector<std::wstring>& textFields() const;
const std::wstring& textField(_In_ TextField pos) const;
const std::wstring& actionLabel(_In_ std::size_t pos) const;
const std::wstring& imagePath() const;
const std::wstring& audioPath() const;
const std::wstring& attributionText() const;
const std::wstring& scenario() const;
bool hasHeroImage() const;
std::vector<std::wstring> const& textFields() const;
std::wstring const& textField(_In_ TextField pos) const;
std::wstring const& actionLabel(_In_ std::size_t pos) const;
std::wstring const& imagePath() const;
std::wstring const& heroImagePath() const;
std::wstring const& audioPath() const;
std::wstring const& attributionText() const;
std::wstring const& scenario() const;
INT64 expiration() const;
WinToastTemplateType type() const;
WinToastTemplate::AudioOption audioOption() const;
Duration duration() const;
bool isToastGeneric() const;
bool isInlineHeroImage() const;
bool isCropHintCircle() const;
bool isInput() const;
private:
std::vector<std::wstring> _textFields{};
std::vector<std::wstring> _actions{};
std::wstring _imagePath{};
std::wstring _audioPath{};
std::wstring _attributionText{};
std::wstring _scenario{L"Default"};
INT64 _expiration{0};
AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
WinToastTemplateType _type{WinToastTemplateType::Text01};
Duration _duration{Duration::System};
bool _hasInput{false};
std::vector<std::wstring> _textFields{};
std::vector<std::wstring> _actions{};
std::wstring _imagePath{};
std::wstring _heroImagePath{};
bool _inlineHeroImage{false};
std::wstring _audioPath{};
std::wstring _attributionText{};
std::wstring _scenario{L"Default"};
INT64 _expiration{0};
AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
WinToastTemplateType _type{WinToastTemplateType::Text01};
Duration _duration{Duration::System};
CropHint _cropHint{CropHint::Square};
};
class WinToast {
@ -167,14 +195,14 @@ namespace WinToastLib {
};
enum ShortcutResult {
SHORTCUT_UNCHANGED = 0,
SHORTCUT_UNCHANGED = 0,
SHORTCUT_WAS_CHANGED = 1,
SHORTCUT_WAS_CREATED = 2,
SHORTCUT_MISSING_PARAMETERS = -1,
SHORTCUT_INCOMPATIBLE_OS = -2,
SHORTCUT_COM_INIT_FAILURE = -3,
SHORTCUT_CREATE_FAILED = -4
SHORTCUT_INCOMPATIBLE_OS = -2,
SHORTCUT_COM_INIT_FAILURE = -3,
SHORTCUT_CREATE_FAILED = -4
};
enum ShortcutPolicy {
@ -182,8 +210,7 @@ namespace WinToastLib {
SHORTCUT_POLICY_IGNORE = 0,
/* Require a shortcut with matching AUMI, don't create or modify an existing one. */
SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1,
/* Require a shortcut with matching AUMI, create if missing, modify if not matching.
* This is the default. */
/* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */
SHORTCUT_POLICY_REQUIRE_CREATE = 2,
};
@ -192,43 +219,100 @@ namespace WinToastLib {
static WinToast* instance();
static bool isCompatible();
static bool isSupportingModernFeatures();
static std::wstring configureAUMI(_In_ const std::wstring& companyName,
_In_ const std::wstring& productName,
_In_ const std::wstring& subProduct = std::wstring(),
_In_ const std::wstring& versionInformation = std::wstring());
static const std::wstring& strerror(_In_ WinToastError error);
static bool isWin10AnniversaryOrHigher();
static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName,
_In_ std::wstring const& subProduct = std::wstring(),
_In_ std::wstring const& versionInformation = std::wstring());
static std::wstring const& strerror(_In_ WinToastError error);
virtual bool initialize(_Out_opt_ WinToastError* error = nullptr);
virtual bool isInitialized() const;
virtual bool hideToast(_In_ INT64 id);
virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_opt_ WinToastError* error = nullptr);
virtual INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler,
_Out_opt_ WinToastError* error = nullptr);
virtual void clear();
virtual enum ShortcutResult createShortcut();
const std::wstring& appName() const;
const std::wstring& appUserModelId() const;
void setAppUserModelId(_In_ const std::wstring& aumi);
void setAppName(_In_ const std::wstring& appName);
std::wstring const& appName() const;
std::wstring const& appUserModelId() const;
void setAppUserModelId(_In_ std::wstring const& aumi);
void setAppName(_In_ std::wstring const& appName);
void setShortcutPolicy(_In_ ShortcutPolicy policy);
protected:
bool _isInitialized{false};
bool _hasCoInitialized{false};
ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
std::wstring _appName{};
std::wstring _aumi{};
std::map<INT64, ComPtr<IToastNotification>> _buffer{};
struct NotifyData {
NotifyData(){};
NotifyData(_In_ ComPtr<IToastNotification> notify, _In_ EventRegistrationToken activatedToken,
_In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) :
_notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {}
~NotifyData() {
RemoveTokens();
}
void RemoveTokens() {
if (!_readyForDeletion) {
return;
}
if (_previouslyTokenRemoved) {
return;
}
if (!_notify.Get()) {
return;
}
_notify->remove_Activated(_activatedToken);
_notify->remove_Dismissed(_dismissedToken);
_notify->remove_Failed(_failedToken);
_previouslyTokenRemoved = true;
}
void markAsReadyForDeletion() {
_readyForDeletion = true;
}
bool isReadyForDeletion() const {
return _readyForDeletion;
}
IToastNotification* notification() {
return _notify.Get();
}
private:
ComPtr<IToastNotification> _notify{nullptr};
EventRegistrationToken _activatedToken{};
EventRegistrationToken _dismissedToken{};
EventRegistrationToken _failedToken{};
bool _readyForDeletion{false};
bool _previouslyTokenRemoved{false};
};
bool _isInitialized{false};
bool _hasCoInitialized{false};
ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
std::wstring _appName{};
std::wstring _aumi{};
std::map<INT64, NotifyData> _buffer{};
void markAsReadyForDeletion(_In_ INT64 id);
HRESULT validateShellLinkHelper(_Out_ bool& wasChanged);
HRESULT createShellLinkHelper();
HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path);
HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default);
HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos);
HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario);
HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle);
HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage);
HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml);
HRESULT
setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path,
_In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default);
HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos);
HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text);
HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments);
HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration);
HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario);
HRESULT addInputHelper(_In_ IXmlDocument* xml);
ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value);
};
}
} // namespace WinToastLib
#endif // WINTOASTLIB_H

View file

@ -1,4 +1,5 @@
#include "Photino.Dialog.h"
#include "Photino.Notification.h"
#include "Photino.h"
#ifdef _WIN32
@ -56,6 +57,11 @@ extern "C"
instance->ClearBrowserAutoFill();
}
EXPORTED void Photino_Focus(Photino* instance)
{
instance->Focus();
}
EXPORTED void Photino_Close(Photino* instance)
{
instance->Close();
@ -266,6 +272,16 @@ extern "C"
instance->SetZoom(zoom);
}
EXPORTED void Photino_StartDragging(Photino* instance)
{
instance->StartDragging();
}
EXPORTED void Photino_StartResizing(Photino* instance, PhotinoWindowHitTestCode hitTestCode)
{
instance->StartResizing(hitTestCode);
}
EXPORTED void Photino_SetFlash(Photino* instance, bool state)
{
instance->SetFlash(state);
@ -280,11 +296,6 @@ extern "C"
{
instance->ClearProgress();
}
EXPORTED void Photino_ShowNotification(Photino* instance, AutoString title, AutoString body)
{
instance->ShowNotification(title, body);
}
EXPORTED void Photino_WaitForExit(Photino* instance)
{
@ -349,4 +360,54 @@ extern "C"
{
instance->Invoke(callback);
}
EXPORTED PhotinoNotification* PhotinoNotification_ctor(Photino* window)
{
return new PhotinoNotification(window);
}
EXPORTED void PhotinoNotification_dtor(PhotinoNotification* instance)
{
delete instance;
}
EXPORTED void PhotinoNotification_AddText(PhotinoNotification* instance, AutoString text)
{
instance->AddText(text);
}
EXPORTED void PhotinoNotification_AddAction(PhotinoNotification* instance, AutoString action)
{
instance->AddAction(action);
}
EXPORTED void PhotinoNotification_SetImagePath(PhotinoNotification* instance, AutoString imagePath)
{
instance->SetImagePath(imagePath);
}
EXPORTED void PhotinoNotification_SetType(PhotinoNotification* instance, PhotinoNotificationType type)
{
instance->SetType(type);
}
EXPORTED void PhotinoNotification_Show(PhotinoNotification* instance)
{
instance->Show();
}
EXPORTED void PhotinoNotification_SetActionCallback(PhotinoNotification* instance, ActionCallback callback)
{
instance->SetActionCallback(callback);
}
EXPORTED void PhotinoNotification_SetActivatedCallback(PhotinoNotification* instance, ActivatedCallback callback)
{
instance->SetActivatedCallback(callback);
}
EXPORTED void PhotinoNotification_SetDismissedCallback(PhotinoNotification* instance, DismissedCallback callback)
{
instance->SetDismissedCallback(callback);
}
}

View file

@ -333,6 +333,11 @@ void Photino::ClearBrowserAutoFill()
// TODO
}
void Photino::Focus()
{
gtk_window_present(GTK_WINDOW(_window));
}
void Photino::Close()
{
gtk_window_close(GTK_WINDOW(_window));

View file

@ -344,6 +344,12 @@ void Photino::ClearBrowserAutoFill()
//TODO
}
void Photino::Focus()
{
[NSApp activateIgnoringOtherApps:YES];
[_window makeKeyAndOrderFront:nil];
}
void Photino::Close()
{
if (_chromeless)

View file

@ -172,8 +172,9 @@ COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\Debug\net8.0\</Command>
<PostBuildEvent>
<Message>
</Message>
<Command>
</Command>
<Command>COPY .\$(OutDir)Photino.Native.dll ..\Photino.Test\bin\Debug\net8.0\
COPY .\$(OutDir)Photino.Native.pdb ..\Photino.Test\bin\Debug\net8.0\
COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\Debug\net8.0\</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -248,6 +249,8 @@ COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\ARM64\Debug\net8.0\</Comm
<ClCompile Include="Photino.Windows.cpp" />
<ClCompile Include="Photino.Windows.DarkMode.cpp" />
<ClCompile Include="Photino.Windows.Dialog.cpp" />
<ClCompile Include="Photino.Windows.Notification.cpp" />
<ClCompile Include="Photino.Windows.ToastHandler.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Dependencies\wintoastlib.h" />
@ -260,6 +263,7 @@ COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\ARM64\Debug\net8.0\</Comm
<ClInclude Include="Photino.Mac.WindowDelegate.h" />
<ClInclude Include="Photino.Mac.UrlSchemeHandler.h" />
<ClInclude Include="Photino.Dialog.h" />
<ClInclude Include="Photino.Notification.h" />
<ClInclude Include="Photino.Windows.ToastHandler.h" />
<ClInclude Include="Photino.Windows.DarkMode.h" />
<ClInclude Include="resource.h" />

View file

@ -36,6 +36,12 @@
<ClCompile Include="Photino.Linux.Dialog.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Photino.Windows.Notification.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Photino.Windows.ToastHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -105,6 +111,9 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Photino.Notification.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />

View file

@ -0,0 +1,91 @@
#pragma once
#ifndef PHOTINO_NOTIFICATION_H
#define PHOTINO_NOTIFICATION_H
#include "Photino.h"
#include <vector>
#ifdef _WIN32
class PhotinoWinToastHandler;
#endif
enum class PhotinoNotificationType {
ImageAndText01,
ImageAndText02,
ImageAndText03,
ImageAndText04,
Text01,
Text02,
Text03,
Text04,
};
enum class PhotinoNotificationDismissalReason {
UserCanceled,
ApplicationHidden,
TimedOut,
};
typedef void (*ActionCallback)(int actionIndex);
typedef void (*ActivatedCallback)();
typedef void (*DismissedCallback)(PhotinoNotificationDismissalReason reason);
class PhotinoNotification
{
private:
PhotinoNotificationType _type;
ActionCallback _actionCallback;
ActivatedCallback _activatedCallback;
DismissedCallback _dismissedCallback;
std::vector<AutoString> _textLines;
std::vector<AutoString> _actionNames;
AutoString _imagePath;
public:
PhotinoNotification(Photino* window);
#ifdef _WIN32
~PhotinoNotification();
#endif
std::vector<AutoString> GetActions() const;
void AddText(AutoString text);
void AddAction(AutoString action);
void SetImagePath(AutoString imagePath);
void SetType(PhotinoNotificationType type);
void Show();
void SetActionCallback(ActionCallback callback) { _actionCallback = callback; }
void SetActivatedCallback(ActivatedCallback callback) { _activatedCallback = callback; }
void SetDismissedCallback(DismissedCallback callback) { _dismissedCallback = callback; }
void InvokeAction(int actionIndex) const
{
if (_actionCallback)
return _actionCallback(actionIndex);
}
void InvokeActivated() const
{
if (_activatedCallback)
return _activatedCallback();
}
void InvokeDismissed(PhotinoNotificationDismissalReason reason) const
{
if (_dismissedCallback)
return _dismissedCallback(reason);
}
protected:
#ifdef _WIN32
Photino* _window{};
PhotinoWinToastHandler* _handler{};
#endif
};
#endif

View file

@ -0,0 +1,114 @@
#include "Photino.Notification.h"
#ifdef _WIN32
#include "Photino.Windows.ToastHandler.h"
#endif
WinToastLib::WinToastTemplate _toast;
PhotinoNotification::PhotinoNotification(Photino* window)
{
_window = window;
_handler = new PhotinoWinToastHandler(_window, this);
}
PhotinoNotification::~PhotinoNotification()
{
delete _handler;
}
std::vector<AutoString> PhotinoNotification::GetActions() const
{
return _actionNames;
}
void PhotinoNotification::SetType(PhotinoNotificationType type)
{
_type = type;
WinToastLib::WinToastTemplate::WinToastTemplateType winToastType;
switch (_type)
{
case PhotinoNotificationType::ImageAndText01:
winToastType = WinToastLib::WinToastTemplate::ImageAndText01;
break;
case PhotinoNotificationType::ImageAndText02:
winToastType = WinToastLib::WinToastTemplate::ImageAndText02;
break;
case PhotinoNotificationType::ImageAndText03:
winToastType = WinToastLib::WinToastTemplate::ImageAndText03;
break;
case PhotinoNotificationType::ImageAndText04:
winToastType = WinToastLib::WinToastTemplate::ImageAndText04;
break;
case PhotinoNotificationType::Text01:
winToastType = WinToastLib::WinToastTemplate::Text01;
break;
case PhotinoNotificationType::Text02:
winToastType = WinToastLib::WinToastTemplate::Text02;
break;
case PhotinoNotificationType::Text03:
winToastType = WinToastLib::WinToastTemplate::Text03;
break;
case PhotinoNotificationType::Text04:
winToastType = WinToastLib::WinToastTemplate::Text04;
break;
default:
winToastType = WinToastLib::WinToastTemplate::Text01;
break;
}
_toast = WinToastLib::WinToastTemplate(winToastType);
}
void PhotinoNotification::AddText(AutoString text)
{
AutoString conv = _window->ToUTF16String(text);
_textLines.push_back(conv);
switch (_textLines.size())
{
case 1:
_toast.setTextField(conv, WinToastLib::WinToastTemplate::FirstLine);
break;
case 2:
_toast.setTextField(conv, WinToastLib::WinToastTemplate::SecondLine);
break;
case 3:
_toast.setTextField(conv, WinToastLib::WinToastTemplate::ThirdLine);
break;
case 4:
_toast.setAttributionText(conv);
break;
}
}
void PhotinoNotification::AddAction(AutoString action)
{
_actionNames.push_back(action);
_toast.addAction(_window->ToUTF16String(action));
}
void PhotinoNotification::SetImagePath(AutoString imagePath)
{
_imagePath = imagePath;
_toast.setImagePath(_window->ToUTF16String(imagePath));
}
void PhotinoNotification::Show()
{
WinToastLib::WinToast::instance()->showToast(_toast, _handler);
}

View file

@ -0,0 +1,47 @@
#ifdef _WIN32
#include "Photino.Windows.ToastHandler.h"
#include "Photino.Notification.h"
using namespace WinToastLib;
void PhotinoWinToastHandler::toastActivated() const
{
ShowWindow(this->_window->getHwnd(), SW_SHOW);
ShowWindow(this->_window->getHwnd(), SW_RESTORE);
SetForegroundWindow(this->_window->getHwnd());
_notification->InvokeActivated();
}
void PhotinoWinToastHandler::toastActivated(int actionIndex) const
{
_notification->InvokeAction(actionIndex);
}
void PhotinoWinToastHandler::toastActivated(std::wstring response) const
{
// Already implemented above
}
void PhotinoWinToastHandler::toastDismissed(WinToastDismissalReason state) const
{
switch (state)
{
case WinToastDismissalReason::UserCanceled:
_notification->InvokeDismissed(PhotinoNotificationDismissalReason::UserCanceled);
break;
case WinToastDismissalReason::ApplicationHidden:
_notification->InvokeDismissed(PhotinoNotificationDismissalReason::ApplicationHidden);
break;
case WinToastDismissalReason::TimedOut:
_notification->InvokeDismissed(PhotinoNotificationDismissalReason::TimedOut);
break;
}
}
void PhotinoWinToastHandler::toastFailed() const
{
// Not implemented
}
#endif

View file

@ -1,42 +1,34 @@
#ifdef _WIN32
#ifndef TOASTHANDLER_H
#define TOASTHANDLER_H
#include "Photino.h"
#include <vector>
#include "Dependencies/wintoastlib.h"
#include <WinUser.h>
using namespace WinToastLib;
class WinToastHandler : public IWinToastHandler
class PhotinoNotification;
class PhotinoWinToastHandler : public WinToastLib::IWinToastHandler
{
private:
Photino* _window;
Photino* _window{};
PhotinoNotification* _notification{};
public:
WinToastHandler(Photino* window)
explicit PhotinoWinToastHandler(Photino* window, PhotinoNotification* notification)
{
this->_window = window;
_window = window;
_notification = notification;
}
void toastActivated() const
{
ShowWindow(this->_window->getHwnd(), SW_SHOW); // Make the window visible if it was hidden
ShowWindow(this->_window->getHwnd(), SW_RESTORE); // Next, restore it if it was minimized
SetForegroundWindow(this->_window->getHwnd()); // Finally, activate the window
}
void toastActivated(int actionIndex) const
{
//
}
void toastDismissed(WinToastDismissalReason state) const
{
//
}
void toastFailed() const
{
//
}
void toastActivated() const override;
void toastActivated(int actionIndex) const override;
void toastActivated(std::wstring response) const override;
void toastDismissed(WinToastDismissalReason state) const override;
void toastFailed() const override;
};
#endif
#endif

View file

@ -300,9 +300,7 @@ Photino::Photino(PhotinoInitParams* initParams)
if (_notificationRegistrationId != NULL)
WinToast::instance()->setAppUserModelId(_notificationRegistrationId);
this->_toastHandler = new WinToastHandler(this);
WinToast::instance()->initialize();
}
_dialog = new PhotinoDialog(this);
@ -520,6 +518,13 @@ void Photino::Center()
SetPosition(left, top);
}
void Photino::Focus()
{
ShowWindow(_hWnd, SW_SHOW);
ShowWindow(_hWnd, SW_RESTORE);
SetForegroundWindow(_hWnd);
}
void Photino::Close()
{
PostMessage(_hWnd, WM_CLOSE, NULL, NULL);
@ -857,6 +862,59 @@ void Photino::SetZoom(int zoom)
//MessageBox(nullptr, msg, L"Setter", MB_OK);
}
void Photino::StartDragging()
{
ReleaseCapture();
SendMessage(_hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
void Photino::StartResizing(PhotinoWindowHitTestCode hitTestCode)
{
int winHitTestCode = 0;
switch (hitTestCode)
{
case PhotinoWindowHitTestCode::Left:
winHitTestCode = HTLEFT;
break;
case PhotinoWindowHitTestCode::Right:
winHitTestCode = HTRIGHT;
break;
case PhotinoWindowHitTestCode::Top:
winHitTestCode = HTTOP;
break;
case PhotinoWindowHitTestCode::Bottom:
winHitTestCode = HTBOTTOM;
break;
case PhotinoWindowHitTestCode::TopLeft:
winHitTestCode = HTTOPLEFT;
break;
case PhotinoWindowHitTestCode::TopRight:
winHitTestCode = HTTOPRIGHT;
break;
case PhotinoWindowHitTestCode::BottomLeft:
winHitTestCode = HTBOTTOMLEFT;
break;
case PhotinoWindowHitTestCode::BottomRight:
winHitTestCode = HTBOTTOMRIGHT;
break;
}
if (winHitTestCode != 0)
{
ReleaseCapture();
SendMessage(_hWnd, WM_NCLBUTTONDOWN, winHitTestCode, 0);
}
}
void Photino::SetFlash(bool state)
{
FLASHWINFO f = { sizeof(FLASHWINFO) };
@ -921,23 +979,6 @@ void Photino::ClearProgress()
}
}
void Photino::ShowNotification(AutoString title, AutoString body)
{
title = ToUTF16String(title);
body = ToUTF16String(body);
if (_notificationsEnabled && WinToast::isCompatible())
{
WinToastTemplate toast = WinToastTemplate(WinToastTemplate::ImageAndText02);
toast.setTextField(title, WinToastTemplate::FirstLine);
toast.setTextField(body, WinToastTemplate::SecondLine);
if (this->_iconFileName != NULL)
toast.setImagePath(this->_iconFileName);
WinToast::instance()->showToast(toast, _toastHandler);
}
}
void Photino::WaitForExit()
{
messageLoopRootWindowHandle = _hWnd;

View file

@ -6,6 +6,7 @@
#include <WebView2.h>
typedef wchar_t *AutoString;
class WinToastHandler;
class PhotinoNotification;
#else
// AutoString for macOS/Linux
typedef char *AutoString;
@ -40,6 +41,18 @@ struct Monitor
double scale;
};
enum class PhotinoWindowHitTestCode
{
Left,
Right,
Top,
Bottom,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
};
enum class PhotinoWindowProgressState
{
Error,
@ -61,6 +74,7 @@ typedef bool (*ClosingCallback)();
typedef void (*FocusInCallback)();
typedef void (*FocusOutCallback)();
class PhotinoNotification;
class PhotinoDialog;
class Photino;
@ -87,7 +101,7 @@ struct PhotinoInitParams
MovedCallback *MovedHandler;
WebMessageReceivedCallback *WebMessageReceivedHandler;
AutoString CustomSchemeNames[16];
WebResourceRequestedCallback *CustomSchemeHandler;
WebResourceRequestedCallback* CustomSchemeHandler;
int Left;
int Top;
@ -103,6 +117,7 @@ struct PhotinoInitParams
bool Chromeless;
bool Transparent;
bool ContextMenuEnabled;
bool ZoomEnabled;
bool DevToolsEnabled;
bool FullScreen;
bool Maximized;
@ -252,6 +267,7 @@ public:
void Center();
void ClearBrowserAutoFill();
void Focus();
void Close();
void GetTransparentEnabled(bool *enabled);
@ -302,7 +318,10 @@ public:
void SetProgress(ULONGLONG current, ULONGLONG total, PhotinoWindowProgressState state);
void ClearProgress();
void ShowNotification(AutoString title, AutoString message);
// Window manipulation
void StartDragging();
void StartResizing(PhotinoWindowHitTestCode hitTest);
void WaitForExit();
// Callbacks

View file

@ -30,16 +30,23 @@
<ItemGroup>
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoDialogEnums.cs" Link="PhotinoDialogEnums.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoDllImports.cs" Link="PhotinoDllImports.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoDllImports.cs" Link="PhotinoDllImports.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNotificationDllImports.cs" Link="PhotinoNotificationDllImports.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoMonitorStruct.cs" Link="PhotinoMonitorStruct.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNativeDelegates.cs" Link="PhotinoNativeDelegates.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNativeParameters.cs" Link="PhotinoNativeParameters.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNetDelegates.cs" Link="PhotinoNetDelegates.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoWindow.NET.cs" Link="PhotinoWindow.NET.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoWindow.NET.cs" Link="PhotinoWindow.NET.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoWindowEnums.cs" Link="PhotinoWindowEnums.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNotification.cs" Link="PhotinoNotification.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNotificationType.cs" Link="PhotinoNotificationType.cs" />
<Compile Include="..\..\photino.NET\Photino.NET\PhotinoNotificationDismissalReason.cs" Link="PhotinoNotificationDismissalReason.cs" />
</ItemGroup>
<ItemGroup>
<None Update="testImage.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="wwwroot\main.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@ -51,7 +58,7 @@
</None>
</ItemGroup>
<Target Name="CopyNativeLibs" AfterTargets="Build" Condition="$(Platform) == 'x64'" >
<Target Name="CopyNativeLibs" AfterTargets="Build" Condition="$(Platform) == 'x64'">
<Copy SourceFiles="..\Photino.Native\x64\Debug\Photino.Native.dll" DestinationFolder="$(TargetDir)" Condition="$(IsWindows) == 'true'" />
<Copy SourceFiles="..\Photino.Native\x64\Debug\WebView2Loader.dll" DestinationFolder="$(TargetDir)" Condition="$(IsWindows) == 'true'" />
<Copy SourceFiles="../lib/x64/Photino.Native.dylib" DestinationFolder="$(TargetDir)" Condition="$(IsOSX) == 'true'" />

View file

@ -99,7 +99,7 @@ namespace Photino.NET
//.Offset(new Point(150, 150))
//.Offset(250, 250)
.SetNotificationRegistrationId("8FDF1B15-3408-47A6-8EF5-2B0676B76277") //Replaces the window title when registering toast notifications
.SetNotificationsEnabled(false)
.SetNotificationsEnabled(true)
//Browser settings
//.SetContextMenuEnabled(false)
@ -132,6 +132,7 @@ namespace Photino.NET
.SetLogVerbosity(_logEvents ? 2 : 0);
mainWindow.WaitForClose();
Console.WriteLine("Done Blocking!");
@ -412,7 +413,21 @@ namespace Photino.NET
}
else if (string.Compare(message, "toastNotification", true) == 0)
{
currentWindow.SendNotification("Toast Title", " Toast message! 🤖");
currentWindow
.CreateNotification(PhotinoNotificationType.ToastImageAndText04)
.AddText("Hello World!")
.AddText("Lorem ipsum dolor sit amet")
.AddText("Some third text!")
.SetImagePath($"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}\\testImage.jpg")
.AddAction("Launch", (notification) =>
{
Log(notification, "Launch notification action clicked!");
})
.AddAction("Ignore", (notification) =>
{
Log(notification, "Ignore notification action clicked!");
})
.Show();
}
else if (string.Compare(message, "showOpenFile", true) == 0)
{
@ -515,6 +530,42 @@ namespace Photino.NET
{
currentWindow.ClearProgress();
}
else if (string.Compare(message, "startDragging", true) == 0)
{
currentWindow.StartDragging();
}
else if (string.Compare(message, "startResizing-left", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.Left);
}
else if (string.Compare(message, "startResizing-topLeft", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.TopLeft);
}
else if (string.Compare(message, "startResizing-top", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.Top);
}
else if (string.Compare(message, "startResizing-topRight", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.TopRight);
}
else if (string.Compare(message, "startResizing-right", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.Right);
}
else if (string.Compare(message, "startResizing-bottomRight", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.BottomRight);
}
else if (string.Compare(message, "startResizing-bottom", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.Bottom);
}
else if (string.Compare(message, "startResizing-bottomLeft", true) == 0)
{
currentWindow.StartResizing(PhotinoWindowHitTestCode.BottomLeft);
}
else
throw new Exception($"Unknown message '{message}'");
}
@ -526,7 +577,23 @@ namespace Photino.NET
private static void WindowCreated(object sender, EventArgs e)
{
var window = (PhotinoWindow)sender;
Log(sender, "WindowCreated Callback Fired.");
window
.CreateNotification(PhotinoNotificationType.ToastImageAndText04)
.AddText("Hello World!")
.AddText("Lorem ipsum dolor sit amet")
.AddText("Some third text!")
.AddAction("Launch", (notification) =>
{
Log(notification, "Launch notification action clicked!");
})
.AddAction("Ignore", (notification) =>
{
Log(notification, "Ignore notification action clicked!");
})
.Show();
}
private static void WindowLocationChanged(object sender, Point location)

BIN
Photino.Test/testImage.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View file

@ -144,6 +144,14 @@
window.external.sendMessage('setMaxSize');
}
function StartDragging() {
window.external.sendMessage('startDragging');
}
function StartResizing(hitTest) {
window.external.sendMessage('startResizing-' + hitTest);
}
function SetProgress(state) {
window.external.sendMessage('setProgress-' + state + '-' + document.getElementById('progress-range').value);
}
@ -300,6 +308,7 @@
<button onclick="SetTransparent()">Transparent</button>
<button onclick="SetFullScreen()">Kiosk</button>
<button onclick="Close()">Close</button>
<button onmousedown="StartDragging()">Start Dragging</button>
<br>
<b>Size</b><br>
<button onclick="Minimize()">Minimize</button>
@ -371,5 +380,80 @@
</span>
<canvas id="canvas" style="visibility:hidden">required to render the photo, but not visible</canvas>
<button class="resize resize-left" onmousedown="StartResizing('left')"></button>
<button class="resize resize-top-left" onmousedown="StartResizing('topLeft')"></button>
<button class="resize resize-top" onmousedown="StartResizing('top')"></button>
<button class="resize resize-top-right" onmousedown="StartResizing('topRight')"></button>
<button class="resize resize-right" onmousedown="StartResizing('right')"></button>
<button class="resize resize-bottom-right" onmousedown="StartResizing('bottomRight')"></button>
<button class="resize resize-bottom" onmousedown="StartResizing('bottom')"></button>
<button class="resize resize-bottom-left" onmousedown="StartResizing('bottomLeft')"></button>
<style>
body {
padding: 10px
}
.resize {
position: fixed;
}
.resize-left {
width: 10px;
top: 10px;
left: 0px;
bottom: 10px;
}
.resize-top-left {
width: 10px;
height: 10px;
top: 0;
left: 0;
}
.resize-top {
height: 10px;
top: 0;
left: 10px;
right: 10px;
}
.resize-top-right {
width: 10px;
height: 10px;
top: 0px;
right: 0px;
}
.resize-right {
width: 10px;
top: 10px;
right: 0px;
bottom: 10px;
}
.resize-bottom-right {
width: 10px;
height: 10px;
right: 0px;
bottom: 0px;
}
.resize-bottom {
height: 10px;
left: 10px;
right: 10px;
bottom: 0px;
}
.resize-bottom-left {
width: 10px;
height: 10px;
left: 0px;
bottom: 0px;
}
</style>
</body>
</html>