diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..c2d6f4e1 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,83 @@ +# Update VARIANT in devcontainer.json to pick a Dart version +ARG VARIANT=2 +FROM dart:${VARIANT} + +# [Option] Install zsh +ARG INSTALL_ZSH="true" +# [Option] Upgrade OS packages to their latest versions +ARG UPGRADE_PACKAGES="true" + +# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies. +ARG USERNAME=vscode +ARG USER_HOME=/home/${USERNAME} +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +COPY library-scripts/*.sh /tmp/library-scripts/ +RUN apt-get update && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" + +# Add bin location to path +ENV PUB_CACHE="/usr/local/share/pub-cache" +ENV PATH="${PUB_CACHE}/bin:${PATH}" +RUN if ! cat /etc/group | grep -e "^pub-cache:" > /dev/null 2>&1; then groupadd -r pub-cache; fi \ + && usermod -a -G pub-cache ${USERNAME} \ + && umask 0002 \ + && mkdir -p ${PUB_CACHE} \ + && chown :pub-cache ${PUB_CACHE} \ + && sed -i -e "s/export PATH=/export PATH=\/usr\/local\/share\/pub-cache:/" /etc/profile.d/00-restore-env.sh \ + # + # Fix incorrect privs if present on directories - https://github.com/dart-lang/dart-docker/issues/62, https://github.com/dart-lang/sdk/issues/47093 + && chmod 755 "$DART_SDK" "$DART_SDK/bin" + +# Install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends \ + openjdk-11-jdk-headless wget curl git xz-utils zip unzip \ + clang cmake ninja-build pkg-config libgtk-3-dev \ + libwebkit2gtk-4.0-dev libkeybinder-3.0-dev libayatana-appindicator3-dev +# Clean up packages +RUN apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts + +# Switch to non-root user +USER ${USERNAME} +WORKDIR ${USER_HOME} + +# +# Android SDK +# https://developer.android.com/studio#downloads +# ENV ANDROID_SDK_TOOLS_VERSION=8512546 +# ENV ANDROID_PLATFORM_VERSION=33 +# ENV ANDROID_BUILD_TOOLS_VERSION=33.0.0 +# ENV ANDROID_HOME=~/android-sdk-linux +# ENV ANDROID_SDK_ROOT="$ANDROID_HOME" +# ENV PATH=${PATH}:${ANDROID_HOME}/cmdline-tools/cmdline-tools/bin:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/emulator + +# RUN curl -C - --output android-sdk-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip \ +# && mkdir -p ${ANDROID_HOME}/ \ +# && unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}/cmdline-tools/ \ +# && rm android-sdk-tools.zip \ +# && yes | sdkmanager --licenses \ +# && touch $HOME/.android/repositories.cfg \ +# && sdkmanager platform-tools \ +# && sdkmanager emulator \ +# && sdkmanager "platforms;android-${ANDROID_PLATFORM_VERSION}" "build-tools;$ANDROID_BUILD_TOOLS_VERSION" \ +# && sdkmanager --install "cmdline-tools;latest" +# # create emulator android +# RUN sdkmanager "system-images;android-${ANDROID_PLATFORM_VERSION};google_apis;x86_64" \ +# && avdmanager create avd -n Android${ANDROID_PLATFORM_VERSION} -k "system-images;android-${ANDROID_PLATFORM_VERSION};google_apis;x86_64" + +# +# Flutter SDK +# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux +ENV FLUTTER_CHANNEL="stable" +ENV FLUTTER_VERSION="3.3.10" +# Make sure to use the needed channel and version for this. +ENV FLUTTER_HOME=${USER_HOME}/flutter +ENV PATH=${PATH}:${FLUTTER_HOME}/bin + +RUN curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ + && tar -xf flutter.tar.xz -C ~ \ + && rm flutter.tar.xz \ + && flutter config --android-sdk "${ANDROID_SDK_ROOT}" \ + && yes | flutter doctor --android-licenses \ + && flutter config --no-analytics \ + && flutter update-packages diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..a570d3b7 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,38 @@ +{ + "name": "Dart (Community)", + "build": { + "dockerfile": "Dockerfile", + // Update VARIANT to pick a Dart version + "args": { "VARIANT": "2" } + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "dart-code.dart-code", + "dart-code.flutter" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a", + // "postCreateCommand": "flutter create test_project", + + // Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": {}, + "ghcr.io/devcontainers-contrib/features/cue-asdf:1": {}, + "ghcr.io/devcontainers-contrib/features/svu-asdf:1": {}, + "ghcr.io/eitsupi/devcontainer-features/go-task:1": {}, + "ghcr.io/dhoeric/features/google-cloud-cli:1": {}, + "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} + } +} diff --git a/.devcontainer/library-scripts/common-debian.sh b/.devcontainer/library-scripts/common-debian.sh new file mode 100644 index 00000000..efdca351 --- /dev/null +++ b/.devcontainer/library-scripts/common-debian.sh @@ -0,0 +1,454 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My Zsh! flag] [Add non-free packages] + +set -e + +INSTALL_ZSH=${1:-"true"} +USERNAME=${2:-"automatic"} +USER_UID=${3:-"automatic"} +USER_GID=${4:-"automatic"} +UPGRADE_PACKAGES=${5:-"true"} +INSTALL_OH_MYS=${6:-"true"} +ADD_NON_FREE_PACKAGES=${7:-"false"} +SCRIPT_DIR="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)" +MARKER_FILE="/usr/local/etc/vscode-dev-containers/common" + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Ensure that login shells get the correct path if the user updated the PATH using ENV. +rm -f /etc/profile.d/00-restore-env.sh +echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh +chmod +x /etc/profile.d/00-restore-env.sh + +# If in automatic mode, determine if a user already exists, if not use vscode +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=vscode + fi +elif [ "${USERNAME}" = "none" ]; then + USERNAME=root + USER_UID=0 + USER_GID=0 +fi + +# Load markers to see which steps have already run +if [ -f "${MARKER_FILE}" ]; then + echo "Marker file found:" + cat "${MARKER_FILE}" + source "${MARKER_FILE}" +fi + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Function to call apt-get if needed +apt_get_update_if_needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies +if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then + + package_list="apt-utils \ + openssh-client \ + gnupg2 \ + dirmngr \ + iproute2 \ + procps \ + lsof \ + htop \ + net-tools \ + psmisc \ + curl \ + wget \ + rsync \ + ca-certificates \ + unzip \ + zip \ + nano \ + vim-tiny \ + less \ + jq \ + lsb-release \ + apt-transport-https \ + dialog \ + libc6 \ + libgcc1 \ + libkrb5-3 \ + libgssapi-krb5-2 \ + libicu[0-9][0-9] \ + liblttng-ust[0-9] \ + libstdc++6 \ + zlib1g \ + locales \ + sudo \ + ncdu \ + man-db \ + strace \ + manpages \ + manpages-dev \ + init-system-helpers" + + # Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian + if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then + # Bring in variables from /etc/os-release like VERSION_CODENAME + . /etc/os-release + sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list + sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list + sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list + sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list + # Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html + sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list + echo "Running apt-get update..." + apt-get update + package_list="${package_list} manpages-posix manpages-posix-dev" + else + apt_get_update_if_needed + fi + + # Install libssl1.1 if available + if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then + package_list="${package_list} libssl1.1" + fi + + # Install appropriate version of libssl1.0.x if available + libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') + if [ "$(echo "$LIlibssl_packageBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then + if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then + # Debian 9 + package_list="${package_list} libssl1.0.2" + elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then + # Ubuntu 18.04, 16.04, earlier + package_list="${package_list} libssl1.0.0" + fi + fi + + echo "Packages to verify are installed: ${package_list}" + apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) + + # Install git if not already installed (may be more recent than distro version) + if ! type git > /dev/null 2>&1; then + apt-get -y install --no-install-recommends git + fi + + PACKAGES_ALREADY_INSTALLED="true" +fi + +# Get to latest versions of all packages +if [ "${UPGRADE_PACKAGES}" = "true" ]; then + apt_get_update_if_needed + apt-get -y upgrade --no-install-recommends + apt-get autoremove -y +fi + +# Ensure at least the en_US.UTF-8 UTF-8 locale is available. +# Common need for both applications and things like the agnoster ZSH theme. +if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen + locale-gen + LOCALE_ALREADY_SET="true" +fi + +# Create or update a non-root user to match UID/GID. +group_name="${USERNAME}" +if id -u ${USERNAME} > /dev/null 2>&1; then + # User exists, update if needed + if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then + group_name="$(id -gn $USERNAME)" + groupmod --gid $USER_GID ${group_name} + usermod --gid $USER_GID $USERNAME + fi + if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then + usermod --uid $USER_UID $USERNAME + fi +else + # Create user + if [ "${USER_GID}" = "automatic" ]; then + groupadd $USERNAME + else + groupadd --gid $USER_GID $USERNAME + fi + if [ "${USER_UID}" = "automatic" ]; then + useradd -s /bin/bash --gid $USERNAME -m $USERNAME + else + useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME + fi +fi + +# Add sudo support for non-root user +if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then + echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME + chmod 0440 /etc/sudoers.d/$USERNAME + EXISTING_NON_ROOT_USER="${USERNAME}" +fi + +# ** Shell customization section ** +if [ "${USERNAME}" = "root" ]; then + user_rc_path="/root" +else + user_rc_path="/home/${USERNAME}" +fi + +# Restore user .bashrc defaults from skeleton file if it doesn't exist or is empty +if [ ! -f "${user_rc_path}/.bashrc" ] || [ ! -s "${user_rc_path}/.bashrc" ] ; then + cp /etc/skel/.bashrc "${user_rc_path}/.bashrc" +fi + +# Restore user .profile defaults from skeleton file if it doesn't exist or is empty +if [ ! -f "${user_rc_path}/.profile" ] || [ ! -s "${user_rc_path}/.profile" ] ; then + cp /etc/skel/.profile "${user_rc_path}/.profile" +fi + +# .bashrc/.zshrc snippet +rc_snippet="$(cat << 'EOF' + +if [ -z "${USER}" ]; then export USER=$(whoami); fi +if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi + +# Display optional first run image specific notice if configured and terminal is interactive +if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then + if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then + cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" + elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then + cat "/workspaces/.codespaces/shared/first-run-notice.txt" + fi + mkdir -p "$HOME/.config/vscode-dev-containers" + # Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it + ((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &) +fi + +# Set the default git editor if not already set +if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then + if [ "${TERM_PROGRAM}" = "vscode" ]; then + if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then + export GIT_EDITOR="code-insiders --wait" + else + export GIT_EDITOR="code --wait" + fi + fi +fi + +EOF +)" + +# code shim, it fallbacks to code-insiders if code is not available +cat << 'EOF' > /usr/local/bin/code +#!/bin/sh + +get_in_path_except_current() { + which -a "$1" | grep -A1 "$0" | grep -v "$0" +} + +code="$(get_in_path_except_current code)" + +if [ -n "$code" ]; then + exec "$code" "$@" +elif [ "$(command -v code-insiders)" ]; then + exec code-insiders "$@" +else + echo "code or code-insiders is not installed" >&2 + exit 127 +fi +EOF +chmod +x /usr/local/bin/code + +# systemctl shim - tells people to use 'service' if systemd is not running +cat << 'EOF' > /usr/local/bin/systemctl +#!/bin/sh +set -e +if [ -d "/run/systemd/system" ]; then + exec /bin/systemctl "$@" +else + echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services instead. e.g.: \n\nservice --status-all' +fi +EOF +chmod +x /usr/local/bin/systemctl + +# Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme +codespaces_bash="$(cat \ +<<'EOF' + +# Codespaces bash prompt theme +__bash_prompt() { + local userpart='`export XIT=$? \ + && [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \ + && [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`' + local gitbranch='`\ + if [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \ + export BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null); \ + if [ "${BRANCH}" != "" ]; then \ + echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \ + && if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \ + echo -n " \[\033[1;33m\]✗"; \ + fi \ + && echo -n "\[\033[0;36m\]) "; \ + fi; \ + fi`' + local lightblue='\[\033[1;34m\]' + local removecolor='\[\033[0m\]' + PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ " + unset -f __bash_prompt +} +__bash_prompt + +EOF +)" + +codespaces_zsh="$(cat \ +<<'EOF' +# Codespaces zsh prompt theme +__zsh_prompt() { + local prompt_username + if [ ! -z "${GITHUB_USER}" ]; then + prompt_username="@${GITHUB_USER}" + else + prompt_username="%n" + fi + PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow + PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd + PROMPT+='$([ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ] && git_prompt_info)' # Git status + PROMPT+='%{$fg[white]%}$ %{$reset_color%}' + unset -f __zsh_prompt +} +ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}" +ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " +ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})" +ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})" +__zsh_prompt + +EOF +)" + +# Add RC snippet and custom bash prompt +if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then + echo "${rc_snippet}" >> /etc/bash.bashrc + echo "${codespaces_bash}" >> "${user_rc_path}/.bashrc" + echo 'export PROMPT_DIRTRIM=4' >> "${user_rc_path}/.bashrc" + if [ "${USERNAME}" != "root" ]; then + echo "${codespaces_bash}" >> "/root/.bashrc" + echo 'export PROMPT_DIRTRIM=4' >> "/root/.bashrc" + fi + chown ${USERNAME}:${group_name} "${user_rc_path}/.bashrc" + RC_SNIPPET_ALREADY_ADDED="true" +fi + +# Optionally install and configure zsh and Oh My Zsh! +if [ "${INSTALL_ZSH}" = "true" ]; then + if ! type zsh > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get install -y zsh + fi + if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then + echo "${rc_snippet}" >> /etc/zsh/zshrc + ZSH_ALREADY_INSTALLED="true" + fi + + # Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme. + # See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script. + oh_my_install_dir="${user_rc_path}/.oh-my-zsh" + if [ ! -d "${oh_my_install_dir}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then + template_path="${oh_my_install_dir}/templates/zshrc.zsh-template" + user_rc_file="${user_rc_path}/.zshrc" + umask g-w,o-w + mkdir -p ${oh_my_install_dir} + git clone --depth=1 \ + -c core.eol=lf \ + -c core.autocrlf=false \ + -c fsck.zeroPaddedFilemode=ignore \ + -c fetch.fsck.zeroPaddedFilemode=ignore \ + -c receive.fsck.zeroPaddedFilemode=ignore \ + "https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1 + echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file} + sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${user_rc_file} + + mkdir -p ${oh_my_install_dir}/custom/themes + echo "${codespaces_zsh}" > "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme" + # Shrink git while still enabling updates + cd "${oh_my_install_dir}" + git repack -a -d -f --depth=1 --window=1 + # Copy to non-root user if one is specified + if [ "${USERNAME}" != "root" ]; then + cp -rf "${user_rc_file}" "${oh_my_install_dir}" /root + chown -R ${USERNAME}:${group_name} "${user_rc_path}" + fi + fi +fi + +# Persist image metadata info, script if meta.env found in same directory +meta_info_script="$(cat << 'EOF' +#!/bin/sh +. /usr/local/etc/vscode-dev-containers/meta.env + +# Minimal output +if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then + echo "${VERSION}" + exit 0 +elif [ "$1" = "release" ]; then + echo "${GIT_REPOSITORY_RELEASE}" + exit 0 +elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then + echo "${CONTENTS_URL}" + exit 0 +fi + +#Full output +echo +echo "Development container image information" +echo +if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi +if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi +if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi +if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi +if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi +if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi +if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi +echo +EOF +)" +if [ -f "${SCRIPT_DIR}/meta.env" ]; then + mkdir -p /usr/local/etc/vscode-dev-containers/ + cp -f "${SCRIPT_DIR}/meta.env" /usr/local/etc/vscode-dev-containers/meta.env + echo "${meta_info_script}" > /usr/local/bin/devcontainer-info + chmod +x /usr/local/bin/devcontainer-info +fi + +# Write marker file +mkdir -p "$(dirname "${MARKER_FILE}")" +echo -e "\ + PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\ + LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\ + EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\ + RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\ + ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}" + +echo "Done!" diff --git a/.gitignore b/.gitignore index d4acd1e9..5cc0eea2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,2 @@ -/Vendor/* -!/Vendor/7zip -!/Vendor/7zip/* -!/Vendor/Verpatch -!/Vendor/Verpatch/* -/Launchpad.ini -/Launchpad.build.ini -/Launchpad.json -/Build -/Dist -/Launchpad.zip -/Launchpad-*.zip -/Launchpad.exe /log.txt -/LaunchpadOverlay/bin -/LaunchpadOverlay/obj /.vs diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..e1c966ce --- /dev/null +++ b/.npmignore @@ -0,0 +1,6 @@ +README.md +test-project +definition-manifest.json +.devcontainer/library-scripts/README.md +.vscode +.npmignore diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 6b778b10..50a02a5f 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,7 +3,9 @@ { "name": "Win32", "includePath": [ - "${workspaceFolder}/**" + "${workspaceFolder}/**", + "${workspaceFolder}/launchpad_app/.dart_tool/**", + "E:/Tools/flutter/**" ], "defines": [ "_DEBUG", diff --git a/.vscode/launch.json b/.vscode/launch.json index 57ded091..944da2da 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,29 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Launchpad (app)", + "cwd": "launchpad_app", + "program": "lib/main.dart", + "request": "launch", + "type": "dart" + }, + { + "name": "Launchpad (app profile mode)", + "cwd": "launchpad_app", + "program": "lib/main.dart", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "Launchpad (app release mode)", + "cwd": "launchpad_app", + "program": "lib/main.dart", + "request": "launch", + "type": "dart", + "flutterMode": "release" + }, { "name": "Launchpad (Debug)", "type": "autohotkey", diff --git a/.vscode/settings.json b/.vscode/settings.json index fd7d893a..51390155 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,16 @@ { "cSpell.words": [ - "Multitool" - ] + "appmanifest", + "calloc", + "FOLDERID", + "installdir", + "libraryfolders", + "Multitool", + "r'riotclient", + "steamapps" + ], + "terminal.integrated.cwd": "${workspaceFolder}/launchpad_app", + "[dart]": { + "editor.formatOnSave": true + } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3002c722..4181697d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,27 +2,132 @@ "version": "2.0.0", "tasks": [ { - "label": "update-includes", - "command": "Vendor/AutoHotKey/AutoHotkey64.exe", - "type": "process", + "label": "flutter pub run build_runner watch", + "type": "flutter", + "command": "flutter", "args": [ - "Scripts/UpdateIncludes.ahk" + "pub", + "run", + "build_runner", + "watch", + "--delete-conflicting-outputs" + ], + "presentation": { + "reveal": "silent", + "revealProblems": "onProblem", + "panel": "dedicated", + "close": true + }, + "problemMatcher": [ + "$dart-build_runner" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "options": { + "cwd": "${workspaceFolder}/launchpad_app" + }, + "isBackground": true, + "detail": "", + "runOptions": { + "runOn": "folderOpen", + "instanceLimit": 1, + "reevaluateOnRerun": true + }, + "dependsOn": [ + "flutter pub get" + ], + }, + { + "label": "flutter pub run build_runner build", + "type": "flutter", + "command": "flutter", + "args": [ + "pub", + "run", + "build_runner", + "build", + "--delete-conflicting-outputs" + ], + "presentation": { + "reveal": "always", + "panel": "shared", + "showReuseMessage": true, + "close": false + }, + "problemMatcher": [ + "$dart-build_runner" ], "options": { - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/launchpad_app" + }, + "group": "none", + "detail": "" + }, + { + "label": "flutter pub get", + "type": "flutter", + "command": "flutter", + "args": [ + "pub", + "get" + ], + "presentation": { + "echo": true, + "focus": false, + "reveal": "silent", + "revealProblems": "onProblem", + "panel": "dedicated", + "close": true + }, + "options": { + "cwd": "${workspaceFolder}/launchpad_app" + }, + "group": "none", + "detail": "" + }, + { + "label": "flutter launcher icons", + "type": "flutter", + "command": "flutter", + "args": [ + "pub", + "run", + "flutter_launcher_icons" + ], + "group": "build", + "options": { + "cwd": "${workspaceFolder}/launchpad_app" } }, { - "label": "build-launchpad", - "command": "Vendor/AutoHotKey/AutoHotkey64.exe", + "label": "flutter native splash", + "type": "flutter", + "command": "flutter", + "args": [ + "pub", + "run", + "flutter_native_splash:create" + ], + "group": "build", + "options": { + "cwd": "${workspaceFolder}/launchpad_app" + } + }, + { + "label": "build-overlay", + "command": "msbuild", "type": "process", "args": [ - "Scripts/Build.ahk" + "Launchpad.sln", + "-property:Configuration=Release" ], "options": { - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/launchpad_legacy" }, - "problemMatcher": [] + "group": "build", + "problemMatcher": "$tsc" }, { "label": "build-overlay-debug", @@ -33,22 +138,36 @@ "-property:Configuration=Debug" ], "options": { - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/launchpad_legacy" }, + "group": "none", "problemMatcher": "$tsc" }, { - "label": "build-overlay", - "command": "msbuild", + "label": "update-includes (AHK)", + "command": "Vendor/AutoHotKey/AutoHotkey64.exe", "type": "process", "args": [ - "Launchpad.sln", - "-property:Configuration=Release" + "Scripts/UpdateIncludes.ahk" ], "options": { - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/launchpad_legacy" }, - "problemMatcher": "$tsc" + "group": "build", + "problemMatcher": [] + }, + { + "label": "build-launchpad (AHK)", + "command": "Vendor/AutoHotKey/AutoHotkey64.exe", + "type": "process", + "args": [ + "Scripts/Build.ahk" + ], + "options": { + "cwd": "${workspaceFolder}/launchpad_legacy" + }, + "group": "build", + "problemMatcher": [] } ] } diff --git a/Launchpad.sln b/Launchpad.sln index 250ecf0a..5e1ac66a 100644 --- a/Launchpad.sln +++ b/Launchpad.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31205.134 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LaunchpadOverlay", "LaunchpadOverlay\LaunchpadOverlay.vcxproj", "{23E282D0-E846-485C-B7A1-DA60F4BEA3D2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LaunchpadOverlay", "launchpad_overlay\LaunchpadOverlay.vcxproj", "{23E282D0-E846-485C-B7A1-DA60F4BEA3D2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchpadDotNet", "launchpad_dotnet\LaunchpadDotNet.csproj", "{6E69DA67-1C6B-4010-B6F7-E753CFF716CC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,6 +21,14 @@ Global {23E282D0-E846-485C-B7A1-DA60F4BEA3D2}.Release|x64.ActiveCfg = Release|x64 {23E282D0-E846-485C-B7A1-DA60F4BEA3D2}.Release|x64.Build.0 = Release|x64 {23E282D0-E846-485C-B7A1-DA60F4BEA3D2}.Release|x86.ActiveCfg = Release|x64 + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Debug|x64.Build.0 = Debug|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Debug|x86.Build.0 = Debug|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Release|x64.ActiveCfg = Release|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Release|x64.Build.0 = Release|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Release|x86.ActiveCfg = Release|Any CPU + {6E69DA67-1C6B-4010-B6F7-E753CFF716CC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Lib/Launchpad/DataSource/LocalDataSource.ahk b/Lib/Launchpad/DataSource/LocalDataSource.ahk deleted file mode 100644 index 7ac01421..00000000 --- a/Lib/Launchpad/DataSource/LocalDataSource.ahk +++ /dev/null @@ -1,2 +0,0 @@ -class LocalDataSource extends DataSourceBase { -} diff --git a/Lib/Shared/Modules/Auth/Auth.module.json b/Lib/Shared/Modules/Auth/Auth.module.json deleted file mode 100644 index 27e65909..00000000 --- a/Lib/Shared/Modules/Auth/Auth.module.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "module": { - "name": "Authentication", - "type": "AppModule", - "icon": "", - "category": "Web Services", - "tags": ["Launchpad", "LaunchpadBuilder"], - "description": "Authenticate with remote accounts, such as the Launchpad API", - "author": { - "name": "Ben McClure, Volantis Dev", - "url": "https://volantisdev.com" - }, - "website": "https://launchpad.games", - "version": "{{VERSION}}", - "appVersion": "", - "dependencies": [] - }, - "parameters": { - "config.auth_service": "" - }, - "services": { - "Auth": { - "class": "AuthService", - "arguments": ["@{App}", "@@config.auth_service", "@state.app"] - } - } -} diff --git a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk b/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk deleted file mode 100644 index 3428b9e0..00000000 --- a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk +++ /dev/null @@ -1,40 +0,0 @@ -class AuthInfo { - isAuthenticated := false - secureData := Map() - persistentData := Map() - userIdField := "userId" - - Authenticated { - get => this.isAuthenticated - set => this.isAuthenticated := !!(value) - } - - UserId { - get => this.Get(this.userIdField) - } - - __New() { - - } - - Get(key) { - value := "" - - if (this.secureData.Has(key)) { - value := this.secureData[key] - } else if (this.persistentData.Has(key)) { - value := this.persistentData[key] - } - - return value - } - - Set(key, value, persist := false) { - mapObj := persist ? this.persistentData : this.secureData - mapObj[key] := value - } - - GetPersistentData() { - return this.persistentData - } -} diff --git a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk b/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk deleted file mode 100644 index dfbcde79..00000000 --- a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk +++ /dev/null @@ -1,103 +0,0 @@ -class AuthInfoTest extends AppTestBase { - TestAuthenticated() { - authInfoObj := AuthInfo() - - authInfoObj.isAuthenticated := false - - this.AssertFalse( - authInfoObj.Authenticated, - "Test setting isAuthenticated to false" - ) - - authInfoObj.Authenticated := true - - this.AssertTrue( - authInfoObj.Authenticated, - "Test changing Authenticated to true" - ) - } - - TestUserId() { - authInfoObj := AuthInfo() - - this.AssertEquals( - "", - authInfoObj.UserId, - "Assert that user ID is blank initially" - ) - - userIds := [ - 123, - "456", - "1234-5678-9012-3456", - "r@nd0m" - ] - - for userId in userIds { - authInfoObj.Set(authInfoObj.userIdField, userId) - - this.AssertEquals( - userId, - authInfoObj.UserId, - "Test changing user ID to " . userId - ) - } - } - - TestGet() { - authInfoObj := AuthInfo() - - this.AssertEmpty( - authInfoObj.Get("nonexistantValue"), - "Getting a non-existent value returns an empty string" - ) - - authInfoObj.Set("testValue", "persistent", true) - - this.AssertEquals( - "persistent", - authInfoObj.Get("testValue"), - "Getting a persistent value is possible" - ) - - authInfoObj.Set("testValue", "overridden", false) - - this.AssertEquals( - "overridden", - authInfoObj.Get("testValue"), - "Getting a secure value overrides a persistent one" - ) - } - - TestSet() { - authInfoObj := AuthInfo() - - authInfoObj.Set("testValue", "persistent", true) - - this.AssertEquals( - "persistent", - authInfoObj.Get("testValue"), - "Setting a persistent value is possible" - ) - - authInfoObj.Set("testValue", "overridden", false) - - this.AssertEquals( - "overridden", - authInfoObj.Get("testValue"), - "Setting a secure value overrides a persistent one" - ) - } - - TestGetPersistentData() { - persistentData := Map("testValue", "persistent") - authInfoObj := AuthInfo() - authInfoObj.persistentData := persistentData - - this.AssertEquals( - persistentData, - authInfoObj.GetPersistentData(), - "GetPersistentData returns all persistent data" - ) - } -} diff --git a/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk b/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk deleted file mode 100644 index 57b560ca..00000000 --- a/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk +++ /dev/null @@ -1,42 +0,0 @@ -class JwtAuthInfo extends AuthInfo { - __New(userInfo) { - super.__New() - - this.Authenticated := false - - added := Map() - - if (userInfo.Has("user_id")) { - this.Authenticated := !!(userInfo["user_id"]) - this.Set("userId", userInfo["user_id"], true) - added["user_id"] := true - } - - if (userInfo.Has("refresh_token")) { - this.Set("refresh", userInfo["refresh_token"], true) - added["refresh_token"] := true - } - - if (userInfo.Has("expires_in")) { - timestamp := DateAdd(A_Now, userInfo["expires_in"], "S") - this.Set("expires", timestamp, true) - added["expires_in"] := true - } - - if (userInfo.Has("id_token")) { - this.Set("authToken", userInfo["id_token"]) - added["id_token"] := true - } - - if (userInfo.Has("access_token")) { - this.Set("accessToken", userInfo["access_token"]) - added["authToken"] := true - } - - for key, value in userInfo { - if (!added.Has(key) || !added[key]) { - this.Set(key, value) - } - } - } -} diff --git a/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk b/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk deleted file mode 100644 index 475bc81f..00000000 --- a/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk +++ /dev/null @@ -1,37 +0,0 @@ -class AuthProviderBase { - __New(persistentData := "") { - - } - - Login() { - - } - - Logout(authInfoObj) { - - } - - NeedsRefresh(authInfoObj) { - - } - - RefreshAuthentication(authInfoObj) { - - } - - AddRefreshInfoToRequest(authInfoObj, httpReqObj) { - ; Add refresh token to the request - } - - AddLoginInfoToRequest(authToken, httpReqObj) { - ; Add auth info needed to complete login to the request - } - - AddAuthInfoToRequest(authInfoObj, httpReqObj) { - ; Add auth info from authInfoObj to httpReqObj as needed - } - - ExtractAuthInfoFromResponse(httpReqObj) { - - } -} diff --git a/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk b/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk deleted file mode 100644 index 5fde6ad2..00000000 --- a/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk +++ /dev/null @@ -1,150 +0,0 @@ -class JwtAuthProvider extends AuthProviderBase { - dataSourceObj := "" - authEndpointUrl := "" - webApiKey := "" - refreshPath := "token" - authToken := "" - refreshToken := "" - - __New(dataSourceObj, authEndpointUrl, webApiKey, persistentData := "") { - this.dataSourceObj := dataSourceObj - this.authEndpointUrl := authEndpointUrl - this.webApiKey := webApiKey - - if (persistentData != "" and persistentData.Has("authToken")) { - this.authToken := persistentData["authToken"] - } - } - - Login(isRetry := false) { - refreshToken := this.refreshToken - - if (!refreshToken) { - refreshToken := this.ShowLoginWindow() - - if (refreshToken) { - this.refreshToken := refreshToken - } - } - - userInfo := "" - - if (refreshToken) { - url := this.GetAuthUrl(this.refreshPath) - request := WinHttpReq(url) - payload := Map("grant_type", "refresh_token", "refresh_token", refreshToken) - response := request.Send("POST", payload) - - if (request.GetStatusCode() == 200) { - userInfo := this.ExtractAuthInfoFromResponse(request) - } else { - this.refreshToken := "" - - if (isRetry) { - ; TODO: Log user out instead of throwing an exception - throw OperationFailedException("Login failed.") - } else { - return this.Login(true) - } - } - } - - return userInfo - } - - GetAuthUrl(path) { - return this.authEndpointUrl . "/" . path . "?key=" . this.webApiKey - } - - ShowLoginWindow() { - return "" - } - - Logout(authInfoObj) { - this.authToken := "" - this.refreshToken := "" - return true - } - - RefreshAuthentication(authInfoObj) { - refreshToken := authInfoObj.Get("refresh") - - if (refreshToken) { - this.refreshToken := refreshToken - } - - return this.Login() - } - - IsAuthenticationValid(authInfoObj) { - isValid := false - - if (authInfoObj && authInfoObj.Authenticated) { - isValid := !this.IsAuthenticationExpired(authInfoObj) - } - - return isValid - } - - AddAuthInfoToRequest(authInfoObj, httpReqObj) { - authToken := authInfoObj.Get("authToken") - - if (authToken) { - this.AddAuthorizationHeader(authToken, httpReqObj) - } - } - - AddAuthorizationHeader(bearerToken, httpReqObj) { - if (bearerToken) { - httpReqObj.requestHeaders["Authorization"] := "Bearer " . bearerToken - } - } - - ExtractAuthInfoFromResponse(httpReqObj) { - responseData := Trim(httpReqObj.GetResponseData()) - - userInfo := Map() - - if (responseData) { - data := JsonData() - userInfo := data.FromString(&responseData) - } - - return JwtAuthInfo(userInfo) - } - - IsAuthenticationExpired(authInfoObj) { - expired := true - - if (authInfoObj and authInfoObj.Authenticated) { - expires := authInfoObj.Get("expires") - - if (expires) { - diff := DateDiff(A_Now, expires, "S") - expired := (diff >= 0) - } - } - - return expired - } - - NeedsRefresh(authInfoObj) { - needsRefresh := false - thresholdSeconds := -600 - - if (!this.IsAuthenticationValid(authInfoObj)) { - needsRefresh := true - } else { - expires := authInfoObj.Get("expires") - - if (expires) { - diff := DateDiff(A_Now, expires, "S") - needsRefresh := (diff >= thresholdSeconds) - } else { - needsRefresh := true - } - } - - return needsRefresh - } -} diff --git a/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk b/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk deleted file mode 100644 index ff44127d..00000000 --- a/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk +++ /dev/null @@ -1,30 +0,0 @@ -class LaunchpadApiAuthProvider extends JwtAuthProvider { - app := "" - - __New(app, stateObj) { - this.app := app - - persistentData := "" - - if (stateObj) { - state := stateObj.GetState() - - if (state.Has("Authentication")) { - persistentData := state["Authentication"] - } - } - - authEndpointUrl := "https://securetoken.googleapis.com/v1" - webApiKey := "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ" - super.__New(app.Service("manager.data_source").GetDefaultDataSource(), authEndpointUrl, webApiKey, persistentData) - } - - ShowLoginWindow() { - return this.app.Service("manager.gui").Dialog(Map("type", "LoginWindow")) - } - - ExtractAuthInfoFromResponse(httpReqObj) { - authInfoObj := super.ExtractAuthInfoFromResponse(httpReqObj) - return authInfoObj - } -} diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk deleted file mode 100644 index bf0cf3aa..00000000 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ /dev/null @@ -1,142 +0,0 @@ -class ApiDataSource extends DataSourceBase { - endpointUrl := "" - app := "" - - __New(app, cacheManager, cacheName, endpointUrl) { - this.app := app - InvalidParameterException.CheckTypes("ApiDataSource", "endpointUrl", endpointUrl, "", "cacheManager", cacheManager, "CacheManager") - this.endpointUrl := endpointUrl - super.__New(cacheManager, cacheName) - } - - ItemExists(path) { - return super.ItemExists(path) || this.ItemExistsInApi(path) - } - - ItemExistsInApi(path) { - exists := (this.cache.ItemExists(path) && !this.cache.ItemNeedsUpdate(path)) - - if (!exists) { - request := this.SendHttpReq(path, "HEAD") - - exists := (request.GetStatusCode() == 200) - - if (!exists) { - this.cache.SetNotFound(path) - } - } - - return exists - } - - GetHttpReq(path, private := false) { - request := WinHttpReq(this.GetRemoteLocation(path)) - - if (private) { - request.requestHeaders["Cache-Control"] := "no-cache" - - if (this.app.Config["api_authentication"] && this.app.Services.Has("Api")) { - this.app.Service("Auth").AlterApiRequest(request) - } - } - - return request - } - - SendHttpReq(path, method := "GET", data := "", private := false) { - request := this.GetHttpReq(path, private) - returnCode := request.Send(method, data) - return request - } - - GetRemoteLocation(path) { - return this.endpointUrl . "/" . path - } - - RetrieveItem(path, private := false, maxCacheAge := "") { - if (maxCacheAge == "") { - maxCacheAge := this.maxCacheAge - } - - exists := (!private && this.cache.ItemExists(path) && !this.cache.ItemNeedsUpdate(path, maxCacheAge)) - - if (!exists) { - request := this.SendHttpReq(path, "GET", "", private) - - if (request.GetStatusCode() != 200) { - return "" - } - - responseBody := Trim(request.GetResponseData()) - - if (responseBody == "") { - return "" - } - - this.cache.WriteItem(path, responseBody) - } - - return this.cache.ItemExists(path) ? this.cache.ReadItem(path) : "" - } - - GetStatus() { - path := "status" - statusExpire := 5 ;60 - - status := Map("authenticated", false, "email", "", "photo", "") - - if (this.app.Config["api_authentication"] && this.app.Service("Auth").IsAuthenticated()) { - statusResult := this.ReadItem(path, true) - - if (statusResult) { - json := JsonData() - status := json.FromString(&statusResult) - } - } - - return status - } - - GetExt(path) { - - } - - Open() { - Run(this.endpointUrl) - } - - ChangeApiEndpoint(existingEndpoint := "", owner := "", parent := "") { - if (existingEndpoint == "") { - existingEndpoint := this.app.Config["api_endpoint"] - } - - ownerOrParent := "" - - if (parent) { - ownerOrParent := parent - } else if (owner) { - ownerOrParent := owner - } - - apiEndpointUrl := this.app.Service("manager.gui").Dialog(Map( - "type", "SingleInputBox", - "title", "API Endpoint URL", - "text", "Enter the base URL of the API endpoint you would like Launchpad to connect to. Leave blank to revert to the default.", - "defaultValue", existingEndpoint, - "ownerOrParent", ownerOrParent, - "child", !!(parent) - )) - - if (apiEndpointUrl != existingEndpoint) { - this.app.Config["api_endpoint"] := apiEndpointUrl - apiEndpointUrl := this.app.Config["api_endpoint"] - - if (apiEndpointUrl != existingEndpoint) { - this.endpointUrl := apiEndpointUrl - this.cache.FlushCache() - } - } - - return apiEndpointUrl - } -} diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json deleted file mode 100644 index bd3b74bc..00000000 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "module": { - "name": "Launchpad API", - "icon": "", - "category": "Web Services", - "tags": ["Launchpad", "LaunchpadBuilder"], - "description": "Connect with Launchpad.games to sync game details and share your launchers and themes", - "author": { - "name": "Ben McClure, Volantis Dev", - "url": "https://volantisdev.com" - }, - "website": "https://launchpad.games", - "version": "{{VERSION}}", - "appVersion": "", - "dependencies": ["Auth"] - }, - "services": { - "data_source.api": { - "class": "ApiDataSource", - "arguments": ["@{App}", "@manager.cache", "api", "@@config.api_endpoint"] - }, - "auth_provider.launchpad_api": { - "class": "LaunchpadApiAuthProvider", - "arguments": ["@{App}", "@state.app"] - }, - "cache_state.api": { - "class": "CacheState", - "arguments": ["@{App}", "@@config.cache_dir", "API.json"] - }, - "cache.api": { - "class": "FileCache", - "arguments": ["@{App}", "@cache_state.api", "@@config.cache_dir", "API"] - } - }, - "parameters": { - "config.data_source_key": "api", - "config.api_endpoint": "https://api.launchpad.games/v1", - "config.api_authentication": true, - "config.api_auto_login": false - } -} diff --git a/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk b/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk deleted file mode 100644 index 91c1fdce..00000000 --- a/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk +++ /dev/null @@ -1,70 +0,0 @@ -class DataSourceBase { - cache := "" - useCache := false - maxCacheAge := 86400 - - __New(cacheManager := "", cacheName := "") { - if (cacheManager != "" && cacheName != "") { - InvalidParameterException.CheckTypes("DataSourceBase", "cacheManager", cacheManager, "CacheManager") - this.useCache := true - this.cache := cacheManager[cacheName] - } - } - - ItemExists(path) { - return this.useCache ? this.cache.ItemExists(path) : false - } - - ReadItem(path, private := false, maxCacheAge := "") { - if (maxCacheAge == "") { - maxCacheAge := this.maxCacheAge - } - - item := "" - - if (this.ItemNeedsRetrieval(path)) { - item := this.RetrieveItem(path, private, maxCacheAge) - } else if (this.useCache) { - item := this.cache.ReadItem(path) - } - - return item - } - - ItemNeedsRetrieval(path) { - return (!this.useCache || this.cache.ItemNeedsUpdate(path)) - } - - RetrieveItem(path, private := false, maxCacheAge := "") { - return "" - } - - CopyItem(path, destination) { - if (this.ItemNeedsRetrieval(path)) { - this.RetrieveItem(path) - } - - return this.useCache ? this.cache.CopyItem(path, destination) : destination - } - - GetRemoteLocation(path) { - return path - } - - ReadListing(path) { - listingInstance := DSListing(path, this) - - listing := [] - - if (listingInstance.Exists()) { - listing := listingInstance.Read() - } - - return listing - } - - ReadJson(key, path := "") { - dsItem := DSJson(key, path, this) - return dsItem.Read() - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk deleted file mode 100644 index a5165b1f..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk +++ /dev/null @@ -1,3 +0,0 @@ -class DSAssetFile extends DSFile { - allowRead := false -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk deleted file mode 100644 index 187d931b..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk +++ /dev/null @@ -1,7 +0,0 @@ -class DSFile extends DataSourceItemBase { - allowRead := true ; Some files are only meant to be copied - - Read() { - return this.allowRead ? super.Read() : "" - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk deleted file mode 100644 index 991c41dc..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk +++ /dev/null @@ -1,17 +0,0 @@ -class DSJson extends DSFile { - itemSuffix := "" - dataType := "Map" - - Read() { - content := super.Read() - dataType := this.dataType - obj := %dataType%() - - if (content) { - data := JsonData() - obj := data.FromString(&content) - } - - return obj - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk deleted file mode 100644 index 1d48a12c..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk +++ /dev/null @@ -1,7 +0,0 @@ -class DSListing extends DSJson { - dataType := "Array" - - __New(path, dataSourceKey := "") { - super.__New(path, "", dataSourceKey) - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk deleted file mode 100644 index ef0ff3ea..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk +++ /dev/null @@ -1,53 +0,0 @@ -class DataSourceItemBase { - endpoint := "" - basePath := "" - itemSuffix := "" - path := "" - key := "" - - __New(key, path := "", dataSource := "") { - InvalidParameterException.CheckTypes("DataSourceItemBase", "key", key, "", "path", path, "") - InvalidParameterException.CheckEmpty("DataSourceItemBase", "key", key) - InvalidParameterException.CheckTypes("DataSourceItemBase", "dataSource", dataSource, "DataSourceBase") - - this.endpoint := dataSource - this.key := key - this.path := path - } - - GetPath(includeFilename := true) { - path := this.basePath - - if (path != "" && this.path != "") { - path .= "/" - } - - path .= this.path - - if (includeFilename) { - if (path) { - path .= "/" - } - - path .= this.key . this.itemSuffix - } - - return path - } - - GetRemoteLocation() { - return this.endpoint.GetRemoteLocation(this.GetPath()) - } - - Exists() { - return this.endpoint.ItemExists(this.GetPath()) - } - - Read() { - return this.endpoint.ReadItem(this.GetPath()) - } - - Copy(destination) { - return this.endpoint.CopyItem(this.GetPath(), destination) - } -} diff --git a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk deleted file mode 100644 index 5b0e84ce..00000000 --- a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk +++ /dev/null @@ -1,195 +0,0 @@ -class AppEntityBase extends FieldableEntity { - app := "" - dataSourcePath := "" - existsInDataSource := false - - __New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity := "") { - this.app := app - - super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity) - } - - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { - className := this.Prototype.__Class - - return %className%( - container.GetApp(), - id, - entityTypeId, - container, - eventMgr, - storageObj, - idSanitizer, - parentEntity - ) - } - - GetDefaultFieldGroups() { - groups := super.GetDefaultFieldGroups() - - groups["advanced"] := Map( - "name", "Advanced", - "weight", 100 - ) - - groups["api"] := Map( - "name", "API", - "weight", 150 - ) - - return groups - } - - BaseFieldDefinitions() { - definitions := super.BaseFieldDefinitions() - - definitions["DataSourceKeys"] := Map( - "description", "The data source keys to load defaults from, in order.", - "help", "The default data source is 'api' which connects to the default api endpoint (Which can be any HTTP location compatible with Launchpad's API format)", - "default", [this.app.Config["data_source_key"]], - "multiple", true, - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) - - definitions["DataSourceItemKey"] := Map( - "description", "The key that is used to look up the entity's data from configured external data sources.", - "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataSourceKey", - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) - - definitions["AssetsDir"] := Map( - "type", "directory", - "description", "The directory where any required assets for this entity will be saved.", - "default", this.app.Config["assets_dir"] . "\" . this.Id, - "group", "advanced", - "formField", false, - "modes", Map("simple", Map("formField", false)) - ) - - definitions["DependenciesDir"] := Map( - "type", "directory", - "description", "The directory where dependencies which have been installed for this entity can be accessed.", - "default", this.app.appDir . "\Vendor", - "group", "advanced", - "required", true, - "formField", false, - "modes", Map("simple", Map("formField", false)) - ) - - return definitions - } - - _getLayerNames() { - layerNames := super._getLayerNames() - layerNames.Push("ds") - - return layerNames - } - - _getLayerSources() { - layerSources := super._getLayerSources() - layerSources["ds"] := ObjBindMethod(this, "AggregateDataSourceDefaults") - - return layerSources - } - - UpdateDataSourceDefaults(recurse := true) { - ; @todo Move this to a module - this.GetData().UnloadLayer("ds") - - if (recurse) { - for key, child in this.GetReferencedEntities(true) { - child.UpdateDataSourceDefaults(recurse) - } - } - } - - AggregateDataSourceDefaults(includeParentData := true, includeChildData := true) { - defaults := (this.parentEntity != "" && includeParentData) - ? this.parentEntity.AggregateDataSourceDefaults(includeParentData, false) - : Map() - - ; @todo Uncomment if needed, remove if not - ;this.GetData().SetLayer("ds", defaults) - - for index, dataSource in this.GetAllDataSources() { - defaults := this.merger.Merge(this.GetDataSourceDefaults(dataSource), defaults) - } - - if (includeChildData) { - for key, child in this.GetReferencedEntities(true) { - defaults := this.merger.Merge(child.AggregateDataSourceDefaults(false, includeChildData), defaults) - } - } - - return defaults - } - - GetAllDataSources() { - dataSources := Map() - - if (this.Has("DataSourceKeys", false)) { - dataSourceKeys := this["DataSourceKeys"] - - if (!HasBase(dataSourceKeys, Array.Prototype)) { - dataSourceKeys := [dataSourceKeys] - } - - for index, dataSourceKey in dataSourceKeys { - if (this.app.Service("manager.data_source").Has(dataSourceKey)) { - dataSource := this.app.Service("manager.data_source")[dataSourceKey] - - if (dataSource) { - dataSources[dataSourceKey] := dataSource - } - } - } - } - - return dataSources - } - - GetDataSourceDefaults(dataSource) { - defaults := Map() - itemKey := this.DiscoverDataSourceItemKey() - - if (itemKey) { - dsData := dataSource.ReadJson(itemKey, this.GetDataSourceItemPath()) - - if (dsData) { - this.existsInDataSource := true - - if (dsData.Has("data")) { - dsData := dsData["data"] - } - - if (dsData.Has("defaults")) { - defaults := this.merger.Merge(dsData["defaults"], defaults) - defaults := this.MergeAdditionalDataSourceDefaults(defaults, dsData) - } - } - } - - return defaults - } - - DiscoverDataSourceItemKey() { - return this.Id - } - - GetDataSourceItemPath() { - return this.dataSourcePath - } - - MergeAdditionalDataSourceDefaults(defaults, dataSourceData) { - return defaults - } - - GetAssetPath(filePath) { - return this["AssetsDir"] . "\" . filePath - } -} diff --git a/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk b/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk deleted file mode 100644 index c8a5b9b7..00000000 --- a/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk +++ /dev/null @@ -1,3 +0,0 @@ -class TaskEntity extends AppEntityBase { - -} diff --git a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk deleted file mode 100644 index bec24d81..00000000 --- a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk +++ /dev/null @@ -1,68 +0,0 @@ -class StatusIndicatorControl extends GuiControlBase { - statusIndicatorW := 120 - statusIndicatorMinW := 120 - innerControl := "" - - CreateControl(statusInfo, handler := "", statusStyle := "status") { - super.CreateControl(false) - - if (handler == "" && HasMethod(this.guiObj, "OnStatusIndicatorClick")) { - handler := "OnStatusIndicatorClick" - } - - options := this.parameters.SetDefaultOptions(this.parameters["options"].Clone(), [ - "x+" . this.guiObj.margin, - "yp", - "w" . this.statusIndicatorW, - "h26", - "vStatusIndicator" - ]) - - name := statusInfo && statusInfo.Has("name") ? statusInfo["name"] : "" - photo := statusInfo && statusInfo.Has("photo") ? statusInfo["photo"] : "" - - this.innerControl := this.guiObj.Add("ButtonControl", options, name, handler, statusStyle, Map("photo", photo)) - this.ctl := this.innerControl.ctl - return this.ctl - } - - UpdateStatusIndicator(statusInfo, statusStyle := "status") { - oldW := this.statusIndicatorW - newW := this.CalculateWidth(statusInfo) - this.statusIndicatorW := newW - difference := 0 - - if (oldW != newW) { - this.ctl.GetPos(&statusX,, &statusW) - difference := newW - oldW - this.ctl.Move(statusX - difference,, statusW + difference) - } - - name := statusInfo && statusInfo.Has("name") ? statusInfo["name"] : "" - photo := statusInfo && statusInfo.Has("photo") ? statusInfo["photo"] : "" - - this.guiObj.themeObj.DrawButton(this.ctl, name, statusStyle, Map("photo", photo)) - return difference - } - - CalculateWidth(statusInfo) { - width := this.statusIndicatorMinW - requiredW := 10 - - if (statusInfo) { - if (statusInfo.Has("name")) { - requiredW += this.guiObj.themeObj.CalculateTextWidth(statusInfo["name"]) - } - - if (StatusInfo.Has("photo")) { - requiredW += 26 - } - } - - if (requiredW > width) { - width := requiredW - } - - return Ceil(width) - } -} diff --git a/Lib/Shared/Volantis.App/Service/AuthService.ahk b/Lib/Shared/Volantis.App/Service/AuthService.ahk deleted file mode 100644 index 9d9b4cd2..00000000 --- a/Lib/Shared/Volantis.App/Service/AuthService.ahk +++ /dev/null @@ -1,154 +0,0 @@ -class AuthService extends AppServiceBase { - authProviderObj := "" - stateObj := "" - authenticationEnabled := false - authInfoObj := "" - - __New(app, authProviderObj, stateObj) { - InvalidParameterException.CheckTypes("AuthenticationService", "stateObj", stateObj, "StateBase") - - if (authProviderObj && Type(authProviderObj) == "String") { - authProviderObj := app.Services.Get(authProviderObj) - } - - this.authProviderObj := authProviderObj - this.stateObj := stateObj - - authState := this.stateObj.Authentication - - if (authState && authState.Count > 0) { - authInfoObj := AuthInfo() - authInfoObj.Authenticated := true - - for key, value in authState { - authInfoObj.Set(key, value, true) - } - - this.authInfoObj := authInfoObj - } - - super.__New(app) - } - - SetAuthProvider(authProviderObj) { - this.authProviderObj := authProviderObj - } - - SetState(stateObj) { - this.stateObj := stateObj - } - - Login() { - if (this.app.Config["api_authentication"] && this.authProviderObj) { - authInfoObj := "" - - if (!this.IsAuthenticated()) { - authInfoObj := this.authProviderObj.Login() - } else if (this.AuthenticationNeedsRefresh()) { - authInfoObj := this.RefreshAuthentication() - } - - if (authInfoObj) { - this.UpdateAuthState(authInfoObj) - this.app.UpdateStatusIndicators() - } - } - } - - GetStatusInfo() { - statusText := "Not logged in" - imgPath := "" - email := "" - - if (this.IsAuthenticated()) { - playerName := this.app.Config["player_name"] - email := this.authInfoObj.Get("email") - - if (playerName) { - statusText := playerName - } else if (email) { - statusText := email - } else { - statusText := "Logged in" - } - - imgPath := this.authInfoObj.Get("photo") - - if (SubStr(imgPath, 1, 4) == "http") { - cachePath := "account--profile.jpg" - imgPath := this.app.Service("manager.cache")["file"].GetCachedDownload(cachePath, imgPath) - } - } - - return Map("name", statusText, "email", email, "photo", imgPath) - } - - Logout() { - if (this.app.Config["api_authentication"] && this.authProviderObj && this.authInfoObj) { - this.authProviderObj.Logout(this.authInfoObj) - this.authInfoObj := "" - this.stateObj.Authentication := Map() - this.app.UpdateStatusIndicators() - } - } - - IsAuthenticated() { - return this.app.Config["api_authentication"] && this.authProviderObj && this.authInfoObj && this.authInfoObj.Authenticated - } - - AuthenticationNeedsRefresh() { - needsRefresh := false - - if (this.app.Config["api_authentication"] && this.authProviderObj && this.IsAuthenticated()) { - needsRefresh := this.authProviderObj.NeedsRefresh(this.authInfoObj) - } - - return needsRefresh - } - - RefreshAuthentication() { - if (this.app.Config["api_authentication"] && this.authProviderObj && this.IsAuthenticated()) { - authInfoObj := this.authProviderObj.RefreshAuthentication(this.authInfoObj) - - if (authInfoObj) { - this.UpdateAuthState(authInfoObj) - } - } - } - - UpdateAuthState(authInfoObj) { - if (this.app.Config["api_authentication"] && this.authProviderObj && authInfoObj) { - this.authInfoObj := authInfoObj - this.AddUserInfoFromApi(authInfoObj) - this.stateObj.SetAuthentication(authInfoObj.GetPersistentData()) - } - } - - AddUserInfoFromApi(authInfoObj) { - dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() - - if (dataSource) { - apiStatus := dataSource.GetStatus() - - if (apiStatus) { - if (apiStatus.Has("email")) { - authInfoObj.Set("email", apiStatus["email"], true) - } - - if (apiStatus.Has("photo")) { - authInfoObj.Set("photo", apiStatus["photo"], true) - } - } - } - } - - AlterApiRequest(request) { - if (this.IsAuthenticated()) { - if (this.AuthenticationNeedsRefresh()) { - this.RefreshAuthentication() - } - - this.authProviderObj.AddAuthInfoToRequest(this.authInfoObj, request) - } - } -} diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk b/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk deleted file mode 100644 index 2b688991..00000000 --- a/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk +++ /dev/null @@ -1,61 +0,0 @@ -class DataSourceManager extends ComponentManagerBase { - primaryKey := "" - - __New(container, eventMgr, notifierObj, primaryKey := "") { - if (primaryKey) { - this.primaryKey := primaryKey - } - - super.__New(container, "data_source.", eventMgr, notifierObj, DataSourceBase) - } - - GetDefaultDataSource() { - if (!this.primaryKey) { - throw ComponentException("There is no default data source set") - } - - if (!this.Has(this.primaryKey)) { - throw ComponentException("Primary data source key " . this.primaryKey . " does not exist") - } - - return this[this.primaryKey] - } - - GetDefaultComponentId() { - return this.primaryKey - } - - GetItem(key := "") { - if (key == "") { - key := this.primaryKey - } - - return super.GetItem(key) - } - - ReadListing(path, dataSourceKey := "") { - if (dataSourceKey == "") { - dataSourceKey := this.primaryKey - } - - if (!this.Has(dataSourceKey)) { - throw ComponentException("Component " . dataSourceKey . " does not exist in the data source manager") - } - - dataSource := this[dataSourceKey] - return dataSource.ReadListing(path) - } - - ReadJson(key, path := "", dataSourceKey := "") { - if (dataSourceKey == "") { - dataSourceKey := this.primaryKey - } - - if (!this.Has(dataSourceKey)) { - throw ComponentException("Component " . dataSourceKey . " does not exist in the data source manager") - } - - dataSource := this[dataSourceKey] - return dataSource.ReadJson(key, path) - } -} diff --git a/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk b/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk deleted file mode 100644 index b3dad413..00000000 --- a/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk +++ /dev/null @@ -1,21 +0,0 @@ -class BooleanEntityField extends EntityFieldBase { - DefinitionDefaults(fieldDefinition) { - defaults := super.DefinitionDefaults(fieldDefinition) - defaults["widget"] := "checkbox" - return defaults - } - - GetValue() { - isTrue := StrLower(super.GetValue()) - - if (isTrue == "true" || isTrue == "false") { - isTrue := (isTrue == "true") - } - - return !!(isTrue) - } - - SetValue(value) { - super.SetValue(!!(value)) - } -} diff --git a/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk b/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk deleted file mode 100644 index 6591eedc..00000000 --- a/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk +++ /dev/null @@ -1,65 +0,0 @@ -class EntityReferenceField extends ServiceReferenceField { - managerObj := "" - - DefinitionDefaults(fieldDefinition) { - entityTypeId := fieldDefinition["entityType"] - - if (!entityTypeId) { - throw EntityException("Entity reference fields require an entityType mapping.") - } - - managerObj := this._entityManager(entityTypeId) - defaults := super.DefinitionDefaults(fieldDefinition) - - defaults["servicePrefix"] := managerObj.GetServicePrefix() - defaults["entityType"] := managerObj.entityTypeId - defaults["child"] := false - - return defaults - } - - GetValidators(value) { - validators := super.GetValidators(value) - - if (value) { - ; @todo check if entity exists - - if (this.Definition["child"]) { - ; @todo Add validator for child entity - } - } - - return validators - } - - _entityManager(entityTypeId := "") { - if (!this.managerObj) { - if (!entityTypeId) { - entityTypeId := this.Definition["entityType"] - } - - this.managerObj := this.container.Get("entity_manager." . entityTypeId) - } - - return this.managerObj - } - - _getService(entityId) { - if (!this.Definition["entityType"]) { - throw AppException("Entity type of reference field is not specified") - } - - entityObj := "" - - if (entityId) { - entityObj := this._entityManager()[entityId] - entityObj.LoadEntity() - } - - return entityObj - } - - _getSelectQuery() { - return this._entityManager().EntityQuery(EntityQuery.RESULT_TYPE_IDS) - } -} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk deleted file mode 100644 index 643d20fe..00000000 --- a/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk +++ /dev/null @@ -1,13 +0,0 @@ -class EntityStorageEvent extends EntityEvent { - _storageObj := "" - - Storage { - get => this._storageObj - } - - __New(eventName, entityTypeId, entityObj, storageObj) { - this._storageObj := storageObj - - super.__New(eventName, entityTypeId, entityObj) - } -} diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 00000000..0de04d35 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,23 @@ +version: '3' + +tasks: + app-watch: + dir: launchpad_app + cmds: + - flutter pub run build_runner watch --delete-conflicting-outputs + app-release-dev: + dir: launchpad_app + cmds: + - flutter_distributor release --name dev + app-generate-protobuf-code: + dir: launchpad_app + cmds: + - protoc --dart_out=./lib/proto ./lib/proto/BlizzardProductDb.proto + dotnet-build-debug: + dir: launchpad_dotnet + cmds: + - dotnet publish /p:LaunchpadDotNet=Shared /p:SelfContained=true -r win-x64 -c debug -o assets/bin/debug/ + dotnet-build-release: + dir: launchpad_dotnet + cmds: + - dotnet publish /p:LaunchpadDotNet=Shared /p:SelfContained=true -r win-x64 -c release -o assets/bin/release/ diff --git a/launchpad_ahk/.gitignore b/launchpad_ahk/.gitignore new file mode 100644 index 00000000..d4acd1e9 --- /dev/null +++ b/launchpad_ahk/.gitignore @@ -0,0 +1,17 @@ +/Vendor/* +!/Vendor/7zip +!/Vendor/7zip/* +!/Vendor/Verpatch +!/Vendor/Verpatch/* +/Launchpad.ini +/Launchpad.build.ini +/Launchpad.json +/Build +/Dist +/Launchpad.zip +/Launchpad-*.zip +/Launchpad.exe +/log.txt +/LaunchpadOverlay/bin +/LaunchpadOverlay/obj +/.vs diff --git a/Launchpad.ahk b/launchpad_ahk/Launchpad.ahk similarity index 84% rename from Launchpad.ahk rename to launchpad_ahk/Launchpad.ahk index 00552dc8..d34cd5e1 100644 --- a/Launchpad.ahk +++ b/launchpad_ahk/Launchpad.ahk @@ -13,8 +13,7 @@ appVersion := "{{VERSION}}" Launchpad(Map( "appName", "Launchpad", - "developer", "Volantis Development", "version", appVersion, - "trayIcon", "Resources\Graphics\Launchpad.ico", + "trayIcon", "Resources\Graphics\launchpad.ico", "console", true )) diff --git a/Launchpad.nsi b/launchpad_ahk/Launchpad.nsi similarity index 100% rename from Launchpad.nsi rename to launchpad_ahk/Launchpad.nsi diff --git a/Launchpad.services.json b/launchpad_ahk/Launchpad.services.json similarity index 84% rename from Launchpad.services.json rename to launchpad_ahk/Launchpad.services.json index 6a725f60..7df3dbb0 100644 --- a/Launchpad.services.json +++ b/launchpad_ahk/Launchpad.services.json @@ -1,9 +1,14 @@ { "parameters": { + "app.website_url": "https://launchpad.games", + "app.custom_tray_menu": true, + "app.developer": "Volantis Development", + "app.has_settings": true, + "app.settings_window": "SettingsWindow", + "app.show_about_menu_item": true, + "app.about_window": "AboutWindow", + "app.show_website_menu_item": true, "backups_config": {}, - "config.api_authentication": false, - "config.api_auto_login": false, - "config.api_endpoint": "", "config.assets_dir": "@@{data_dir}\\Launcher Assets", "config.auto_backup_config_files": true, "config.backups_to_keep": 5, @@ -12,7 +17,6 @@ "config.clean_launchers_on_build": false, "config.clean_launchers_on_exit": true, "config.create_desktop_shortcuts": true, - "config.data_source_key": "local", "config.default_launcher_theme": "", "config.destination_dir": "@@{data_dir}\\Launchers", "config.launcher_double_click_action": "Edit", @@ -22,7 +26,6 @@ "config.platforms_file": "@@{data_dir}\\Platforms.json", "config.platforms_view_mode": "Report", "config.tasks_view_mode": "Report", - "config.player_name": "", "config.rebuild_existing_launchers": false, "config.tasks_file": "@@{data_dir}\\Tasks.json", "config.use_advanced_launcher_editor": false, @@ -33,27 +36,29 @@ "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", "manager_view_mode_parameter": "config.launcher_view_mode", - "default_icon": "Game", + "default_icon": "game", "allow_add": true, "allow_edit": true, "allow_delete": true, "manager_gui": "MainWindow" }, - "entity_type.managed_game": { + "entity_type.game_process": { "name_singular": "Managed Game", "name_plural": "Managed Games", - "entity_class": "ManagedGameEntity", + "entity_class": "GameProcessEntity", "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", - "parent_entity_type": "launcher" + "parent_entity_type": "launcher", + "parent_entity_storage": true }, - "entity_type.managed_launcher": { + "entity_type.launcher_process": { "name_singular": "Managed Launcher", "name_plural": "Managed Launchers", - "entity_class": "ManagedLauncherEntity", + "entity_class": "LauncherProcessEntity", "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", - "parent_entity_type": "launcher" + "parent_entity_type": "launcher", + "parent_entity_storage": true }, "entity_type.platform": { "name_singular": "Platform", @@ -65,8 +70,8 @@ "storage_config_storage_parent_key": "Platforms", "storage_config_path_parameter": "config.platforms_file", "manager_view_mode_parameter": "config.platforms_view_mode", - "default_icon": "Platform", - "manager_gui": "ManagePlatformsWindow", + "default_icon": "platform", + "manager_gui": "PlatformsWindow", "manager_link_in_tools_menu": true }, "entity_type.task": { @@ -112,10 +117,6 @@ "class": "LaunchpadConfig", "arguments": ["@config_storage.app_config", "@{}", "@@config_key"] }, - "data_source.local": { - "class": "LocalDataSource", - "arguments": ["@manager.cache", "local"] - }, "ini_migrator": { "class": "LaunchpadIniMigrator", "arguments": ["@{App}", "@manager.gui"] @@ -132,10 +133,6 @@ "class": "BuilderManager", "arguments": ["@entity_manager.launcher", "@{}", "@manager.event", "@notifier"] }, - "manager.data_source": { - "class": "DataSourceManager", - "arguments": ["@{}", "@manager.event", "@notifier", "@@config.data_source_key"] - }, "state.app": { "class": "LaunchpadAppState", "arguments": ["@{App}", "@@state_path"] diff --git a/LaunchpadTest.ahk b/launchpad_ahk/LaunchpadTest.ahk similarity index 92% rename from LaunchpadTest.ahk rename to launchpad_ahk/LaunchpadTest.ahk index 304cb70f..c9720435 100644 --- a/LaunchpadTest.ahk +++ b/launchpad_ahk/LaunchpadTest.ahk @@ -13,7 +13,7 @@ appVersion := "1.0.0" -TraySetIcon("Resources\Graphics\Launchpad.ico") +TraySetIcon("Resources\Graphics\launchpad.ico") HtmlResultViewer("Launchpad Test") .ViewResults(SimpleTestRunner(FilesystemTestLoader().GetTests()).RunTests()) diff --git a/Lib/Launchpad/App/Launchpad.ahk b/launchpad_ahk/Lib/Launchpad/App/Launchpad.ahk similarity index 60% rename from Lib/Launchpad/App/Launchpad.ahk rename to launchpad_ahk/Lib/Launchpad/App/Launchpad.ahk index 73eb3320..8ab4a97d 100644 --- a/Lib/Launchpad/App/Launchpad.ahk +++ b/launchpad_ahk/Lib/Launchpad/App/Launchpad.ahk @@ -1,30 +1,5 @@ class Launchpad extends AppBase { - customTrayMenu := true detectGames := false - isSetup := false - - CheckForUpdates(notify := true) { - updateAvailable := false - - if (this.Version != "{{VERSION}}" && this.Service("manager.data_source").GetDefaultDataSource()) { - dataSource := this.Service("manager.data_source").GetDefaultDataSource() - releaseInfoStr := dataSource.ReadItem("release-info") - - if (releaseInfoStr) { - data := JsonData() - releaseInfo := data.FromString(&releaseInfoStr) - - if (releaseInfo && releaseInfo["data"].Has("version") && releaseInfo["data"]["version"] && this.Service("version_checker").VersionIsOutdated(releaseInfo["data"]["version"], this.Version)) { - updateAvailable := true - this.Service("manager.gui").Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo) - } - } - } - - if (!updateAvailable && notify) { - this.Service("notifier").Info("You're running the latest version of Launchpad. Shiny!") - } - } UpdateIncludes() { this.RunAhkScript(this.appDir . "\Scripts\UpdateIncludes.ahk") @@ -32,7 +7,7 @@ } InitializeApp(config) { - eventMgr := this.Service("manager.event") + eventMgr := this["manager.event"] eventMgr.Register(EntityEvents.ENTITY_CREATED, "LaunchpadEntityCreated", ObjBindMethod(this, "OnEntityCreated")) eventMgr.Register(EntityEvents.ENTITY_UPDATED, "LaunchpadEntityUpdated", ObjBindMethod(this, "OnEntityUpdated")) eventMgr.Register(EntityEvents.ENTITY_DELETED, "LaunchpadEntityDeleted", ObjBindMethod(this, "OnEntityDeleted")) @@ -109,35 +84,31 @@ RunApp(config) { this.MigrateConfiguration() - - if (this.Config["api_auto_login"] && this.Services.Has("Auth")) { - this.Service("Auth").Login() - } super.RunApp(config) - this.Service("entity_manager.platform").LoadComponents() - this.Service("entity_manager.launcher").LoadComponents() - this.Service("entity_manager.backup").LoadComponents() + this["entity_manager.platform"].LoadComponents() + this["entity_manager.launcher"].LoadComponents() + this["entity_manager.backup"].LoadComponents() this.OpenApp() if (this.detectGames) { - this.Service("entity_manager.platform").DetectGames() + this["entity_manager.platform"].DetectGames() } } MigrateConfiguration() { - configFile := this.Parameter("previous_config_file") + configFile := this.Parameter["previous_config_file"] if (configFile && FileExist(configFile)) { - response := this.Service("manager.gui").Dialog(Map( + response := this["manager.gui"].Dialog(Map( "title", "Migrate settings?", "text", this.appName . " uses a new configuration file format, and has detected that you have a previous configuration file.`n`nWould you like to automatically migrate your settings?`n`nChoose Yes to migrate your previous configuration. Choose no to simply delete it and start from scratch." )) if (response == "Yes") { - this.Service("ini_migrator").Migrate(configFile, this.Config) + this["ini_migrator"].Migrate(configFile, this.Config) } else { FileDelete(configFile) } @@ -145,7 +116,7 @@ } InitialSetup(config) { - result := this.Service("manager.gui").Dialog(Map("type", "SetupWindow")) + result := this["manager.gui"].Dialog(Map("type", "SetupWindow")) if (result == "Exit") { this.ExitApp() @@ -153,45 +124,87 @@ this.detectGames := true } - this.isSetup := true super.InitialSetup(config) } UpdateStatusIndicators() { - if (this.Service("manager.gui").Has("MainWindow")) { - this.Service("manager.gui")["MainWindow"].UpdateStatusIndicator() + if (this["manager.gui"].Has("MainWindow")) { + serviceMgr := this.container["entity_manager.web_service"] + webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Condition(IsTrueCondition(), "StatusIndicator") + .Execute() + + windowObj := this["manager.gui"]["MainWindow"] + + for serviceId, webService in webServices { + windowObj.UpdateStatusIndicator(webService) + } } } ExitApp() { if (this.isSetup && this.Config["clean_launchers_on_exit"]) { - this.Service("manager.builder").CleanLaunchers() + this["manager.builder"].CleanLaunchers() } if (this.isSetup && this.Config["flush_cache_on_exit"]) { - this.Service("manager.cache").FlushCaches(false, false) + this["manager.cache"].FlushCaches(false, false) } super.ExitApp() } - OpenWebsite() { - Run("https://launchpad.games") + RestartApp() { + if (this.Services.Has("manager.gui")) { + guiMgr := this["manager.gui"] + + if (guiMgr.Has("MainWindow")) { + guiMgr.StoreWindowState(this["manager.gui"]["MainWindow"]) + } + } + + super.RestartApp() } - ProvideFeedback() { - this.Service("manager.gui").Dialog(Map("type", "FeedbackWindow")) + AddMainMenuEarlyItems(menuItems, showOpenItem := false) { + menuItems := super.AddMainMenuEarlyItems(menuItems, showOpenItem) + + launchersItems := [] + launchersItems.Push(Map("label", "&Clean Launchers", "name", "CleanLaunchers")) + launchersItems.Push(Map("label", "&Reload Launchers", "name", "ReloadLaunchers")) + + menuItems.Push(Map("label", "&Launchers", "name", "LaunchersMenu", "childItems", launchersItems)) + + return menuItems } - RestartApp() { - if (this.Services.Has("manager.gui")) { - guiMgr := this.Service("manager.gui") + HandleMainMenuClick(result) { + if (result == "CleanLaunchers") { + this["manager.builder"].CleanLaunchers() + } else if (result == "ReloadLaunchers") { + this["entity_manager.launcher"].LoadComponents(true) + guiMgr := this["manager.gui"] if (guiMgr.Has("MainWindow")) { - guiMgr.StoreWindowState(this.Service("manager.gui")["MainWindow"]) + guiMgr["MainWindow"].UpdateListView() } + } else if (result == "ManageModules") { + this["manager.gui"].OpenWindow("ManageModulesWindow") + } else if (result == "FlushCache") { + this["manager.cache"].FlushCaches(true, true) + } else { + super.HandleMainMenuClick(result) } - super.RestartApp() + return result + } + + GetToolsMenuItems() { + toolsItems := super.GetToolsMenuItems() + toolsItems.Push(Map("label", "&Modules", "name", "ManageModules")) + toolsItems.Push(Map("label", "&Flush Cache", "name", "FlushCache")) + + return toolsItems } } diff --git a/Lib/Launchpad/Builder/AhkLauncherBuilder.ahk b/launchpad_ahk/Lib/Launchpad/Builder/AhkLauncherBuilder.ahk similarity index 100% rename from Lib/Launchpad/Builder/AhkLauncherBuilder.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/AhkLauncherBuilder.ahk diff --git a/Lib/Launchpad/Builder/BuildFile/BuildFileBase.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/BuildFileBase.ahk similarity index 100% rename from Lib/Launchpad/Builder/BuildFile/BuildFileBase.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/BuildFileBase.ahk diff --git a/Lib/Launchpad/Builder/BuildFile/ComposableBuildFile.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/ComposableBuildFile.ahk similarity index 100% rename from Lib/Launchpad/Builder/BuildFile/ComposableBuildFile.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/ComposableBuildFile.ahk diff --git a/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk similarity index 95% rename from Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk index 2bd1146e..aaa77f98 100644 --- a/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk +++ b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk @@ -56,7 +56,7 @@ class CopyableBuildFile extends BuildFileBase { filePath := FileSelect(1,, this.launcherEntityObj.Id . ": " . this.RequestMessage, this.SelectFilter) if (filePath == "") { - this.app.Service("notifier").Warning("No file selected. Skipping build file.") + this.app["notifier"].Warning("No file selected. Skipping build file.") } return filePath diff --git a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk similarity index 82% rename from Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk index ed0eaedc..d1cbe8cd 100644 --- a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk +++ b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk @@ -14,9 +14,9 @@ class GameAhkFile extends ComposableBuildFile { "launcherName", this.launcherEntityObj.Id . " - Launchpad", "appVersion", appVersion, "appDir", this.appDir, - "gameConfig", this.launcherEntityObj["ManagedGame"].FieldData, + "gameConfig", this.launcherEntityObj["GameProcess"].FieldData, "launchpadLauncherConfig", this.launcherEntityObj.FieldData, - "launcherConfig", this.launcherEntityObj["ManagedLauncher"].FieldData, + "launcherConfig", this.launcherEntityObj["LauncherProcess"].FieldData, "launcherId", this.launcherEntityObj.Id, "themesDir", this.launcherEntityObj["ThemesDir"], "resourcesDir", this.launcherEntityObj["ResourcesDir"], @@ -35,7 +35,7 @@ class GameAhkFile extends ComposableBuildFile { GetPlatforms() { platforms := Map() - for key, platform in this.app.Service("entity_manager.platform").GetActivePlatforms(EntityQuery.RESULT_TYPE_ENTITIES) { + for key, platform in this.app["entity_manager.platform"].GetActivePlatforms(EntityQuery.RESULT_TYPE_ENTITIES) { platforms[key] := platform.FieldData } diff --git a/Lib/Launchpad/Builder/BuildFile/GameExeFile.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/GameExeFile.ahk similarity index 100% rename from Lib/Launchpad/Builder/BuildFile/GameExeFile.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/GameExeFile.ahk diff --git a/Lib/Launchpad/Builder/BuildFile/IconFile.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/IconFile.ahk similarity index 89% rename from Lib/Launchpad/Builder/BuildFile/IconFile.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/IconFile.ahk index 333333a4..729d4c01 100644 --- a/Lib/Launchpad/Builder/BuildFile/IconFile.ahk +++ b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/IconFile.ahk @@ -53,7 +53,7 @@ class IconFile extends CopyableBuildFile { } if (iconsCount == 0) { - this.app.Service("notifier").Warning("No icons could be extracted from %exeFile%. Please try another file.") + this.app["notifier"].Warning("No icons could be extracted from %exeFile%. Please try another file.") iconFilePath := "" this.Cleanup() } else { @@ -61,7 +61,7 @@ class IconFile extends CopyableBuildFile { iconFilePath := FileSelect(, iconsDir, "Select the correct icon from the extracted files", "Icons (*.ico)") if (iconFilePath == "") { - this.app.Service("notifier").Warning("Canceled icon selection. Please try again.") + this.app["notifier"].Warning("Canceled icon selection. Please try again.") this.Cleanup() } } diff --git a/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk similarity index 85% rename from Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk index 6f61ab18..d434a996 100644 --- a/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk +++ b/launchpad_ahk/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk @@ -6,14 +6,14 @@ class ShortcutFile extends CopyableBuildFile { if (destPath == "") { ext := ".lnk" - if (launcherEntityObj["ManagedGame"]["ShortcutSrc"] != "" && SubStr(launcherEntityObj["ManagedGame"]["ShortcutSrc"], -4) == ".url") { + if (launcherEntityObj["GameProcess"]["ShortcutSrc"] != "" && SubStr(launcherEntityObj["GameProcess"]["ShortcutSrc"], -4) == ".url") { ext := ".url" } destPath := launcherEntityObj["AssetsDir"] . "\" . launcherEntityObj.Id . ext } - super.__New(launcherEntityObj, launcherEntityObj["ManagedGame"]["ShortcutSrc"], destPath) + super.__New(launcherEntityObj, launcherEntityObj["GameProcess"]["ShortcutSrc"], destPath) } Locate() { diff --git a/Lib/Launchpad/Builder/BuilderBase.ahk b/launchpad_ahk/Lib/Launchpad/Builder/BuilderBase.ahk similarity index 95% rename from Lib/Launchpad/Builder/BuilderBase.ahk rename to launchpad_ahk/Lib/Launchpad/Builder/BuilderBase.ahk index b9609894..d4f77787 100644 --- a/Lib/Launchpad/Builder/BuilderBase.ahk +++ b/launchpad_ahk/Lib/Launchpad/Builder/BuilderBase.ahk @@ -32,7 +32,7 @@ class BuilderBase { } NeedsShortcutFile(launcherEntityObj) { - return (launcherEntityObj["ManagedGame"]["UsesShortcut"]) + return (launcherEntityObj["GameProcess"]["UsesShortcut"]) } BuildAction(launcherEntityObj, launcherDir, assetsDir) { diff --git a/Lib/Launchpad/BulkOperation/DetectedGamesOp/AddDetectedGamesOp.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/DetectedGamesOp/AddDetectedGamesOp.ahk similarity index 100% rename from Lib/Launchpad/BulkOperation/DetectedGamesOp/AddDetectedGamesOp.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/DetectedGamesOp/AddDetectedGamesOp.ahk diff --git a/Lib/Launchpad/BulkOperation/DetectedGamesOp/DetectGamesOp.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/DetectedGamesOp/DetectGamesOp.ahk similarity index 100% rename from Lib/Launchpad/BulkOperation/DetectedGamesOp/DetectGamesOp.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/DetectedGamesOp/DetectGamesOp.ahk diff --git a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/BuildLaunchersOp.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/BuildLaunchersOp.ahk similarity index 100% rename from Lib/Launchpad/BulkOperation/LauncherBuilderOp/BuildLaunchersOp.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/BuildLaunchersOp.ahk diff --git a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/CleanLaunchersOp.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/CleanLaunchersOp.ahk similarity index 100% rename from Lib/Launchpad/BulkOperation/LauncherBuilderOp/CleanLaunchersOp.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/CleanLaunchersOp.ahk diff --git a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk similarity index 90% rename from Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk index f1194602..f9f976b3 100644 --- a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk +++ b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk @@ -11,7 +11,7 @@ class LauncherBuilderOpBase extends LauncherGameOpBase { } if (Type(builder) == "String" && builder != "") { - builder := app.Service("manager.builder")[builder] + builder := app["manager.builder"][builder] } InvalidParameterException.CheckTypes("LauncherBuilderOpBase", "builder", builder, "BuilderBase") diff --git a/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk similarity index 90% rename from Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk index 558ee665..f9193b3d 100644 --- a/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk +++ b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk @@ -14,7 +14,7 @@ class LauncherGameOpBase extends BulkOperationBase { itemFailedText := "Failed." __New(app, launcherEntities := "", owner := "") { - this.launcherManager := app.Service("entity_manager.launcher") + this.launcherManager := app["entity_manager.launcher"] if (launcherEntities == "") { launcherEntities := this.launcherManager.All() @@ -63,12 +63,12 @@ class LauncherGameOpBase extends BulkOperationBase { VerifyRequirements() { if (this.app.Config["destination_dir"] == "") { - this.app.Service("notifier").Error("Launcher directory is not set.") + this.app["notifier"].Error("Launcher directory is not set.") return false } if (this.app.Config["assets_dir"] == "") { - this.app.Service("notifier").Error("Assets directory is not set.") + this.app["notifier"].Error("Assets directory is not set.") return false } diff --git a/Lib/Launchpad/BulkOperation/LauncherGameOp/ValidateLaunchersOp.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherGameOp/ValidateLaunchersOp.ahk similarity index 100% rename from Lib/Launchpad/BulkOperation/LauncherGameOp/ValidateLaunchersOp.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/LauncherGameOp/ValidateLaunchersOp.ahk diff --git a/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk b/launchpad_ahk/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk similarity index 92% rename from Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk rename to launchpad_ahk/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk index fab21b04..c51002ac 100644 --- a/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk +++ b/launchpad_ahk/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk @@ -10,7 +10,7 @@ class LoadEntitiesOp extends BulkOperationBase { failedMessage := "{n} launcher(s) could not be loaded due to errors." __New(app, launcherConfigObj := "", owner := "") { - this.launcherManager := app.Service("entity_manager.launcher") + this.launcherManager := app["entity_manager.launcher"] if (launcherConfigObj == "") { launcherConfigObj := this.launcherManager.GetConfig() @@ -29,7 +29,7 @@ class LoadEntitiesOp extends BulkOperationBase { } ; @todo replace this since EntityFactory is no longer used - factory := this.app.Service("EntityFactory") + factory := this.app["EntityFactory"] for key, config in this.launcherConfigObj { this.StartItem(key, key) diff --git a/Lib/Launchpad/Config/LauncherConfig.ahk b/launchpad_ahk/Lib/Launchpad/Config/LauncherConfig.ahk similarity index 100% rename from Lib/Launchpad/Config/LauncherConfig.ahk rename to launchpad_ahk/Lib/Launchpad/Config/LauncherConfig.ahk diff --git a/Lib/Launchpad/Config/LaunchpadConfig.ahk b/launchpad_ahk/Lib/Launchpad/Config/LaunchpadConfig.ahk similarity index 100% rename from Lib/Launchpad/Config/LaunchpadConfig.ahk rename to launchpad_ahk/Lib/Launchpad/Config/LaunchpadConfig.ahk diff --git a/Lib/Launchpad/Config/PlatformsConfig.ahk b/launchpad_ahk/Lib/Launchpad/Config/PlatformsConfig.ahk similarity index 100% rename from Lib/Launchpad/Config/PlatformsConfig.ahk rename to launchpad_ahk/Lib/Launchpad/Config/PlatformsConfig.ahk diff --git a/Lib/Launchpad/ConfigMigrator/LaunchpadIniMigrator.ahk b/launchpad_ahk/Lib/Launchpad/ConfigMigrator/LaunchpadIniMigrator.ahk similarity index 100% rename from Lib/Launchpad/ConfigMigrator/LaunchpadIniMigrator.ahk rename to launchpad_ahk/Lib/Launchpad/ConfigMigrator/LaunchpadIniMigrator.ahk diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/launchpad_ahk/Lib/Launchpad/DetectedGame/DetectedGame.ahk similarity index 75% rename from Lib/Launchpad/DetectedGame/DetectedGame.ahk rename to launchpad_ahk/Lib/Launchpad/DetectedGame/DetectedGame.ahk index 02851d7c..e92e87a7 100644 --- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk +++ b/launchpad_ahk/Lib/Launchpad/DetectedGame/DetectedGame.ahk @@ -8,7 +8,7 @@ class DetectedGame { installDir := "" launcherInstallDir := "" exeName := "" - launcherSpecificId := "" + platformRef := "" possibleExeNames := [] keyMap := Map() ; @todo Move this to properties or config or allow it to be extended @@ -17,16 +17,16 @@ class DetectedGame { prioritySuffixes := ["-Win64-Shipping", "-Win32-Shipping"] filterExes := [] - __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", launcherSpecificId := "", possibleExeNames := "") { + __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", platformRef := "", possibleExeNames := "", displayName := "") { this.key := key - this.displayName := key + this.displayName := displayName ? displayName : key this.platform := platform this.detectedKey := key this.launcherType := launcherType this.gameType := gameType this.installDir := installDir this.exeName := exeName - this.launcherSpecificId := launcherSpecificId + this.platformRef := platformRef if (possibleExeNames) { if (Type(possibleExeNames) == "String") { @@ -44,12 +44,10 @@ class DetectedGame { if ( this.displayName != launcher["name"] - || this.launcherType != launcher["ManagedLauncher"].EntityTypeId - || this.gameType != launcher["ManagedGame"].EntityTypeId - || this.installDir != launcher["ManagedGame"]["InstallDir"] - || this.launcherInstallDir != launcher["ManagedLauncher"]["InstallDir"] - || this.exeName != launcher["ManagedGame"]["Exe"] - || this.launcherSpecificId != launcher["ManagedGame"]["LauncherSpecificId"] + || this.installDir != launcher["GameProcess"]["InstallDir"] + || this.launcherInstallDir != launcher["LauncherProcess"]["InstallDir"] + || this.exeName != launcher["GameProcess"]["Exe"] + || this.platformRef != launcher["GameProcess"]["PlatformRef"] ) { hasChanges := true } @@ -70,40 +68,28 @@ class DetectedGame { modified := true } - if (this.launcherType && launcher["ManagedLauncher"].EntityTypeId != this.launcherType) { - launcher["ManagedLauncher"].EntityType := this.launcherType - modified := true + if (this.launcherInstallDir && launcher["LauncherProcess"]["InstallDir"] != this.launcherInstallDir) { + launcher["LauncherProcess"]["InstallDir"] := this.launcherInstallDir } - if (this.gameType && launcher["ManagedGame"].EntityTypeId != this.gameType) { - launcher["ManagedGame"].EntityType := this.gameType + if (this.installDir && launcher["GameProcess"]["InstallDir"] != this.installDir) { + launcher["GameProcess"]["InstallDir"] := this.installDir modified := true } - if (this.launcherInstallDir && launcher["ManagedLauncher"]["InstallDir"] != this.launcherInstallDir) { - launcher["ManagedLauncher"]["InstallDir"] := this.launcherInstallDir - } - - if (this.installDir && launcher["ManagedGame"]["InstallDir"] != this.installDir) { - launcher["ManagedGame"]["InstallDir"] := this.installDir - modified := true - } - - if (this.exeName && launcher["ManagedGame"]["Exe"] != this.exeName) { - launcher["ManagedGame"]["Exe"] := this.exeName + if (this.exeName && launcher["GameProcess"]["Exe"] != this.exeName) { + launcher["GameProcess"]["Exe"] := this.exeName modified := true } if (modified) { - launcher.SaveModifiedData() + launcher.SaveEntity(true) } } CreateLauncher(launcherManager) { config := Map( - "Platform", this.platform.key, - "LauncherType", this.launcherType, - "GameType", this.gameType + "Platform", this.platform.key ) if (this.displayName && this.displayName != this.key) { @@ -122,8 +108,8 @@ class DetectedGame { config["GameExe"] := this.exeName } - if (this.launcherSpecificId) { - config["GameLauncherSpecificId"] := this.launcherSpecificId + if (this.platformRef) { + config["GamePlatformRef"] := this.platformRef } entityObj := launcherManager.GetFactory().CreateEntity(this.key, config) @@ -153,15 +139,19 @@ class DetectedGame { } } - key := StrReplace(key, ": ", " - ") - key := StrReplace(key, ":", "") - key := StrReplace(key, "\", "") - key := StrReplace(key, "/", "") - key := StrReplace(key, "*", "") - key := StrReplace(key, "?", "") - key := StrReplace(key, "`"", "") - key := StrReplace(key, "®", "") - key := StrReplace(key, "â„¢", "") + replacements := [ + [" : ", " - "], + [": ", " - "], + [":", "-"], + ["®", ""], + ["â„¢", ""] + ] + + for , vals in replacements { + key := StrReplace(key, vals[1], vals[2]) + } + + key := RegExReplace(key, "[\\/:*?`"<>|]'") return key } diff --git a/Lib/Launchpad/Entity/ManagedGameEntity.ahk b/launchpad_ahk/Lib/Launchpad/Entity/GameProcessEntity.ahk similarity index 70% rename from Lib/Launchpad/Entity/ManagedGameEntity.ahk rename to launchpad_ahk/Lib/Launchpad/Entity/GameProcessEntity.ahk index 907991e6..da93710b 100644 --- a/Lib/Launchpad/Entity/ManagedGameEntity.ahk +++ b/launchpad_ahk/Lib/Launchpad/Entity/GameProcessEntity.ahk @@ -1,8 +1,5 @@ -class ManagedGameEntity extends ManagedEntityBase { - configPrefix := "Game" - defaultType := "Default" +class GameProcessEntity extends LaunchProcessEntity { defaultClass := "SimpleGame" - dataSourcePath := "game-types" BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() @@ -10,7 +7,7 @@ class ManagedGameEntity extends ManagedEntityBase { definitions["HasLoadingWindow"] := Map( "type", "boolean", "description", "Whether or not the game has a loading window to watch for.", - "storageKey", this.configPrefix . "HasLoadingWindow", + "storageKey", "HasLoadingWindow", "default", false ) @@ -20,7 +17,7 @@ class ManagedGameEntity extends ManagedEntityBase { definitions["LoadingWindowProcessType"] := Map( "description", "Which method to use to wait for the game's loading window to open.", "help", "This lets Launchpad know when the game is loading. Only used if a LoadingWindowProcessId is set", - "storageKey", this.configPrefix . "LoadingWindowProcessType", + "storageKey", "LoadingWindowProcessType", "default", "Exe", "widget", "select", "selectOptionsCallback", ObjBindMethod(this, "ListProcessTypes") @@ -30,7 +27,7 @@ class ManagedGameEntity extends ManagedEntityBase { ; - Title - This value will default to the game's Key unless overridden ; - Class - This value should be set to the game's window class definitions["LoadingWindowProcessId"] := Map( - "storageKey", this.configPrefix . "LoadingWindowProcessId", + "storageKey", "LoadingWindowProcessId", "help", "This value's type is dependent on the GameProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.", "modes", Map( "simple", Map("formField", false) @@ -40,16 +37,6 @@ class ManagedGameEntity extends ManagedEntityBase { return definitions } - GetBlizzardProductKey() { - productKey := this["LauncherSpecificId"] - - if (this.HasConfigValue("BlizzardProductId", true, false)) { - productKey := this.GetConfigValue("BlizzardProductId") - } - - return productKey - } - ShouldDetectShortcutSrc(extraConfig) { detectShortcut := false @@ -78,28 +65,28 @@ class ManagedGameEntity extends ManagedEntityBase { return detectShortcut } - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) - exeKey := this.configPrefix . "Exe" + AutoDetectValues() { + detectedValues := super.AutoDetectValues() + exeKey := "Exe" if (!detectedValues.Has(exeKey)) { detectedValues[exeKey] := this["Exe"] ? this["Exe"] : this.Id . ".exe" } - if (!detectedValues.Has(this.configPrefix . "ProcessId") || !detectedValues[this.configPrefix . "ProcessId"]) { - detectedValues[this.configPrefix . "ProcessId"] := detectedValues[exeKey] + if (!detectedValues.Has("ProcessId") || !detectedValues["ProcessId"]) { + detectedValues["ProcessId"] := detectedValues[exeKey] } - if (detectedValues.Has(this.configPrefix . "ProcessType")) { - detectedValues[this.configPrefix . "LoadingWindowProcessType"] := detectedValues[this.configPrefix . "ProcessType"] + if (detectedValues.Has("ProcessType")) { + detectedValues["LoadingWindowProcessType"] := detectedValues["ProcessType"] } if (!this["LoadingWindowProcessId"]) { - detectedValues[this.configPrefix . "LoadingWindowProcessId"] := detectedValues[exeKey] + detectedValues["LoadingWindowProcessId"] := detectedValues[exeKey] } if (this.ShouldDetectShortcutSrc(detectedValues)) { - basePath := this["AssetsDir"] . "\" . this.Id + basePath := this.ParentEntity["AssetsDir"] . "\" . this.Id shortcutSrc := "" if (FileExist(basePath . ".lnk")) { @@ -111,7 +98,7 @@ class ManagedGameEntity extends ManagedEntityBase { } if (shortcutSrc != "") { - detectedValues[this.configPrefix . "ShortcutSrc"] := shortcutSrc + detectedValues["ShortcutSrc"] := shortcutSrc } } diff --git a/Lib/Launchpad/Entity/ManagedEntityBase.ahk b/launchpad_ahk/Lib/Launchpad/Entity/LaunchProcessEntity.ahk similarity index 76% rename from Lib/Launchpad/Entity/ManagedEntityBase.ahk rename to launchpad_ahk/Lib/Launchpad/Entity/LaunchProcessEntity.ahk index 976c6489..a102f336 100644 --- a/Lib/Launchpad/Entity/ManagedEntityBase.ahk +++ b/launchpad_ahk/Lib/Launchpad/Entity/LaunchProcessEntity.ahk @@ -1,9 +1,10 @@ -class ManagedEntityBase extends AppEntityBase { - defaultType := "Default" +class LaunchProcessEntity extends FieldableEntity { defaultClass := "Default" - DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { - return container.Get("entity_manager.launcher")[id] + DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") { + this.parentEntityObj := parentEntity + ? parentEntity + : container.Get("entity_manager.launcher")[id] } GetDefaultFieldGroups() { @@ -48,21 +49,10 @@ class ManagedEntityBase extends AppEntityBase { ) ) - definitions["EntityType"] := Map( - "default", this.defaultType, - "description", "The key of the managed type to load settings and defaults from.", - "required", true, - "storageKey", this.configPrefix . "Type", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListEntityTypes"), - "group", "general" - ) - - definitions["EntityClass"] := Map( + definitions["ProcessClass"] := Map( "default", this.defaultClass, - "description", "The name of the AHK class that will be used to control the managed entity.", + "description", "The name of the AHK class that will be used to control the process.", "formField", false, - "storageKey", this.configPrefix . "Class", "required", true, "group", "advanced", "modes", Map( @@ -73,11 +63,10 @@ class ManagedEntityBase extends AppEntityBase { definitions["SearchDirs"] := Map( "type", "directory", "mustExist", false, - "storageKey", this.configPrefix . "SearchDirs", "default", [A_ProgramFiles], "description", "Possible parent directories where the game's launcher might exist, to be used for auto-detection.", "help", "These should be as specific as possible to reduce detection time.", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "locations", "modes", Map( "simple", Map("formField", false) @@ -87,7 +76,6 @@ class ManagedEntityBase extends AppEntityBase { definitions["InstallDir"] := Map( "type", "directory", "mustExist", false, - "storageKey", this.configPrefix . "InstallDir", "group", "locations", "modes", Map( "simple", Map("group", "general") @@ -99,7 +87,6 @@ class ManagedEntityBase extends AppEntityBase { "type", "file", "fileMask", "*.exe", "mustExist", false, - "storageKey", this.configPrefix . "Exe", "description", "This can be the full path on the system to the launcher's .exe file, or simply the name of the .exe file itself.", "help", "If the .exe doesn't include the absolute path, auto-detection will be used by searching the DestinationDirs.", "group", "locations", @@ -113,7 +100,6 @@ class ManagedEntityBase extends AppEntityBase { ; - BlizzardProductDb (will search Battle.net's product.db file if it can be located for the installation directory, and the file will be found from there ; - Registry (will get a directory from the registry key specified by LocateRegKey and search for the file within it) definitions["LocateMethod"] := Map( - "storageKey", this.configPrefix . "LocateMethod", "default", "SearchDirs", "description", "How to search for the .exe if it isn't a full path already", "group", "general", @@ -122,16 +108,14 @@ class ManagedEntityBase extends AppEntityBase { ), "widget", "select", "selectOptionsCallback", ObjBindMethod(this, "ListLocateMethods"), - "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for LauncherSpecificId within the Blizzard product.db file if present" + "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for PlatformRef within the Blizzard product.db file if present" ) definitions["WindowTitle"] := Map( - "storageKey", this.configPrefix . "WindowTitle", "group", "process" ) definitions["LocateRegView"] := Map( - "storageKey", this.configPrefix . "LocateRegView", "default", 64, "group", "registry", "widget", "select", @@ -143,7 +127,7 @@ class ManagedEntityBase extends AppEntityBase { ) definitions["LocateRegKey"] := Map( - "storageKey", this.configPrefix . "LocateRegKey", + "title", "Registry Locator - Key", "group", "registry", "description", "The registry key to look up the install dir within.", "help", "Path parts should be separated with backslashes and must start with one of: HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, or the abbreviation of one of those. To read from a remote registry, prefix the root path with two backslashes and the computer name.`n`nSimple example: HKLM\Path\To\Key`nRemote example: \\OTHERPC\HKLM\Path\To\Key", @@ -153,7 +137,7 @@ class ManagedEntityBase extends AppEntityBase { ) definitions["LocateRegValue"] := Map( - "storageKey", this.configPrefix . "LocateRegValue", + "title", "Registry Locator - Value", "group", "registry", "description", "The name of the registry value to look up within the specified key.", "help", "Example: InstallPath", @@ -163,7 +147,7 @@ class ManagedEntityBase extends AppEntityBase { ) definitions["LocateRegRemovePrefix"] := Map( - "storageKey", this.configPrefix . "LocateRegRemovePrefix", + "title", "Registry Locator - Remove Prefix", "group", "registry", "modes", Map( "simple", Map("formField", false) @@ -171,7 +155,7 @@ class ManagedEntityBase extends AppEntityBase { ) definitions["LocateRegRemoveSuffix"] := Map( - "storageKey", this.configPrefix . "LocateRegRemoveSuffix", + "title", "Registry Locator - Remove Suffix", "group", "registry", "modes", Map( "simple", Map("formField", false) @@ -179,7 +163,7 @@ class ManagedEntityBase extends AppEntityBase { ) definitions["LocateRegStripQuotes"] := Map( - "storageKey", this.configPrefix . "LocateRegStripQuotes", + "title", "Registry Locator - Strip Quotes", "default", false, "group", "registry", "description", "Strip quotes from registry value", @@ -188,17 +172,17 @@ class ManagedEntityBase extends AppEntityBase { ) ) - definitions["LauncherSpecificId"] := Map( - "storageKey", this.configPrefix . "LauncherSpecificId", + definitions["PlatformRef"] := Map( + "title", "Platform Reference", "description", "If the item is known to the launcher by a specific ID, it should be stored here.", "group", "general" ) definitions["WorkingDir"] := Map( + "title", "Working Directory", "type", "directory", "description", "The directory that the launcher should be run from.", "help", "If not set, it will be run without setting an explicit working directory, which is usually sufficient.", - "storageKey", this.configPrefix . "WorkingDir", "group", "locations", "modes", Map( "simple", Map("formField", false) @@ -210,7 +194,6 @@ class ManagedEntityBase extends AppEntityBase { definitions["RunType"] := Map( "description", "Which method to use for launching this item.", "help", "This is only needed for launchers that have to manage their own process.", - "storageKey", this.configPrefix . "RunType", "default", "Command", "group", "process", "widget", "select", @@ -220,15 +203,13 @@ class ManagedEntityBase extends AppEntityBase { definitions["UsesShortcut"] := Map( "type", "boolean", "description", "Whether a shortcut file will be used when starting the internally-managed game launcher", - "formField", false, - "storageKey", this.configPrefix . "UsesShortcut" + "formField", false ) definitions["ReplaceProcess"] := Map( "type", "boolean", "description", "Kill and re-launch the game process immediately after it is detected.", "help", "This can be used to force Launchpad to own the game process, but won't for for every game.", - "storageKey", this.configPrefix . "ReplaceProcess", "default", false, "group", "process" ) @@ -238,9 +219,9 @@ class ManagedEntityBase extends AppEntityBase { ; - The path of an .exe file on the system to which a shortcut will be created in AssetsDir if it doesn't already exist. Using this option ; is usually not necessary, since you can run the .exe directly instead. definitions["ShortcutSrc"] := Map( + "title", "Shortcut Source", "description", "The shortcut file used to launch the game launcher itself.", "help", "This is typically only needed if the Shortcut LauncherRunType is selected.", - "storageKey", this.configPrefix . "ShortcutSrc", "group", "locations", "modes", Map( "simple", Map("group", "general") @@ -252,7 +233,6 @@ class ManagedEntityBase extends AppEntityBase { ; - Scheduled (Creates an immediate scheduled task that runs the game, then waits until the window opens (if needed) and then closes) definitions["RunMethod"] := Map( "description", "Which method to use to run the RunCmd", - "storageKey", this.configPrefix . "RunMethod", "default", "Run", "group", "process", "widget", "select", @@ -265,7 +245,6 @@ class ManagedEntityBase extends AppEntityBase { definitions["ProcessType"] := Map( "description", "Which method to use to wait for the game to close.", "help", "This is not needed if the GameRunType is RunWait", - "storageKey", this.configPrefix . "ProcessType", "default", "Exe", "group", "process", "widget", "select", @@ -277,7 +256,6 @@ class ManagedEntityBase extends AppEntityBase { ; - Class - This value should be set to the game's window class definitions["ProcessId"] := Map( "help", "This value's type is dependent on the ProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.", - "storageKey", this.configPrefix . "ProcessId", "group", "process", "modes", Map( "simple", Map("formField", false) @@ -286,7 +264,6 @@ class ManagedEntityBase extends AppEntityBase { definitions["ProcessTimeout"] := Map( "description", "The number of seconds to wait before giving up when waiting for a process.", - "storageKey", this.configPrefix . "ProcessTimeout", "default", 30, "group", "process", "modes", Map( @@ -295,45 +272,26 @@ class ManagedEntityBase extends AppEntityBase { ) definitions["RunCmd"] := Map( + "title", "Run Command", "description", "The command that will be used to run the game's launcher.", "help", "Typically only used if LauncherRunType is Command.", - "storageKey", this.configPrefix . "RunCmd", "group", "process" ) return definitions } - GetData() { - if (!this.ParentEntity) { - throw EntityException("A parent entity is required on type " . Type(this)) - } - - return this.ParentEntity.GetData() - } - - _createEntityData() { - return "" - } - - DiscoverDataSourceItemKey() { - return this["EntityType"] - } - - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) + AutoDetectValues() { + detectedValues := super.AutoDetectValues() processId := "" - usesShortcut := false - if (this.GetData().HasValue(this.configPrefix . "UsesShortcut")) { - usesShortcut := this.GetData().GetValue(this.configPrefix . "UsesShortcut") - } else { - usesShortcut := (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "") - } + usesShortcut := (this.GetData().HasValue("UsesShortcut")) + ? this.GetData().GetValue("UsesShortcut") + : (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "") - detectedValues[this.configPrefix . "UsesShortcut"] := usesShortcut - detectedValues[this.configPrefix . "RunType"] := usesShortcut ? "Shortcut" : "Command" - detectedValues[this.configPrefix . "InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir + detectedValues["UsesShortcut"] := usesShortcut + detectedValues["RunType"] := usesShortcut ? "Shortcut" : "Command" + detectedValues["InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir if (this["ProcessType"] == "Exe") { SplitPath(this["Exe"], &processId) @@ -341,37 +299,12 @@ class ManagedEntityBase extends AppEntityBase { processId := this["WindowTitle"] ? this["WindowTitle"] : this.Id } - detectedValues[this.configPrefix . "ProcessId"] := processId - detectedValues[this.configPrefix . "WorkingDir"] := this["InstallDir"] + detectedValues["ProcessId"] := processId + detectedValues["WorkingDir"] := this["InstallDir"] return detectedValues } - ListEntityTypes() { - types := [] - dataSources := this.GetAllDataSources() - dsPath := this.GetDataSourceItemPath() - - for index, dataSource in dataSources { - for listingIndex, listingItem in dataSource.ReadListing(dsPath) { - exists := false - - for index, item in types { - if (item == listingItem) { - exists := true - break - } - } - - if (!exists) { - types.Push(listingItem) - } - } - } - - return types - } - ListRunTypes() { return [ "Command", @@ -381,19 +314,25 @@ class ManagedEntityBase extends AppEntityBase { ListProcessTypes() { return [ - "Exe", "Title", "Class" + "Exe", + "Title", + "Class" ] } ListRunMethods() { return [ - "Run", "Scheduled", "RunWait" + "Run", + "Scheduled", + "RunWait" ] } ListLocateMethods() { return [ - "Search", "Registry", "BlizzardProductDb" + "Search", + "Registry", + "BlizzardProductDb" ; TODO Move this to the Blizzard module ] } @@ -422,18 +361,20 @@ class ManagedEntityBase extends AppEntityBase { validateResult["invalidFields"].push("RunCmd") } - ; TODO: Perform more launcher and game type validation here + ; TODO: Perform more validation here return validateResult } ShortcutFileExists() { - shortcutSrc := this["ShortcutSrc"] != "" ? this["ShortcutSrc"] : this.GetAssetPath(this.Id . ".lnk") + shortcutSrc := (this["ShortcutSrc"] != "") + ? this["ShortcutSrc"] + : this["AssetsDir"] . "\" . this.Id . ".lnk" exists := FileExist(shortcutSrc) if (!exists) { - shortcutSrc := this.GetAssetPath(this.Id . ".url") + shortcutSrc := this["AssetsDir"] . "\" . this.Id . ".url" exists := FileExist(shortcutSrc) } @@ -443,8 +384,7 @@ class ManagedEntityBase extends AppEntityBase { LocateInstallDir() { installDir := "" - ; TODO: Add additional methods to detect the install dir - + ; TODO Move BlizzardProductDb method to an event handled by the Blizzard module if (this["LocateMethod"] == "BlizzardProductDb") { blizzardDir := this.GetBlizzardProductDir() @@ -453,6 +393,8 @@ class ManagedEntityBase extends AppEntityBase { } } + ; TODO: Add additional methods to detect the install dir + return installDir } @@ -504,6 +446,7 @@ class ManagedEntityBase extends AppEntityBase { } } } else if (this["LocateMethod"] == "BlizzardProductDb") { + ; TODO Move BlizzardProductDb method to an event handled by the Blizzard module blizzardDir := this.GetBlizzardProductDir() if (blizzardDir != "") { @@ -543,18 +486,14 @@ class ManagedEntityBase extends AppEntityBase { return path } - GetBlizzardProductKey() { - return "bna" ; Default to the Battle.net client itself - } - + ; TODO Move this method to the Blizzard module and call it from an eevent GetBlizzardProductDir() { - path := "" - productCode := this.GetBlizzardProductKey() - - if (productCode != "" && this.app.Services.Has("BlizzardProductDb")) { - path := this.app.Service("BlizzardProductDb").GetProductInstallPath(productCode) - } + productCode (HasBase(this, GameProcessEntity.Prototype)) + ? productCode := this["PlatformRef"] + : "bna" ; Default to the Battle.net client itself - return path + return (productCode != "" && this.app.Services.Has("BlizzardProductDb")) + ? this.app["BlizzardProductDb"].GetProductInstallPath(productCode) + : "" } } diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/launchpad_ahk/Lib/Launchpad/Entity/LauncherEntity.ahk similarity index 77% rename from Lib/Launchpad/Entity/LauncherEntity.ahk rename to launchpad_ahk/Lib/Launchpad/Entity/LauncherEntity.ahk index edaec377..b5d4c554 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/launchpad_ahk/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -1,7 +1,5 @@ -class LauncherEntity extends AppEntityBase { - dataSourcePath := "games" - configPrefix := "Launcher" - additionalManagedLauncherDefaults := Map() +class LauncherEntity extends FieldableEntity { + additionalLauncherProcessDefaults := Map() IsBuilt { get => this.LauncherExists(false) @@ -29,6 +27,11 @@ class LauncherEntity extends AppEntityBase { "weight", 80 ) + groups["advanced"] := Map( + "name", "Advanced", + "weight", 100 + ) + return groups } @@ -47,18 +50,12 @@ class LauncherEntity extends AppEntityBase { definitions["id"]["modes"]["wizard"]["formField"] := true definitions["id"]["modes"]["wizard"]["widget"] := "combo" - definitions["id"]["modes"]["wizard"]["selectOptionsCallback"] := ObjBindMethod(this, "ListKnownGames") + definitions["id"]["modes"]["wizard"]["selectOptionsCallback"] := ObjBindMethod(this, "ListEntities", false, true) definitions["id"]["modes"]["wizard"]["description"] := "Select an existing game from the API, or enter a custom game key to create your own." definitions["name"]["description"] := "You can change the display name of the game if it differs from the key." definitions["name"]["help"] := "The launcher filename will still be created using the key." - if (definitions.Has("DataSourceItemKey")) { - definitions["DataSourceItemKey"]["default"] := "" - definitions["DataSourceItemKey"]["description"] := "The key to use when looking this item up in its data source(s)." - definitions["DataSourceItemKey"]["help"] := "By default, this is the same as the main key." - } - definitions["Platform"] := Map( "type", "entity_reference", "entityType", "platform", @@ -68,10 +65,10 @@ class LauncherEntity extends AppEntityBase { "showDefaultCheckbox", false ) - definitions["ManagedLauncher"] := Map( + definitions["LauncherProcess"] := Map( "type", "entity_reference", "required", true, - "entityType", "managed_launcher", + "entityType", "launcher_process", "child", true, "weight", -25, "widget", "entity_select", @@ -87,13 +84,13 @@ class LauncherEntity extends AppEntityBase { ), "default", this.idVal, "showDefaultCheckbox", false, - "valueType", EntityFieldBase.VALUE_TYPE_DEFAULT + "storeEntityData", true ) - definitions["ManagedGame"] := Map( + definitions["GameProcess"] := Map( "type", "entity_reference", "required", true, - "entityType", "managed_game", + "entityType", "game_process", "child", true, "weight", -20, "widget", "entity_select", @@ -109,7 +106,7 @@ class LauncherEntity extends AppEntityBase { ), "default", this.idVal, "showDefaultCheckbox", false, - "valueType", EntityFieldBase.VALUE_TYPE_DEFAULT + "storeEntityData", true ) definitions["DestinationDir"] := Map( @@ -189,7 +186,7 @@ class LauncherEntity extends AppEntityBase { definitions["RunBefore"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -201,7 +198,7 @@ class LauncherEntity extends AppEntityBase { definitions["CloseBefore"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -213,7 +210,7 @@ class LauncherEntity extends AppEntityBase { definitions["RunAfter"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -225,7 +222,7 @@ class LauncherEntity extends AppEntityBase { definitions["CloseAfter"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -295,6 +292,15 @@ class LauncherEntity extends AppEntityBase { "help", "If the Steam Overlay attaches within this time, and the Force option is not active, then the Launchpad Overlay will not be used." ) + definitions["AssetsDir"] := Map( + "type", "directory", + "description", "The directory where any required assets for this launcher will be saved.", + "default", this.app.Config["assets_dir"] . "\" . this.Id, + "group", "advanced", + "formField", false, + "modes", Map("simple", Map("formField", false)) + ) + return definitions } @@ -305,13 +311,6 @@ class LauncherEntity extends AppEntityBase { return (FileExist(this.GetLauncherFile(this.Id, checkSourceFile)) != "") } - ListKnownGames() { - return this.container - .Get("manager.data_source") - .GetDefaultDataSource() - .ReadListing("game-keys") - } - LauncherIsOutdated() { outdated := true @@ -320,7 +319,7 @@ class LauncherEntity extends AppEntityBase { if (filePath && FileExist(filePath)) { launcherVersion := FileGetVersion(this.GetLauncherFile(this.Id)) - if (launcherVersion && !this.app.Service("version_checker").VersionIsOutdated(this.app.Version, launcherVersion)) { + if (launcherVersion && !this.app["version_checker"].VersionIsOutdated(this.app.Version, launcherVersion)) { outdated := false } @@ -330,7 +329,7 @@ class LauncherEntity extends AppEntityBase { if (!buildInfo["Version"] || !buildInfo["Timestamp"]) { outdated := true } else { - if (configInfo["Version"] && this.app.Service("version_checker").VersionIsOutdated(configInfo["Version"], buildInfo["Version"])) { + if (configInfo["Version"] && this.app["version_checker"].VersionIsOutdated(configInfo["Version"], buildInfo["Version"])) { outdated := true } else if (configInfo["Timestamp"] && DateDiff(configInfo["Timestamp"], buildInfo["Timestamp"], "S") > 0) { outdated := true @@ -379,76 +378,17 @@ class LauncherEntity extends AppEntityBase { return ValidateResult } - SaveModifiedData() { - super.SaveModifiedData() + SaveEntity(recurse := true) { + super.SaveEntity(recurse) this.app.State.SetLauncherConfigInfo(this.Id) } - DiscoverDataSourceItemKey() { - if (!this["DataSourceItemKey"]) { - dataSources := this.GetAllDataSources() - - for index, dataSource in dataSources { - platform := this["Platform"] ? this["Platform"]["id"] : "" - apiPath := "lookup/" this.Id - - if (platform) { - apiPath .= "/" . platform - } - - dsData := dataSource.ReadJson(apiPath) - - if (dsData != "" && dsData.Has("id") && dsData["id"]) { - this["DataSourceItemKey"] := dsData["id"] - break - } - } - } - - if (this["DataSourceItemKey"]) { - return this["DataSourceItemKey"] - } else { - return "" - } - } - IconFileExists() { - iconSrc := this["IconSrc"] != "" ? this["IconSrc"] : this.GetAssetPath(this.Id . ".ico") - return FileExist(iconSrc) - } - - MergeAdditionalDataSourceDefaults(defaults, dataSourceData) { - launcherType := this.DetectLauncherType(defaults, dataSourceData) - - checkType := (launcherType == "") ? "Default" : launcherType - if (dataSourceData.Has("Launchers") && dataSourceData["Launchers"].Has(checkType) && HasBase(dataSourceData["Launchers"][checkType], Map.Prototype)) { - this.additionalManagedLauncherDefaults := this.merger.Merge(dataSourceData["Launchers"][checkType], this.additionalManagedLauncherDefaults) - defaults := this.merger.Merge(defaults, dataSourceData["Launchers"][checkType]) - } - - defaults["ManagedLauncher"] := launcherType + iconSrc := (this["IconSrc"] != "") + ? this["IconSrc"] + : this["AssetsDir"] . "\" . this.Id . ".ico" - return defaults - } - - DetectLauncherType(defaults, dataSourceData := "") { - launcherType := "" - - if (this.UnmergedFieldData.Has("LauncherType")) { - launcherType := this.UnmergedFieldData["LauncherType"] - } else if (defaults.Has("LauncherType")) { - launcherType := defaults["LauncherType"] - } - - if (launcherType == "") { - launcherType := "Default" - } - - if (dataSourceData != "" && dataSourceData.Has("Launchers")) { - launcherType := this._dereferenceKey(launcherType, dataSourceData["Launchers"]) - } - - return launcherType + return FileExist(iconSrc) } _dereferenceKey(key, map) { @@ -459,19 +399,19 @@ class LauncherEntity extends AppEntityBase { return key } - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) + AutoDetectValues() { + detectedValues := super.AutoDetectValues() if (!detectedValues.Has("IconSrc")) { checkPath := this["AssetsDir"] . "\" . this.Id . ".ico" if (FileExist(checkPath)) { detectedValues["IconSrc"] := checkPath - } else if (this.Has("ManagedGame", false) && this["ManagedGame"].Has("Exe", false)) { - detectedValues["IconSrc"] := this["ManagedGame"].LocateExe() + } else if (this.Has("GameProcess", false) && this["GameProcess"].Has("Exe", false)) { + detectedValues["IconSrc"] := this["GameProcess"].LocateExe() } else { theme := this.container.Get("manager.theme").GetComponent() - detectedValues["IconSrc"] := theme.GetIconPath("Game") + detectedValues["IconSrc"] := theme.GetIconPath("game") } } diff --git a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk b/launchpad_ahk/Lib/Launchpad/Entity/LauncherProcessEntity.ahk similarity index 79% rename from Lib/Launchpad/Entity/ManagedLauncherEntity.ahk rename to launchpad_ahk/Lib/Launchpad/Entity/LauncherProcessEntity.ahk index c060cb91..ee21fcc1 100644 --- a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk +++ b/launchpad_ahk/Lib/Launchpad/Entity/LauncherProcessEntity.ahk @@ -1,16 +1,13 @@ -class ManagedLauncherEntity extends ManagedEntityBase { - configPrefix := "Launcher" - defaultType := "Default" +class LauncherProcessEntity extends LaunchProcessEntity { defaultClass := "SimpleLauncher" - dataSourcePath := "launcher-types" BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() - definitions["ManagedGame"] := Map( + definitions["GameProcess"] := Map( "type", "entity_reference", "required", true, - "entityType", "managed_game", + "entityType", "game_process", "child", false, "formField", false, "editable", false @@ -18,14 +15,14 @@ class ManagedLauncherEntity extends ManagedEntityBase { definitions["CloseBeforeRun"] := Map( "type", "boolean", - "storageKey", this.configPrefix . "CloseBeforeRun", + "storageKey", "CloseBeforeRun", "default", false, "description", "whether or not the launcher should be closed (if it is running) before starting the game" ) definitions["CloseAfterRun"] := Map( "type", "boolean", - "storageKey", this.configPrefix . "CloseAfterRun", + "storageKey", "CloseAfterRun", "default", false, "description", "Indicates whether the launcher should be closed after the game stops" ) @@ -34,7 +31,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "ClosePreDelay", + "storageKey", "ClosePreDelay", "default", 0, "required", true, "group", "advanced", @@ -48,7 +45,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "ClosePostDelay", + "storageKey", "ClosePostDelay", "default", 0, "required", true, "group", "advanced", @@ -65,7 +62,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { ; - "AutoPolite" - Automatically attempt to politely close the launcher, or fail if it can't be closed politely ; - "AutoKill" - Automatically attempts to end the launcher process, forcefully if needed definitions["CloseMethod"] := Map( - "storageKey", this.configPrefix . "CloseMethod", + "storageKey", "CloseMethod", "default", "Prompt", "description", "How to attempt to close the launcher if running", "widget", "select", @@ -77,7 +74,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "RecheckDelay", + "storageKey", "RecheckDelay", "default", 10, "required", true, "group", "advanced", @@ -91,7 +88,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "WaitTimeout", + "storageKey", "WaitTimeout", "default", 30, "required", true, "group", "advanced", @@ -105,11 +102,11 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "ms", "min", 0, - "storageKey", this.configPrefix . "KillPreDelay", + "storageKey", "KillPreDelay", "default", 10, "required", true, "group", "advanced", - "description", "If killing a managed launcher forcefully, ending the process will be delayed by this number of seconds.", + "description", "If killing a launch process forcefully, ending the process will be delayed by this number of seconds.", "modes", Map( "simple", Map("formField", false) ) @@ -119,11 +116,11 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "ms", "min", 0, - "storageKey", this.configPrefix . "KillPostDelay", + "storageKey", "KillPostDelay", "default", 5, "required", true, "group", "advanced", - "description", "If killing a managed launcher forcefully, the launcher will wait this number of seconds after trying to end the process before reporting success.", + "description", "If killing a launch process forcefully, the launcher will wait this number of seconds after trying to end the process before reporting success.", "modes", Map( "simple", Map("formField", false) ) @@ -133,7 +130,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "PoliteCloseWait", + "storageKey", "PoliteCloseWait", "required", true, "default", 10, "group", "advanced", diff --git a/Lib/Launchpad/Entity/PlatformEntity.ahk b/launchpad_ahk/Lib/Launchpad/Entity/PlatformEntity.ahk similarity index 95% rename from Lib/Launchpad/Entity/PlatformEntity.ahk rename to launchpad_ahk/Lib/Launchpad/Entity/PlatformEntity.ahk index 43a4a1b8..77060a1a 100644 --- a/Lib/Launchpad/Entity/PlatformEntity.ahk +++ b/launchpad_ahk/Lib/Launchpad/Entity/PlatformEntity.ahk @@ -1,7 +1,5 @@ -class PlatformEntity extends AppEntityBase { +class PlatformEntity extends FieldableEntity { platformObj := "" - configPrefix := "" - dataSourcePath := "platforms" Platform { get => this.GetPlatform() @@ -136,8 +134,8 @@ class PlatformEntity extends AppEntityBase { } } - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) + AutoDetectValues() { + detectedValues := super.AutoDetectValues() detectedValues["IsInstalled"] := this.Platform.IsInstalled() detectedValues["InstalledVersion"] := this.Platform.GetInstalledVersion() detectedValues["InstallDir"] := this.Platform.GetInstallDir() diff --git a/Lib/Launchpad/GamePlatform/BasicPlatform.ahk b/launchpad_ahk/Lib/Launchpad/GamePlatform/BasicPlatform.ahk similarity index 100% rename from Lib/Launchpad/GamePlatform/BasicPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/GamePlatform/BasicPlatform.ahk diff --git a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk b/launchpad_ahk/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk similarity index 71% rename from Lib/Launchpad/GamePlatform/GamePlatformBase.ahk rename to launchpad_ahk/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk index d5b3f944..7c25316b 100644 --- a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk +++ b/launchpad_ahk/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk @@ -87,7 +87,7 @@ class GamePlatformBase { } NeedsUpdate() { - return this.app.Service("version_checker").VersionIsOutdated(this.GetLatestVersion(), this.GetInstalledVersion()) + return this.app["version_checker"].VersionIsOutdated(this.GetLatestVersion(), this.GetInstalledVersion()) } GetInstalledVersion() { @@ -138,7 +138,7 @@ class GamePlatformBase { return [] } - GetLauncherSpecificId(key) { + GetPlatformRef(key) { return key } @@ -164,8 +164,8 @@ class GamePlatformBase { locator := GameExeLocator(installDir) possibleExes := locator.Locate("") exeName := this.DetermineMainExe(key, possibleExes) - launcherSpecificId := this.GetLauncherSpecificId(key) - detectedGameObj := DetectedGame(key, this, this.launcherType, this.gameType, installDir, exeName, launcherSpecificId, possibleExes) + platformRef := this.GetPlatformRef(key) + detectedGameObj := DetectedGame(key, this, this.launcherType, this.gameType, installDir, exeName, platformRef, possibleExes) if (this.installDir) { detectedGameObj.launcherInstallDir := this["InstallDir"] @@ -179,35 +179,48 @@ class GamePlatformBase { } DetermineMainExe(key, possibleExes) { - dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() - dsData := this.GetDataSourceDefaults(dataSource, key) - mainExe := "" if (possibleExes.Length == 1) { mainExe := possibleExes[1] - } else if (possibleExes.Length > 1 && dsData.Has("GameExe")) { - for index, possibleExe in possibleExes { - SplitPath(possibleExe, &fileName) - - if (dsData["GameExe"] == fileName) { - mainExe := possibleExe - break + } else if (possibleExes.Length > 1) { + ; @todo move the API functionality into an event in an event in the WebServicesEventSubscriber + if (this.app.Services.Has("web_services.adapter_manager")) { + resultData := this.app["web_services.adapter_manager"].AdapterRequest( + Map("id", key), + Map( + "dataType", "entity_data", + "entityType", "launcher", + "tags", "defaults" + ), + "read", + true + ) + + for key, data in resultData { + if ( + data + && HasBase(data, Map.Prototype) + && data.Has("defaults") + && data["defaults"] + && data["defaults"].Has("GameExe") + && data["defaults"]["GameExe"] + ) { + for index, possibleExe in possibleExes { + SplitPath(possibleExe, &fileName) + + if (data["defaults"]["GameExe"] == fileName) { + mainExe := possibleExe + break 2 + } + } + } } } - } - return mainExe - } - - GetDataSourceDefaults(dataSource, key) { - defaults := Map() - dsData := dataSource.ReadJson(key, "Games") - - if (dsData != "" && dsData.Has("data") && dsData["data"].Has("defaults")) { - defaults := this.merger.Merge(dsData["data"]["defaults"], defaults) + } - return defaults + return mainExe } } diff --git a/Lib/Launchpad/GamePlatform/RegistryLookupGamePlatformBase.ahk b/launchpad_ahk/Lib/Launchpad/GamePlatform/RegistryLookupGamePlatformBase.ahk similarity index 100% rename from Lib/Launchpad/GamePlatform/RegistryLookupGamePlatformBase.ahk rename to launchpad_ahk/Lib/Launchpad/GamePlatform/RegistryLookupGamePlatformBase.ahk diff --git a/Lib/Launchpad/Gui/Dialog/AboutWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Dialog/AboutWindow.ahk similarity index 100% rename from Lib/Launchpad/Gui/Dialog/AboutWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Dialog/AboutWindow.ahk diff --git a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk similarity index 57% rename from Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk index 79b752ba..11299d17 100644 --- a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk @@ -3,26 +3,31 @@ defaults := super.GetDefaultConfig(container, config) defaults["title"] := "Account Info" defaults["buttons"] := "*&Save|&Cancel|&Logout" + defaults["webService"] := "" return defaults } Controls() { super.Controls() - if (this.app.Services.Has("Auth")) { - info := this.app.Service("Auth").GetStatusInfo() + if (this.app.Services.Has("entity_manager.web_service")) { + entityMgr := this.app.Services["entity_manager.web_service"] + + webService := this.config["webService"] + + if (!webService) { + throw AppException("Opened AccountInfoWindow without a webService specified.") + } + + info := webService.GetStatusInfo() if (info) { opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"]) - this.guiObj.AddText(opts, "Email: " . info["email"]) + this.guiObj.AddText(opts, "Account: " . info["account"]) } } - this.AddHeading("Player Name") - this.AddEdit("PlayerName", this.app.Config["player_name"], "", 250) - this.guiObj.AddText("w" . this.windowSettings["contentWidth"], "Note: Player name is stored locally and not synced with your online Launchpad account yet.") - position := "Wrap x" . this.margin . " y+" . this.margin options := position . " w" . this.windowSettings["contentWidth"] . " +0x200 c" . this.themeObj.GetColor("textLink") this.guiObj.AddLink(options, 'Manage your account at launchpad.games.') @@ -30,11 +35,17 @@ ProcessResult(result, submittedData := "") { if (result == "Logout") { - if (this.app.Services.Has("Auth")) { - this.app.Service("Auth").Logout() + webService := this.config["webService"] + + if (webService) { + webService.Logout() + } + } else if (result == "Login") { + webService := this.config["webService"] + + if (webService) { + webService.Login() } - } else if (result == "Save" && submittedData) { - this.app.Config["player_name"] := submittedData.PlayerName } return result diff --git a/Lib/Launchpad/Gui/Dialog/LauncherDeleteWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Dialog/LauncherDeleteWindow.ahk similarity index 100% rename from Lib/Launchpad/Gui/Dialog/LauncherDeleteWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Dialog/LauncherDeleteWindow.ahk diff --git a/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk similarity index 98% rename from Lib/Launchpad/Gui/Dialog/LoginWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk index 66b256e0..7edc0d12 100644 --- a/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk @@ -2,7 +2,6 @@ entityObj := "" entityManager := "" missingFields := Map() - dataSource := "" GetDefaultConfig(container, config) { defaults := super.GetDefaultConfig(container, config) diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk similarity index 63% rename from Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk index 04119844..3aee2ba9 100644 --- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk @@ -2,10 +2,7 @@ detectedGameObj := "" newValues := Map() missingFields := Map() - dataSource := "" knownGames := "" - launcherTypes := "" - gameTypes := "" __New(container, themeObj, config, detectedGameObj) { this.detectedGameObj := detectedGameObj @@ -22,11 +19,45 @@ Create() { super.Create() - this.dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() - this.knownPlatforms := this.dataSource.ReadListing("platforms") - this.knownGames := this.dataSource.ReadListing("game-keys") - this.launcherTypes := this.dataSource.ReadListing("launcher-types") - this.gameTypes := this.dataSource.ReadListing("game-types") + + this.knownPlatforms := [] + this.knownGames := [] + + ; @todo replace this, or at least refactor it to live somewhere else + if (this.container.Has("web_services.adapter_manager")) { + knownMap := Map( + "platform", "knownPlatforms", + "launcher", "knownGames", + ) + + for entityTypeId, varName in knownMap { + results := this.container["web_services.adapter_manager"].AdapterRequest("", Map( + "dataType", "entity_list", + "entityType", entityTypeId + ), "read", true) + + if (results) { + for , idList in results { + if (idList) { + for , id in idList { + exists := false + + for , item in %varName% { + if (item == id) { + exists := true + break + } + } + + if (!exists) { + this.%varName%.Push(id) + } + } + } + } + } + } + } } GetTitle() { @@ -35,12 +66,10 @@ Controls() { super.Controls() - this.Add("ComboBoxControl", "vId", "Id", this.detectedGameObj.Id, this.knownGames, "OnIdChange", "You can change the detected game key here, which will become the name of your launcher. Your existing launchers, and all launchers known about via the API, can be selected to match this game up with one of those items.") - this.Add("SelectControl", "vLauncherType", "Launcher Type", this.detectedGameObj.launcherType, this.launcherTypes, "OnLauncherTypeChange", "This tells " . this.app.appName . " how to interact with any launcher your game might require. If your game's launcher isn't listed, or your game doesn't have a launcher, start with `"Default`".`n`nYou can customize the details of the launcher type after it is added.") - this.Add("SelectControl", "vGameType", "Game Type", this.detectedGameObj.gameType, this.gameTypes, "OnGameTypeChange", "This tells " . this.app.appName . " how to launch your game. Most games can use 'default', but launchers can support different game types.`n`nYou can customize the details of the game type after it is added.") + this.Add("ComboBoxControl", "vId", "Id", this.detectedGameObj.key, this.knownGames, "OnIdChange", "You can change the detected game key here, which will become the name of your launcher. Your existing launchers, and all launchers known about via the API, can be selected to match this game up with one of those items.") this.Add("LocationBlock", "", "Install Dir", this.detectedGameObj.installDir, "InstallDir", "", true, "This is the directory that the game is installed in, if it could be detected.") this.Add("ComboBoxControl", "vExe", "Exe", this.detectedGameObj.exeName, this.detectedGameObj.possibleExeNames, "OnExeChange", "The main Exe, if detected, should be pre-selected. You may change it to be the name (or path) of another exe, or select another one of the detected .exe files from the list (if more than one was found).") - this.AddTextBlock("Launcher-Specific ID", "LauncherSpecificId", this.detectedGameObj.launcherSpecificId, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.") + this.AddTextBlock("Launcher-Specific ID", "PlatformRef", this.detectedGameObj.platformRef, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.") } AddTextBlock(heading, field, existingVal := "", helpText := "") { @@ -61,16 +90,6 @@ this.newValues["key"] := ctl.Text } - OnLauncherTypeChange(ctl, info) { - this.guiObj.Submit(false) - this.newValues["launcherType"] := ctl.Text - } - - OnGameTypeChange(ctl, info) { - this.guiObj.Submit(false) - this.newValues["gameType"] := ctl.Text - } - GetValue(key) { val := this.detectedGameObj.%key% @@ -117,9 +136,9 @@ this.newValues["exeName"] := ctl.Text } - OnLauncherSpecificIdChange(ctl, info) { + OnPlatformRefChange(ctl, info) { this.guiObj.Submit(false) - this.newValues["launcherSpecificId"] := ctl.Text + this.newValues["platformRef"] := ctl.Text } ProcessResult(result, submittedData := "") { diff --git a/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk similarity index 97% rename from Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk index 82c7b008..de956383 100644 --- a/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk @@ -29,7 +29,7 @@ GetLauncherConfig() { platformKey := Trim(this.guiObj["Platform"].Text) config := Map("Platform", platformKey) - platform := this.app.Service("entity_manager.platform")[platformKey] + platform := this.app["entity_manager.platform"][platformKey] if (platform) { config["LauncherType"] := platform.Platform.launcherType diff --git a/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk similarity index 75% rename from Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk index dccde664..78d55ef9 100644 --- a/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk @@ -1,10 +1,12 @@ class LauncherCreateFormBase extends FormGuiBase { knownGames := "" knownPlatforms := "" - dataSource := "" + launcherMgr := "" + platformMgr := "" __New(container, themeObj, config) { - this.dataSource := container.Get("manager.data_source").GetDefaultDataSource() + this.launcherMgr := container.Get("entity_manager.launcher") + this.platformMgr := container.Get("entity_manager.platform") super.__New(container, themeObj, config) } @@ -17,8 +19,8 @@ Create() { super.Create() - this.knownGames := this.dataSource.ReadListing("game-keys") - this.knownPlatforms := this.dataSource.ReadListing("platforms") + this.knownGames := this.launcherMgr.ListEntities(false, true) + this.knownPlatforms := this.platformMgr.ListEntities(false, true) } ProcessResult(result, submittedData := "") { diff --git a/Lib/Launchpad/Gui/Form/LauncherWizard.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Form/LauncherWizard.ahk similarity index 97% rename from Lib/Launchpad/Gui/Form/LauncherWizard.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Form/LauncherWizard.ahk index 1f1889b7..97bcd11a 100644 --- a/Lib/Launchpad/Gui/Form/LauncherWizard.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Form/LauncherWizard.ahk @@ -25,7 +25,7 @@ GetLauncherConfig() { platformKey := Trim(this.guiObj["Platform"].Text) config := Map("Platform", platformKey, "GameInstallDir", this.installDir, "GameExe", this.exe) - platform := this.app.Service("entity_manager.platform")[platformKey] + platform := this.app["entity_manager.platform"][platformKey] if (platform) { config["LauncherType"] := platform.Platform.launcherType diff --git a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Form/SettingsWindow.ahk similarity index 87% rename from Lib/Launchpad/Gui/Form/SettingsWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Form/SettingsWindow.ahk index e6482347..ecf2c0cd 100644 --- a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Form/SettingsWindow.ahk @@ -122,14 +122,6 @@ ctl := this.guiObj.AddDDL("vlogging_level xs y+m Choose" . chosen . " w200 c" . this.themeObj.GetColor("editText"), this.container.Get("logger").GetLogLevels()) ctl.OnEvent("Change", "OnLoggingLevelChange") - this.AddConfigLocationBlock("API Endpoint", "api_endpoint") - - this.AddHeading("API Settings") - ctl := this.AddConfigCheckBox("Enable API login for enhanced functionality", "api_authentication") - ctl.ctl.NeedsRestart := true - ctl := this.AddConfigCheckBox("Automatically initiate API login when needed", "api_auto_login") - ctl.ctl.NeedsRestart := true - tabs.UseTab() closeW := 100 @@ -137,11 +129,11 @@ } OnManageBackups(btn, info) { - this.app.Service("entity_type.backup").OpenManageWindow() + this.app["entity_type.backup"].OpenManageWindow() } OnManagePlatforms(btn, info) { - this.app.Service("entity_type.platform").OpenManageWindow() + this.app["entity_type.platform"].OpenManageWindow() } AddConfigLocationBlock(heading, settingName, extraButton := "", helpText := "") { @@ -194,7 +186,7 @@ } else if (btn == "OpenLauncherFile") { this.app.Config.OpenLauncherFile() } else if (btn == "ReloadLauncherFile") { - this.app.Service("entity_manager.launcher").LoadComponents(true) + this.app["entity_manager.launcher"].LoadComponents(true) } } @@ -205,7 +197,7 @@ } else if (btn == "OpenBackupsFile") { this.app.Config.OpenBackupsFile() } else if (btn == "ReloadBackupsFile") { - this.app.Service("entity_manager.backup").LoadComponents(true) + this.app["entity_manager.backup"].LoadComponents(true) } } @@ -216,7 +208,7 @@ } else if (btn == "OpenPlatformsFile") { this.app.Config.OpenPlatformsFile() } else if (btn == "ReloadPlatformsFile") { - this.app.Service("entity_manager.platform").LoadComponents(true) + this.app["entity_manager.platform"].LoadComponents(true) } } @@ -246,34 +238,24 @@ } } - OnApiEndpointMenuClick(btn) { - if (btn == "ChangeApiEndpoint") { - this.app.Service("manager.data_source").GetDefaultDataSource().ChangeApiEndpoint("", "") - this.SetText("ApiEndpoint", this.app.Config["api_endpoint"], "Bold") - this.needsRestart := true - } else if (btn == "OpenApiEndpoint") { - this.app.Service("manager.data_source").GetDefaultDataSource().Open() - } - } - OnCacheDirMenuClick(btn) { if (btn == "ChangeCacheDir") { - this.app.Service("manager.cache").ChangeCacheDir() + this.app["manager.cache"].ChangeCacheDir() this.SetText("CacheDir", this.app.Config["cache_dir"], "Bold") } else if (btn == "OpenCacheDir") { - this.app.Service("manager.cache").OpenCacheDir() + this.app["manager.cache"].OpenCacheDir() } else if (btn == "FlushCacheDir") { - this.app.Service("manager.cache").FlushCaches(true, true) + this.app["manager.cache"].FlushCaches(true, true) } } OnBackupDirMenuClick(btn) { if (btn == "ChangeBackupDir") { - this.app.Service("entity_manager.backup").ChangeBackupDir() + this.app["entity_manager.backup"].ChangeBackupDir() this.SetText("BackupDir", this.app.Config["backup_dir"], "Bold") this.needsRestart := true } else if (btn == "OpenBackupDir") { - this.app.Service("entity_manager.backup").OpenBackupDir() + this.app["entity_manager.backup"].OpenBackupDir() } } @@ -318,7 +300,7 @@ this.app.Config.SaveConfig() if (this.needsRestart) { - response := this.app.Service("manager.gui").Dialog(Map( + response := this.app["manager.gui"].Dialog(Map( "title", "Restart " . this.app.appName . "?", "text", "One or more settings that have been changed require restarting " . this.app.appName . " to fully take effect.`n`nWould you like to restart " . this.app.appName . " now?" )) @@ -328,8 +310,8 @@ } } - if (this.app.Service("manager.gui").Has("MainWindow")) { - this.app.Service("manager.gui")["MainWindow"].UpdateListView() + if (this.app["manager.gui"].Has("MainWindow")) { + this.app["manager.gui"]["MainWindow"].UpdateListView() } return result diff --git a/Lib/Launchpad/Gui/Form/SetupWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/Form/SetupWindow.ahk similarity index 93% rename from Lib/Launchpad/Gui/Form/SetupWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/Form/SetupWindow.ahk index 1ebd5784..0be2ca76 100644 --- a/Lib/Launchpad/Gui/Form/SetupWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/Form/SetupWindow.ahk @@ -16,7 +16,7 @@ Create() { super.Create() - this.availableThemes := this.app.Service("manager.theme").GetAvailableThemes() + this.availableThemes := this.app["manager.theme"].GetAvailableThemes() } AddDescription(text) { @@ -58,7 +58,7 @@ } GetInstalledPlatforms() { - platformMgr := this.app.Service("entity_manager.platform") + platformMgr := this.app["entity_manager.platform"] platformQuery := platformMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) .Condition(IsTrueCondition(), "IsInstalled") return platformQuery.Execute() @@ -80,7 +80,7 @@ this.guiObj.Submit(false) len := StrLen("PlatformToggle") name := SubStr(chk.Name, len + 1) - platformMgr := this.app.Service("entity_manager.platform") + platformMgr := this.app["entity_manager.platform"] if (platformMgr.Has(name)) { platform := platformMgr[name] @@ -115,8 +115,8 @@ ProcessResult(result, submittedData := "") { if (result == "Start") { - this.app.Service("config.app").SaveConfig() - this.app.Service("entity_manager.platform").SaveModifiedEntities() + this.app["config.app"].SaveConfig() + this.app["entity_manager.platform"].SaveModifiedEntities() if (submittedData.DetectGames) { result := "Detect" diff --git a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk similarity index 95% rename from Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk index e78ab041..6f8aa4ba 100644 --- a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk @@ -12,9 +12,7 @@ this.detectedGames := detectedGames this.state := container.Get("state.app") this.launcherManager := container.Get("entity_manager.launcher") - this.knownGames := container.Get("manager.data_source") - .GetDefaultDataSource() - .ReadListing("game-keys") + this.knownGames := this.launcherManager.ListEntities(false, true) super.__New(container, themeObj, config) } @@ -83,7 +81,7 @@ GetListViewImgList(lv, large := false) { IL := IL_Create(this.detectedGames.Count, 1, large) - defaultIcon := this.themeObj.GetIconPath("Game") + defaultIcon := this.themeObj.GetIconPath("game") iconNum := 1 for key, detectedGameObj in this.detectedGames { @@ -200,7 +198,7 @@ detectedGameObj := this.detectedGames[key] - result := this.app.Service("manager.gui").Dialog(Map( + result := this.app["manager.gui"].Dialog(Map( "type", "DetectedGameEditor", "ownerOrParent", this.guiId, "child", true @@ -233,7 +231,7 @@ menuItems := [] menuItems.Push(Map("label", "Edit", "name", "EditDetectedGame")) - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EditDetectedGame") { this.EditDetectedGame(key) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk similarity index 76% rename from Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 25dfa92e..3f897337 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -3,6 +3,7 @@ launcherManager := "" platformManager := "" showDetailsPane := true + lvResizeOpts := "h" __New(container, themeObj, config) { this.launcherManager := container.Get("entity_manager.launcher") @@ -17,7 +18,7 @@ defaults["child"] := false defaults["title"] := container.GetApp().appName defaults["titleIsMenu"] := true - defaults["showStatusIndicator"] := !!(container.Get("config.app").Has("api_authentication") && container.Get("config.app")["api_authentication"]) + defaults["showStatusIndicator"] := container.Has("entity_manager.web_service") return defaults } @@ -90,144 +91,12 @@ } } - _getToolsMenuEntityTypes() { - entityTypes := Map() - - for key, entityType in this.container["manager.entity_type"] { - if (entityType.definition["manager_link_in_tools_menu"]) { - entityTypes[key] := entityType - } - } - - return entityTypes - } - ShowTitleMenu() { - menuEntityTypes := this._getToolsMenuEntityTypes() - toolsItems := [] - - for key, entityType in menuEntityTypes { - menuLinkText := entityType.definition["manager_menu_link_text"] - - if (!menuLinkText) { - menuLinkText := "&" . entityType.definition["name_plural"] - } - - toolsItems.Push(Map("label", menuLinkText, "name", "manage_" . key)) - } - - toolsItems.Push(Map("label", "&Modules", "name", "ManageModules")) - toolsItems.Push(Map("label", "&Flush Cache", "name", "FlushCache")) - - launchersItems := [] - launchersItems.Push(Map("label", "&Clean Launchers", "name", "CleanLaunchers")) - launchersItems.Push(Map("label", "&Reload Launchers", "name", "ReloadLaunchers")) - - aboutItems := [] - aboutItems.Push(Map("label", "&About Launchpad", "name", "About")) - aboutItems.Push(Map("label", "&Open Website", "name", "OpenWebsite")) - - menuItems := [] - menuItems.Push(Map("label", "&Tools", "name", "ToolsMenu", "childItems", toolsItems)) - menuItems.Push(Map("label", "&Launchers", "name", "LaunchersMenu", "childItems", launchersItems)) - menuItems.Push("") - menuItems.Push(Map("label", "&About", "name", "About", "childItems", aboutItems)) - menuItems.Push("") - menuItems.Push(Map("label", "&Settings", "name", "Settings")) - menuItems.Push(Map("label", "Check for &Updates", "name", "CheckForUpdates")) - menuItems.Push("") - menuItems.Push(Map("label", "Provide &Feedback", "name", "ProvideFeedback")) - menuItems.Push("") - menuItems.Push(Map("label", "&Restart", "name", "Reload")) - menuItems.Push(Map("label", "E&xit", "name", "Exit")) - - result := this.container["manager.gui"].Menu(menuItems, this, this.guiObj["WindowTitleText"]) - - if (result == "ManageModules") { - this.container["manager.gui"].OpenWindow("ManageModulesWindow") - } else if (result == "FlushCache") { - this.container["manager.cache"].FlushCaches(true, true) - } else if (result == "CleanLaunchers") { - this.container["manager.builder"].CleanLaunchers() - } else if (result == "ReloadLaunchers") { - this.launcherManager.LoadComponents(true) - this.UpdateListView() - } else if (result == "About") { - this.container["manager.gui"].Dialog(Map("type", "AboutWindow")) - } else if (result == "OpenWebsite") { - this.app.OpenWebsite() - } else if (result == "ProvideFeedback") { - this.app.ProvideFeedback() - } else if (result == "Settings") { - this.container["manager.gui"].Dialog(Map("type", "SettingsWindow", "unique", false)) - } else if (result == "CheckForUpdates") { - this.app.CheckForUpdates() - } else if (result == "Reload") { - this.app.restartApp() - } else if (result == "Exit") { - this.app.ExitApp() - } else { - for key, entityType in menuEntityTypes { - if (result == "manage_" . key) { - this.container["entity_type." . key].OpenManageWindow() - break - } - } - } - } - - GetStatusInfo() { - info := "" - - if (this.container.Has("Auth")) { - info := this.container["Auth"].GetStatusInfo() - } - - return info - } - - OnStatusIndicatorClick(btn, info) { - menuItems := [] - - if (this.container.Has("Auth")) { - if (this.container["Auth"].IsAuthenticated()) { - menuItems.Push(Map("label", "Account Details", "name", "AccountDetails")) - menuItems.Push(Map("label", "Logout", "name", "Logout")) - } else { - menuItems.Push(Map("label", "Login", "name", "Login")) - } - } - - result := this.container["manager.gui"].Menu(menuItems, this, btn) - - if (result == "AccountDetails") { - accountResult := this.container["manager.gui"].Dialog(Map( - "type", "AccountInfoWindow", - "ownerOrParent", this.guiId, - "child", true - )) - - if (accountResult == "OK") { - this.UpdateStatusIndicator() - } - } else if (result == "Logout") { - if (this.container.Has("Auth")) { - this.container["Auth"].Logout() - } - } else if (result == "Login") { - if (this.container.Has("Auth")) { - this.container["Auth"].Login() - } - } - } - - StatusWindowIsOnline() { - isOnline := false - - if (this.container.Has("Auth")) { - isOnline := this.container["Auth"].IsAuthenticated() - } - return isOnline + this.app.MainMenu( + this, + this.guiObj["WindowTitleText"], + false + ) } FormatDate(timestamp) { @@ -262,7 +131,9 @@ } status := launcher.GetStatus() - apiStatus := launcher["DataSourceItemKey"] ? "Linked" : "Not linked" + + ; @todo Move the API data to an event in the LaunchpadApi module + apiStatus := (launcher.HasField["web_service_launchpad_api_ref"] && launcher["web_service_launchpad_api_ref"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo("Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo("Build")["Timestamp"]) @@ -438,7 +309,9 @@ } status := launcher.GetStatus() - apiStatus := launcher["DataSourceItemKey"] ? "Linked" : "Not linked" + + ; @todo Move the API code to the LaunchpadApi module + apiStatus := (launcher.HasField("web_service_launchpad_api_ref") && launcher["web_service_launchpad_api_ref"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo(key, "Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo(key, "Build")["Timestamp"]) @@ -513,7 +386,7 @@ GetListViewImgList(lv, large := false) { IL := IL_Create(this.launcherManager.Count(true), 1, large) - defaultIcon := this.themeObj.GetIconPath("Game") + defaultIcon := this.themeObj.GetIconPath("game") iconNum := 1 for key, launcher in this.launcherManager { @@ -533,7 +406,7 @@ GetItemImage(launcher) { iconSrc := launcher["IconSrc"] assetIcon := launcher["AssetsDir"] . "\" . launcher.Id . ".ico" - defaultIcon := this.themeObj.GetIconPath("Game") + defaultIcon := this.themeObj.GetIconPath("game") if ((!iconSrc || !FileExist(iconSrc)) && FileExist(assetIcon)) { iconSrc := assetIcon @@ -581,7 +454,7 @@ } entity.SaveEntity() - entity.UpdateDataSourceDefaults() + entity.UpdateDefaults() this.UpdateListView() } } diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk similarity index 96% rename from Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk index a73baba9..be595c3e 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk @@ -40,7 +40,7 @@ class ManageBackupsWindow extends ManageWindowBase { GetListViewImgList(lv, large := false) { IL := IL_Create(this.backupManager.Count(true), 1, large) - defaultIcon := this.themeObj.GetIconPath("Backup") + defaultIcon := this.themeObj.GetIconPath("backup") iconNum := 1 for key, backup in this.backupManager { @@ -109,7 +109,7 @@ class ManageBackupsWindow extends ManageWindowBase { menuItems.Push(Map("label", "Restore", "name", "RestoreBackup")) menuItems.Push(Map("label", "Delete", "name", "DeleteBackup")) - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EditBackup") { this.EditBackup(key) diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk similarity index 90% rename from Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk index bc961de2..7597c34b 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk @@ -1,5 +1,5 @@ class ManageModulesWindow extends ManageWindowBase { - listViewColumns := Array("NAME", "ENABLED", "SOURCE", "VERSION") + listViewColumns := Array("NAME", "CATEGORY", "ENABLED", "SOURCE", "VERSION") moduleManager := "" needsRestart := false @@ -26,9 +26,8 @@ class ManageModulesWindow extends ManageWindowBase { for name, module in this.moduleManager.All("", false, true) { enabledText := module.IsEnabled() ? "Yes" : "No" - ; TODO Define source - source := "" - data[name] := [name, enabledText, source, module.GetVersion()] + source := module.IsCore() ? "Built-in" : "Third-party" + data[name] := [name, module.moduleInfo["category"], enabledText, source, module.GetVersion()] } return data @@ -44,7 +43,7 @@ class ManageModulesWindow extends ManageWindowBase { GetListViewImgList(lv, large := false) { IL := IL_Create(this.lvCount, 1, large) - defaultIcon := this.themeObj.GetIconPath("Module") + defaultIcon := this.themeObj.GetIconPath("module") iconNum := 1 for key, module in this.moduleManager.All("", false, true) { @@ -85,7 +84,7 @@ class ManageModulesWindow extends ManageWindowBase { this.Submit(false) } - response := this.app.Service("manager.gui").Dialog(Map( + response := this.app["manager.gui"].Dialog(Map( "title", "Restart " . this.app.appName . "?", "text", "One or more module changes require restarting " . this.app.appName . " to fully take effect.`n`nWould you like to restart " . this.app.appName . " now?" )) @@ -154,7 +153,7 @@ class ManageModulesWindow extends ManageWindowBase { menuItems.Push(Map("label", "Delete", "name", "DeleteModule")) } - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EnableModule") { this.EnableModule(key) diff --git a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk similarity index 81% rename from Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk rename to launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk index 51a526af..b3b54fe7 100644 --- a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk +++ b/launchpad_ahk/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk @@ -4,7 +4,7 @@ class PlatformsWindow extends ManageWindowBase { __New(container, themeObj, config) { this.platformManager := container.Get("entity_manager.platform") - this.lvCount := this.platformManager.Count(true) + this.lvCount := this.platformManager.Count(true) - 1 super.__New(container, themeObj, config) } @@ -24,10 +24,12 @@ class PlatformsWindow extends ManageWindowBase { data := Map() for key, platform in this.platformManager { - enabledText := platform["IsEnabled"] ? "Yes" : "No" - detectGamesText := platform["DetectGames"] ? "Yes" : "No" - installedText := platform["IsInstalled"] ? "Yes" : "No" - data[key] := [platform.GetName(), enabledText, detectGamesText, installedText, platform["InstalledVersion"]] + if (key != "Basic") { + enabledText := platform["IsEnabled"] ? "Yes" : "No" + detectGamesText := platform["DetectGames"] ? "Yes" : "No" + installedText := platform["IsInstalled"] ? "Yes" : "No" + data[key] := [platform.GetName(), enabledText, detectGamesText, installedText, platform["InstalledVersion"]] + } } return data @@ -43,18 +45,21 @@ class PlatformsWindow extends ManageWindowBase { GetListViewImgList(lv, large := false) { IL := IL_Create(this.platformManager.Count(true), 1, large) - defaultIcon := this.themeObj.GetIconPath("Platform") + defaultIcon := this.themeObj.GetIconPath("platform") iconNum := 1 for key, platform in this.platformManager { - iconSrc := platform["IconSrc"] - if (!iconSrc or !FileExist(iconSrc)) { - iconSrc := defaultIcon - } + if (key != "Basic") { + iconSrc := platform["IconSrc"] - IL_Add(IL, iconSrc) - iconNum++ + if (!iconSrc or !FileExist(iconSrc)) { + iconSrc := defaultIcon + } + + IL_Add(IL, iconSrc) + iconNum++ + } } return IL @@ -110,7 +115,7 @@ class PlatformsWindow extends ManageWindowBase { menuItems.Push(Map("label", "Install", "name", "InstallPlatform")) } - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EditPlatform") { this.EditPlatform(key) diff --git a/Lib/Launchpad/Includes.ahk b/launchpad_ahk/Lib/Launchpad/Includes.ahk similarity index 94% rename from Lib/Launchpad/Includes.ahk rename to launchpad_ahk/Lib/Launchpad/Includes.ahk index cfacabc5..cd69a6ec 100644 --- a/Lib/Launchpad/Includes.ahk +++ b/launchpad_ahk/Lib/Launchpad/Includes.ahk @@ -21,12 +21,11 @@ #Include Config\LaunchpadConfig.ahk #Include Config\PlatformsConfig.ahk #Include ConfigMigrator\LaunchpadIniMigrator.ahk -#Include DataSource\LocalDataSource.ahk #Include DetectedGame\DetectedGame.ahk +#Include Entity\GameProcessEntity.ahk #Include Entity\LauncherEntity.ahk -#Include Entity\ManagedEntityBase.ahk -#Include Entity\ManagedGameEntity.ahk -#Include Entity\ManagedLauncherEntity.ahk +#Include Entity\LauncherProcessEntity.ahk +#Include Entity\LaunchProcessEntity.ahk #Include Entity\PlatformEntity.ahk #Include GamePlatform\BasicPlatform.ahk #Include GamePlatform\GamePlatformBase.ahk diff --git a/Lib/Launchpad/Includes.test.ahk b/launchpad_ahk/Lib/Launchpad/Includes.test.ahk similarity index 100% rename from Lib/Launchpad/Includes.test.ahk rename to launchpad_ahk/Lib/Launchpad/Includes.test.ahk diff --git a/Lib/Launchpad/Installer/DependencyInstaller.ahk b/launchpad_ahk/Lib/Launchpad/Installer/DependencyInstaller.ahk similarity index 100% rename from Lib/Launchpad/Installer/DependencyInstaller.ahk rename to launchpad_ahk/Lib/Launchpad/Installer/DependencyInstaller.ahk diff --git a/Lib/Launchpad/Installer/LaunchpadUpdate.ahk b/launchpad_ahk/Lib/Launchpad/Installer/LaunchpadUpdate.ahk similarity index 100% rename from Lib/Launchpad/Installer/LaunchpadUpdate.ahk rename to launchpad_ahk/Lib/Launchpad/Installer/LaunchpadUpdate.ahk diff --git a/Lib/Launchpad/Launchpad.library.json b/launchpad_ahk/Lib/Launchpad/Launchpad.library.json similarity index 100% rename from Lib/Launchpad/Launchpad.library.json rename to launchpad_ahk/Lib/Launchpad/Launchpad.library.json diff --git a/Lib/Launchpad/Locator/GameExeLocator.ahk b/launchpad_ahk/Lib/Launchpad/Locator/GameExeLocator.ahk similarity index 100% rename from Lib/Launchpad/Locator/GameExeLocator.ahk rename to launchpad_ahk/Lib/Launchpad/Locator/GameExeLocator.ahk diff --git a/Lib/Launchpad/Modules/Bethesda/Bethesda.module.json b/launchpad_ahk/Lib/Launchpad/Modules/Bethesda/Bethesda.module.json similarity index 100% rename from Lib/Launchpad/Modules/Bethesda/Bethesda.module.json rename to launchpad_ahk/Lib/Launchpad/Modules/Bethesda/Bethesda.module.json diff --git a/Lib/Launchpad/Modules/Bethesda/GamePlatform/BethesdaPlatform.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Bethesda/GamePlatform/BethesdaPlatform.ahk similarity index 100% rename from Lib/Launchpad/Modules/Bethesda/GamePlatform/BethesdaPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Bethesda/GamePlatform/BethesdaPlatform.ahk diff --git a/Lib/Launchpad/Modules/Blizzard/Blizzard.module.json b/launchpad_ahk/Lib/Launchpad/Modules/Blizzard/Blizzard.module.json similarity index 100% rename from Lib/Launchpad/Modules/Blizzard/Blizzard.module.json rename to launchpad_ahk/Lib/Launchpad/Modules/Blizzard/Blizzard.module.json diff --git a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk similarity index 80% rename from Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk index 5d326086..097d4a3e 100644 --- a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk +++ b/launchpad_ahk/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk @@ -22,22 +22,22 @@ class BlizzardPlatform extends RegistryLookupGamePlatformBase { productInstalls := [] if (this.app.Services.Has("BlizzardProductDb")) { - productInstalls := this.app.Service("BlizzardProductDb").GetProductInstalls() + productInstalls := this.app["BlizzardProductDb"].GetProductInstalls() } games := [] for index, productData in productInstalls { - launcherSpecificId := productData["productCode"] + platformRef := productData["productCode"] - if (launcherSpecificId != "agent" && launcherSpecificId != "bna" && productData.Has("settings") && productData["settings"].Has("installPath")) { + if (platformRef != "agent" && platformRef != "bna" && productData.Has("settings") && productData["settings"].Has("installPath")) { installPath := productData["settings"]["installPath"] installPath := StrReplace(installPath, "/", "\") SplitPath(installPath, &key) locator := GameExeLocator(installPath) possibleExes := locator.Locate("") mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installPath, mainExe, launcherSpecificId, possibleExes)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installPath, mainExe, platformRef, possibleExes)) } } diff --git a/Lib/Launchpad/Modules/Blizzard/StructuredData/BlizzardProductDb.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Blizzard/StructuredData/BlizzardProductDb.ahk similarity index 100% rename from Lib/Launchpad/Modules/Blizzard/StructuredData/BlizzardProductDb.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Blizzard/StructuredData/BlizzardProductDb.ahk diff --git a/Lib/Launchpad/Modules/Epic/Epic.module.json b/launchpad_ahk/Lib/Launchpad/Modules/Epic/Epic.module.json similarity index 100% rename from Lib/Launchpad/Modules/Epic/Epic.module.json rename to launchpad_ahk/Lib/Launchpad/Modules/Epic/Epic.module.json diff --git a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk similarity index 66% rename from Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk index 06e768d5..1cdb2845 100644 --- a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk +++ b/launchpad_ahk/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk @@ -3,12 +3,12 @@ class EpicPlatform extends RegistryLookupGamePlatformBase { displayName := "Epic Store" launcherType := "Epic" gameType := "Epic" - installDirRegView := 32 - installDirRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}" - versionRegView := 32 - versionRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}" - uninstallCmdRegView := 32 - uninstallCmdRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}" + installDirRegView := 64 + installDirRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties" + versionRegView := 64 + versionRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties" + uninstallCmdRegView := 64 + uninstallCmdRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties" Install() { Run("https://www.epicgames.com/store/en-US/download") @@ -41,15 +41,29 @@ class EpicPlatform extends RegistryLookupGamePlatformBase { } if (isGame) { - key := obj["Name"] + key := obj.Has("Name") ? obj["Name"] : "" + + if (!key && obj.Has("DisplayName")) { + key := obj["DisplayName"] + } + + if (!key && obj.Has("MandatoryAppFolderName")) { + key := obj["MandatoryAppFolderName"] + } + + if (!key) { + throw AppException("Could not determine detected game key.") + } + + displayName := obj.Has("DisplayName") ? obj["DisplayName"] : "" installDir := obj["InstallLocation"] - launcherSpecificId := obj["AppName"] + platformRef := obj["AppName"] ;exeName := obj["LaunchExecutable"] ;possibleExes := [obj["LaunchExecutable"]] locator := GameExeLocator(installDir) possibleExes := locator.Locate("") mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, platformRef, possibleExes, displayName)) } } } diff --git a/Lib/Launchpad/Modules/Origin/GamePlatform/OriginPlatform.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Origin/GamePlatform/OriginPlatform.ahk similarity index 100% rename from Lib/Launchpad/Modules/Origin/GamePlatform/OriginPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Origin/GamePlatform/OriginPlatform.ahk diff --git a/Lib/Launchpad/Modules/Origin/Origin.module.json b/launchpad_ahk/Lib/Launchpad/Modules/Origin/Origin.module.json similarity index 100% rename from Lib/Launchpad/Modules/Origin/Origin.module.json rename to launchpad_ahk/Lib/Launchpad/Modules/Origin/Origin.module.json diff --git a/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk similarity index 98% rename from Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk index ef37ab9e..091b1bf5 100644 --- a/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk +++ b/launchpad_ahk/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk @@ -66,7 +66,7 @@ class RiotPlatform extends RegistryLookupGamePlatformBase { return dirs } - GetLauncherSpecificId(key) { + GetPlatformRef(key) { if (key == "VALORANT") { key := "valorant" } else if (key == "Legends of Runeterra") { diff --git a/Lib/Launchpad/Modules/Riot/Riot.module.json b/launchpad_ahk/Lib/Launchpad/Modules/Riot/Riot.module.json similarity index 100% rename from Lib/Launchpad/Modules/Riot/Riot.module.json rename to launchpad_ahk/Lib/Launchpad/Modules/Riot/Riot.module.json diff --git a/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk b/launchpad_ahk/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk similarity index 95% rename from Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk rename to launchpad_ahk/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk index 3d88dfad..644f96ff 100644 --- a/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk +++ b/launchpad_ahk/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk @@ -64,7 +64,7 @@ class SteamPlatform extends RegistryLookupGamePlatformBase { if (IsObject(obj) && obj.Has("AppState")) { gameState := obj["AppState"] - launcherSpecificId := gameState["appid"] + platformRef := gameState["appid"] key := gameState["name"] installDir := dir . "\common\" . gameState["installdir"] installDir := StrReplace(installDir, "/", "\") @@ -76,7 +76,7 @@ class SteamPlatform extends RegistryLookupGamePlatformBase { } mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, platformRef, possibleExes)) } } } diff --git a/Lib/Launchpad/Modules/Steam/Steam.module.json b/launchpad_ahk/Lib/Launchpad/Modules/Steam/Steam.module.json similarity index 100% rename from Lib/Launchpad/Modules/Steam/Steam.module.json rename to launchpad_ahk/Lib/Launchpad/Modules/Steam/Steam.module.json diff --git a/Lib/Launchpad/Service/BuilderManager.ahk b/launchpad_ahk/Lib/Launchpad/Service/BuilderManager.ahk similarity index 100% rename from Lib/Launchpad/Service/BuilderManager.ahk rename to launchpad_ahk/Lib/Launchpad/Service/BuilderManager.ahk diff --git a/Lib/Launchpad/Service/PlatformManager.ahk b/launchpad_ahk/Lib/Launchpad/Service/PlatformManager.ahk similarity index 100% rename from Lib/Launchpad/Service/PlatformManager.ahk rename to launchpad_ahk/Lib/Launchpad/Service/PlatformManager.ahk diff --git a/Lib/Launchpad/State/LaunchpadAppState.ahk b/launchpad_ahk/Lib/Launchpad/State/LaunchpadAppState.ahk similarity index 100% rename from Lib/Launchpad/State/LaunchpadAppState.ahk rename to launchpad_ahk/Lib/Launchpad/State/LaunchpadAppState.ahk diff --git a/Lib/Launchpad/State/LaunchpadAppState.test.ahk b/launchpad_ahk/Lib/Launchpad/State/LaunchpadAppState.test.ahk similarity index 100% rename from Lib/Launchpad/State/LaunchpadAppState.test.ahk rename to launchpad_ahk/Lib/Launchpad/State/LaunchpadAppState.test.ahk diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk similarity index 84% rename from Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 7397b273..6dffed31 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/launchpad_ahk/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -2,12 +2,9 @@ class LaunchpadBuilder extends AppBase { GetParameterDefinitions(config) { parameters := super.GetParameterDefinitions(config) parameters["config_path"] := this.appDir . "\Launchpad.build.json" - parameters["config.api_endpoint"] := "https://api.launchpad.games/v1" - parameters["config.data_source_key"] := "" - parameters["config.api_authentication"] := true parameters["config.dist_dir"] := this.appDir . "\Dist" parameters["config.build_dir"] := this.appDir . "\Build" - parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\Launchpad.ico" + parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\launchpad.ico" parameters["config.github_username"] := "" parameters["config.github_token"] := "" parameters["config.github_repo"] := "VolantisDev/Launchpad" @@ -33,11 +30,6 @@ class LaunchpadBuilder extends AppBase { "arguments", [AppRef(), this.appDir . "\" . this.appName . ".ini"] ) - services["manager.data_source"] := Map( - "class", "DataSourceManager", - "arguments", [ContainerRef(), ServiceRef("manager.event"), ServiceRef("notifier"), ParameterRef("config.data_source_key")] - ) - services["FileHasher"] := "FileHasher" services["GitTagVersionIdentifier"] := Map( @@ -50,8 +42,8 @@ class LaunchpadBuilder extends AppBase { RunApp(config) { super.RunApp(config) - version := this.Service("GitTagVersionIdentifier").IdentifyVersion() - buildInfo := this.Service("manager.gui").Dialog(Map( + version := this["GitTagVersionIdentifier"].IdentifyVersion() + buildInfo := this["manager.gui"].Dialog(Map( "type", "BuildSettingsForm", "version", version )) @@ -60,8 +52,12 @@ class LaunchpadBuilder extends AppBase { this.ExitApp() } - if (buildInfo.DeployToApi && this.Services.Has("Auth")) { - this.Service("Auth").Login() + if (buildInfo.DeployToApi && this.Services.Has("entity_manager.web_service")) { + entityMgr := this.Services["entity_manager.web_service"] + + if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { + entityMgr["launchpad_api"].Login() + } } version := buildInfo.Version @@ -80,7 +76,7 @@ class LaunchpadBuilder extends AppBase { } if (buildInfo.DeployToGitHub || buildInfo.DeployToApi || buildInfo.DeployToChocolatey) { - releaseInfo := this.Service("manager.gui").Dialog(Map("type", "ReleaseInfoForm")) + releaseInfo := this["manager.gui"].Dialog(Map("type", "ReleaseInfoForm")) if (!releaseInfo) { this.ExitApp() @@ -145,7 +141,7 @@ class LaunchpadBuilder extends AppBase { if (!this.GetCmdOutput("git show-ref " . version)) { RunWait("git tag " . version, this.appDir) - response := this.Service("manager.gui").Dialog(Map( + response := this["manager.gui"].Dialog(Map( "title", "Push git tag?", "text", "Would you like to push the git tag that was just created (" . version . ") to origin?" )) diff --git a/Lib/LaunchpadBuilder/AppBuilder/AhkBinsBuilder.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/AhkBinsBuilder.ahk similarity index 100% rename from Lib/LaunchpadBuilder/AppBuilder/AhkBinsBuilder.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/AhkBinsBuilder.ahk diff --git a/Lib/LaunchpadBuilder/AppBuilder/AhkExeBuilder.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/AhkExeBuilder.ahk similarity index 100% rename from Lib/LaunchpadBuilder/AppBuilder/AhkExeBuilder.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/AhkExeBuilder.ahk diff --git a/Lib/LaunchpadBuilder/AppBuilder/AppBuilderBase.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/AppBuilderBase.ahk similarity index 100% rename from Lib/LaunchpadBuilder/AppBuilder/AppBuilderBase.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/AppBuilderBase.ahk diff --git a/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk similarity index 94% rename from Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk index 8ea2644b..a855d436 100644 --- a/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk +++ b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk @@ -9,7 +9,7 @@ class ChocoPkgBuilder extends AppBuilderBase { throw AppException("Installer file doesn't exist, cannot build chocolatey package.") } - hash := this.app.Service("FileHasher").Hash(installer, FileHasher.HASH_TYPE_SHA256) + hash := this.app["FileHasher"].Hash(installer, FileHasher.HASH_TYPE_SHA256) if (!hash) { throw AppException("Failed to create an SHA256 hash of the installer file.") diff --git a/Lib/LaunchpadBuilder/AppBuilder/LaunchpadOverlayBuilder.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/LaunchpadOverlayBuilder.ahk similarity index 100% rename from Lib/LaunchpadBuilder/AppBuilder/LaunchpadOverlayBuilder.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/LaunchpadOverlayBuilder.ahk diff --git a/Lib/LaunchpadBuilder/AppBuilder/NsisInstallerBuilder.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/NsisInstallerBuilder.ahk similarity index 100% rename from Lib/LaunchpadBuilder/AppBuilder/NsisInstallerBuilder.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/AppBuilder/NsisInstallerBuilder.ahk diff --git a/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk similarity index 89% rename from Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk index 330209b9..5173dc95 100644 --- a/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk +++ b/launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk @@ -2,7 +2,7 @@ class ApiBuildDeployer extends BuildDeployerBase { Deploy(deployInfo) { apiUrl := "https://api.launchpad.games/v1/release-info" - this.app.Service("manager.gui").Dialog(Map( + this.app["manager.gui"].Dialog(Map( "title", "Not Yet Available", "text", "Release info pushing is not yet available. Please update release info manually." )) diff --git a/Lib/LaunchpadBuilder/BuildDeployer/BuildDeployerBase.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/BuildDeployerBase.ahk similarity index 100% rename from Lib/LaunchpadBuilder/BuildDeployer/BuildDeployerBase.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/BuildDeployerBase.ahk diff --git a/Lib/LaunchpadBuilder/BuildDeployer/ChocoDeployer.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/ChocoDeployer.ahk similarity index 100% rename from Lib/LaunchpadBuilder/BuildDeployer/ChocoDeployer.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/ChocoDeployer.ahk diff --git a/Lib/LaunchpadBuilder/BuildDeployer/GitHubBuildDeployer.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/GitHubBuildDeployer.ahk similarity index 100% rename from Lib/LaunchpadBuilder/BuildDeployer/GitHubBuildDeployer.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/BuildDeployer/GitHubBuildDeployer.ahk diff --git a/Lib/LaunchpadBuilder/BulkOperation/LaunchpadBuildOp.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/BulkOperation/LaunchpadBuildOp.ahk similarity index 100% rename from Lib/LaunchpadBuilder/BulkOperation/LaunchpadBuildOp.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/BulkOperation/LaunchpadBuildOp.ahk diff --git a/Lib/LaunchpadBuilder/BulkOperation/LaunchpadDeployOp.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/BulkOperation/LaunchpadDeployOp.ahk similarity index 100% rename from Lib/LaunchpadBuilder/BulkOperation/LaunchpadDeployOp.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/BulkOperation/LaunchpadDeployOp.ahk diff --git a/Lib/LaunchpadBuilder/Gui/BuildSettingsForm.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/Gui/BuildSettingsForm.ahk similarity index 100% rename from Lib/LaunchpadBuilder/Gui/BuildSettingsForm.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/Gui/BuildSettingsForm.ahk diff --git a/Lib/LaunchpadBuilder/Gui/ReleaseInfoForm.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/Gui/ReleaseInfoForm.ahk similarity index 100% rename from Lib/LaunchpadBuilder/Gui/ReleaseInfoForm.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/Gui/ReleaseInfoForm.ahk diff --git a/Lib/LaunchpadBuilder/Includes.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/Includes.ahk similarity index 100% rename from Lib/LaunchpadBuilder/Includes.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/Includes.ahk diff --git a/Lib/LaunchpadBuilder/Includes.test.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/Includes.test.ahk similarity index 100% rename from Lib/LaunchpadBuilder/Includes.test.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/Includes.test.ahk diff --git a/Lib/LaunchpadBuilder/LaunchpadBuilder.library.json b/launchpad_ahk/Lib/LaunchpadBuilder/LaunchpadBuilder.library.json similarity index 100% rename from Lib/LaunchpadBuilder/LaunchpadBuilder.library.json rename to launchpad_ahk/Lib/LaunchpadBuilder/LaunchpadBuilder.library.json diff --git a/Lib/LaunchpadBuilder/VersionIdentifier/GitTagVersionIdentifier.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/VersionIdentifier/GitTagVersionIdentifier.ahk similarity index 100% rename from Lib/LaunchpadBuilder/VersionIdentifier/GitTagVersionIdentifier.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/VersionIdentifier/GitTagVersionIdentifier.ahk diff --git a/Lib/LaunchpadBuilder/VersionIdentifier/VersionIdentifierBase.ahk b/launchpad_ahk/Lib/LaunchpadBuilder/VersionIdentifier/VersionIdentifierBase.ahk similarity index 100% rename from Lib/LaunchpadBuilder/VersionIdentifier/VersionIdentifierBase.ahk rename to launchpad_ahk/Lib/LaunchpadBuilder/VersionIdentifier/VersionIdentifierBase.ahk diff --git a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk similarity index 89% rename from Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk index ec563367..7bc3a131 100644 --- a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk +++ b/launchpad_ahk/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk @@ -39,7 +39,7 @@ class LaunchpadLauncher extends AppBase { ) services["Game"] := Map( - "class", config["gameConfig"]["GameClass"], + "class", config["gameConfig"]["ProcessClass"], "arguments", [ AppRef(), ParameterRef("launcher_key"), @@ -48,7 +48,7 @@ class LaunchpadLauncher extends AppBase { ) services["Launcher"] := Map( - "class", config["launcherConfig"]["LauncherClass"], + "class", config["launcherConfig"]["ProcessClass"], "arguments", [ ParameterRef("launcher_key"), ServiceRef("manager.gui"), @@ -64,11 +64,11 @@ class LaunchpadLauncher extends AppBase { RunApp(config) { super.RunApp(config) - this.Service("Launcher").LaunchGame() + this["Launcher"].LaunchGame() } RestartApp() { - game := this.Service("Game") + game := this["Game"] if (game) { game.StopOverlay() @@ -78,7 +78,7 @@ class LaunchpadLauncher extends AppBase { } ExitApp() { - game := this.Service("Game") + game := this["Game"] if (game) { game.StopOverlay() diff --git a/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk similarity index 91% rename from Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk index 43f0ebbe..765a5369 100644 --- a/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk +++ b/launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk @@ -11,7 +11,7 @@ class SteamConditionBase extends ConditionBase { GetSteamPath(app) { steamPath := "" - platforms := app.Parameter("platforms") + platforms := app.Parameter["platforms"] if (platforms.Has("Steam") && platforms["Steam"].Has("InstallDir")) { steamPath := platforms["Steam"]["InstallDir"] diff --git a/Lib/LaunchpadLauncher/Condition/SteamIsOpenCondition.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamIsOpenCondition.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Condition/SteamIsOpenCondition.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamIsOpenCondition.ahk diff --git a/Lib/LaunchpadLauncher/Condition/SteamOverlayAttachedCondition.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamOverlayAttachedCondition.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Condition/SteamOverlayAttachedCondition.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Condition/SteamOverlayAttachedCondition.ahk diff --git a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk similarity index 83% rename from Lib/LaunchpadLauncher/Game/BlizzardGame.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk index 15a0fc83..c88ed59b 100644 --- a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk +++ b/launchpad_ahk/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk @@ -4,10 +4,10 @@ class BlizzardGame extends SimpleGame { playButtonColors := ["0074E0", "148EFF"] GetRunCmd() { - launcherPath := this.app.Service("Launcher").config["LauncherInstallDir"] . "\" . this.app.Service("Launcher").config["LauncherExe"] + launcherPath := this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] if (launcherPath != "") { - gameKey := this.config["GameLauncherSpecificId"] + gameKey := this.config["GamePlatformRef"] launcherPath .= " --game=" . gameKey . " --gamepath=`"" . this.config["GameInstallDir"] . "`" --productcode=" . gameKey } @@ -16,7 +16,7 @@ class BlizzardGame extends SimpleGame { RunGameRun() { pid := super.RunGameRun() - winTitle := this.app.Service("Launcher").config["LauncherWindowTitle"] + winTitle := this.app["Launcher"].config["LauncherWindowTitle"] if (!WinExist(winTitle)) { WinWait(winTitle) @@ -53,7 +53,7 @@ class BlizzardGame extends SimpleGame { } CleanupAfterRun(progress := "") { - winTitle := this.app.Service("Launcher").config["LauncherWindowTitle"] + winTitle := this.app["Launcher"].config["LauncherWindowTitle"] if (WinExist(winTitle)) { WinClose("ahk_id" . WinGetID(winTitle)) } diff --git a/Lib/LaunchpadLauncher/Game/GameBase.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Game/GameBase.ahk similarity index 97% rename from Lib/LaunchpadLauncher/Game/GameBase.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Game/GameBase.ahk index f50dd053..e943ab16 100644 --- a/Lib/LaunchpadLauncher/Game/GameBase.ahk +++ b/launchpad_ahk/Lib/LaunchpadLauncher/Game/GameBase.ahk @@ -23,7 +23,7 @@ class GameBase { config := Map() } - this.launcherConfig := app.Service("config.app") + this.launcherConfig := app["config.app"] InvalidParameterException.CheckTypes("GameBase", "app", app, "AppBase", "key", key, "", "config", config, "Map") this.app := app this.key := key @@ -33,7 +33,7 @@ class GameBase { Log(message, level := "Debug") { if (this.app.Services.Has("logger") && this.launcherConfig["LoggingLevel"] != "None") { - this.app.Service("logger").Log(this.key . ": " . message, level) + this.app["logger"].Log(this.key . ": " . message, level) } } @@ -180,14 +180,14 @@ class GameBase { StartOverlay() { SetTimer(this.overlayCallbackObj, 0) this.Log("Starting Launchpad Overlay...") - this.app.Service("manager.overlay").Start(this.launcherConfig["OverlayHotkey"]) + this.app["manager.overlay"].Start(this.launcherConfig["OverlayHotkey"]) this.overlayStarted := true } StopOverlay() { if (this.overlayStarted) { this.Log("Shutting down Launchpad Overlay...") - this.app.Service("manager.overlay").Close() + this.app["manager.overlay"].Close() } } @@ -198,7 +198,7 @@ class GameBase { } this.Log("Closing overlay if running...") - this.app.Service("manager.overlay").Close() + this.app["manager.overlay"].Close() this.Log("Cleaning up scheduled task(s)...") this.CleanupScheduledTask() } diff --git a/Lib/LaunchpadLauncher/Game/RiotGame.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Game/RiotGame.ahk similarity index 52% rename from Lib/LaunchpadLauncher/Game/RiotGame.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Game/RiotGame.ahk index 0a63e8ec..1d63a6d2 100644 --- a/Lib/LaunchpadLauncher/Game/RiotGame.ahk +++ b/launchpad_ahk/Lib/LaunchpadLauncher/Game/RiotGame.ahk @@ -1,9 +1,9 @@ class RiotGame extends SimpleGame { GetRunCmd() { - launcherPath := "`"" . this.app.Service("Launcher").config["LauncherInstallDir"] . "\" . this.app.Service("Launcher").config["LauncherExe"] . "`"" + launcherPath := "`"" . this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] . "`"" if (launcherPath != "") { - gameKey := this.config["GameLauncherSpecificId"] + gameKey := this.config["GamePlatformRef"] launcherPath .= " --launch-product=" . gameKey . " --launch-patchline=live" } diff --git a/Lib/LaunchpadLauncher/Game/SimpleGame.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Game/SimpleGame.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Game/SimpleGame.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Game/SimpleGame.ahk diff --git a/Lib/LaunchpadLauncher/Gui/LauncherProgressIndicator.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Gui/LauncherProgressIndicator.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Gui/LauncherProgressIndicator.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Gui/LauncherProgressIndicator.ahk diff --git a/Lib/LaunchpadLauncher/Includes.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Includes.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Includes.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Includes.ahk diff --git a/Lib/LaunchpadLauncher/Includes.test.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Includes.test.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Includes.test.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Includes.test.ahk diff --git a/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk similarity index 99% rename from Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk index 21754d91..900a7adb 100644 --- a/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk +++ b/launchpad_ahk/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk @@ -189,7 +189,7 @@ class LauncherBase { } LaunchGameAction() { - this.Log("Calling managed game's RunGame action") + this.Log("Calling game process's RunGame action") return this.game.RunGame(this.progress) } diff --git a/Lib/LaunchpadLauncher/Launcher/SimpleLauncher.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Launcher/SimpleLauncher.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Launcher/SimpleLauncher.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Launcher/SimpleLauncher.ahk diff --git a/Lib/LaunchpadLauncher/LaunchpadLauncher.library.json b/launchpad_ahk/Lib/LaunchpadLauncher/LaunchpadLauncher.library.json similarity index 100% rename from Lib/LaunchpadLauncher/LaunchpadLauncher.library.json rename to launchpad_ahk/Lib/LaunchpadLauncher/LaunchpadLauncher.library.json diff --git a/Lib/LaunchpadLauncher/Service/OverlayManager.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/Service/OverlayManager.ahk similarity index 100% rename from Lib/LaunchpadLauncher/Service/OverlayManager.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/Service/OverlayManager.ahk diff --git a/Lib/LaunchpadLauncher/State/LaunchpadLauncherState.ahk b/launchpad_ahk/Lib/LaunchpadLauncher/State/LaunchpadLauncherState.ahk similarity index 100% rename from Lib/LaunchpadLauncher/State/LaunchpadLauncherState.ahk rename to launchpad_ahk/Lib/LaunchpadLauncher/State/LaunchpadLauncherState.ahk diff --git a/Lib/Shared/Includes.ahk b/launchpad_ahk/Lib/Shared/Includes.ahk similarity index 88% rename from Lib/Shared/Includes.ahk rename to launchpad_ahk/Lib/Shared/Includes.ahk index 9cfe560e..faff2db3 100644 --- a/Lib/Shared/Includes.ahk +++ b/launchpad_ahk/Lib/Shared/Includes.ahk @@ -1,10 +1,28 @@ ; Automatically-generated file. Manual edits will be overwritten. -#Include Modules\Auth\AuthInfo\AuthInfo.ahk -#Include Modules\Auth\AuthInfo\JwtAuthInfo.ahk -#Include Modules\Auth\AuthProvider\AuthProviderBase.ahk -#Include Modules\Auth\AuthProvider\JwtAuthProvider.ahk -#Include Modules\LaunchpadApi\AuthProvider\LaunchpadApiAuthProvider.ahk -#Include Modules\LaunchpadApi\DataSource\ApiDataSource.ahk +#Include Modules\LaunchpadApi\EventSubscriber\LaunchpadApiSubscriber.ahk +#Include Modules\WebServices\ComponentManager\WebServiceAdapterManager.ahk +#Include Modules\WebServices\Entity\WebServiceEntity.ahk +#Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk +#Include Modules\WebServices\Event\WebServicesEntityDataParamsEvent.ahk +#Include Modules\WebServices\Event\WebServicesRequestEvent.ahk +#Include Modules\WebServices\Event\WebServicesResponseEvent.ahk +#Include Modules\WebServices\Events\WebServicesEvents.ahk +#Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk +#Include Modules\WebServices\Factory\WebServiceAdapterFactory.ahk +#Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk +#Include Modules\WebServices\Gui\Form\FeedbackWindow.ahk +#Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk +#Include Modules\WebServices\LayerSource\WebServiceAdapterLayerSource.ahk +#Include Modules\WebServices\WebServiceAdapter\FileWebServiceAdapter.ahk +#Include Modules\WebServices\WebServiceAdapter\JsonWebServiceAdapter.ahk +#Include Modules\WebServices\WebServiceAdapter\WebServiceAdapterBase.ahk +#Include Modules\WebServices\WebServiceAuthenticator\JwtWebServiceAuthenticator.ahk +#Include Modules\WebServices\WebServiceAuthenticator\WebServiceAuthenticatorBase.ahk +#Include Modules\WebServices\WebServiceRequest\BasicWebServiceRequest.ahk +#Include Modules\WebServices\WebServiceRequest\WebServiceRequestBase.ahk +#Include Modules\WebServices\WebServiceResponse\CachedWebServiceResponse.ahk +#Include Modules\WebServices\WebServiceResponse\HttpReqWebServiceResponse.ahk +#Include Modules\WebServices\WebServiceResponse\WebServiceResponseBase.ahk #Include Vendor\Gdip_All.ahk #Include Vendor\LV_Constants.ahk #Include Volantis.App\App\AppBase.ahk @@ -23,13 +41,6 @@ #Include Volantis.App\Config\AppConfig.ahk #Include Volantis.App\Container\ServiceComponentContainer.ahk #Include Volantis.App\Container\WindowContainer.ahk -#Include Volantis.App\DataSource\DataSourceBase.ahk -#Include Volantis.App\DataSourceItem\DataSourceItemBase.ahk -#Include Volantis.App\DataSourceItem\DSAssetFile.ahk -#Include Volantis.App\DataSourceItem\DSFile.ahk -#Include Volantis.App\DataSourceItem\DSJson.ahk -#Include Volantis.App\DataSourceItem\DSListing.ahk -#Include Volantis.App\Entity\AppEntityBase.ahk #Include Volantis.App\Entity\BackupEntity.ahk #Include Volantis.App\Entity\TaskEntity.ahk #Include Volantis.App\Event\AlterComponentsEvent.ahk @@ -38,7 +49,10 @@ #Include Volantis.App\Event\ComponentInfoEvent.ahk #Include Volantis.App\Event\DefineComponentsEvent.ahk #Include Volantis.App\Event\LoadComponentEvent.ahk +#Include Volantis.App\Event\MenuItemsEvent.ahk +#Include Volantis.App\Event\MenuResultEvent.ahk #Include Volantis.App\Event\RegisterComponentsEvent.ahk +#Include Volantis.App\Event\ReleaseInfoEvent.ahk #Include Volantis.App\Event\ServiceDefinitionsEvent.ahk #Include Volantis.App\Events\Events.ahk #Include Volantis.App\Exception\AppException.ahk @@ -53,7 +67,6 @@ #Include Volantis.App\Gui\Dialog\UpdateAvailableWindow.ahk #Include Volantis.App\Gui\EntityEditor\EntityEditorBase.ahk #Include Volantis.App\Gui\EntityEditor\SimpleEntityEditor.ahk -#Include Volantis.App\Gui\Form\FeedbackWindow.ahk #Include Volantis.App\Gui\Form\FormGuiBase.ahk #Include Volantis.App\Gui\Form\IconSelector.ahk #Include Volantis.App\Gui\ManageWindow\ManageEntitiesWindow.ahk @@ -83,12 +96,10 @@ #Include Volantis.App\Installer\InstallerComponent\GitHubReleaseInstallerComponent.ahk #Include Volantis.App\Installer\InstallerComponent\InstallerComponentBase.ahk #Include Volantis.App\Service\AppServiceBase.ahk -#Include Volantis.App\Service\AuthService.ahk #Include Volantis.App\Service\EventManager.ahk #Include Volantis.App\Service\LoggerService.ahk #Include Volantis.App\Service\NotificationService.ahk #Include Volantis.App\Service\ComponentManager\CacheManager.ahk -#Include Volantis.App\Service\ComponentManager\DataSourceManager.ahk #Include Volantis.App\Service\ComponentManager\GuiManager.ahk #Include Volantis.App\Service\ComponentManager\InstallerManager.ahk #Include Volantis.App\Service\ComponentManager\ThemeManager.ahk @@ -96,6 +107,7 @@ #Include Volantis.App\State\AppState.ahk #Include Volantis.App\State\CacheState.ahk #Include Volantis.App\State\JsonState.ahk +#Include Volantis.App\State\ParameterState.ahk #Include Volantis.App\State\StateBase.ahk #Include Volantis.Base\CLR\CLR.ahk #Include Volantis.Base\Event\EventBase.ahk @@ -210,16 +222,20 @@ #Include Volantis.Entity\EntityManager\EntityManagerBase.ahk #Include Volantis.Entity\EntityStorage\ConfigEntityStorage.ahk #Include Volantis.Entity\EntityStorage\EntityStorageBase.ahk +#Include Volantis.Entity\EntityStorage\NullEntityStorage.ahk #Include Volantis.Entity\EntityType\BasicEntityType.ahk #Include Volantis.Entity\EntityType\EntityTypeBase.ahk #Include Volantis.Entity\Event\EntityDataProcessorsEvent.ahk +#Include Volantis.Entity\Event\EntityDetectValuesEvent.ahk #Include Volantis.Entity\Event\EntityEvent.ahk #Include Volantis.Entity\Event\EntityFieldDefinitionsEvent.ahk #Include Volantis.Entity\Event\EntityFieldGroupsEvent.ahk #Include Volantis.Entity\Event\EntityLayersEvent.ahk +#Include Volantis.Entity\Event\EntityLayerSourcesEvent.ahk +#Include Volantis.Entity\Event\EntityListEvent.ahk +#Include Volantis.Entity\Event\EntityParentEvent.ahk #Include Volantis.Entity\Event\EntityReferenceEvent.ahk #Include Volantis.Entity\Event\EntityRefreshEvent.ahk -#Include Volantis.Entity\Event\EntityStorageEvent.ahk #Include Volantis.Entity\Event\EntityValidateEvent.ahk #Include Volantis.Entity\Events\EntityEvents.ahk #Include Volantis.Entity\Exception\EntityException.ahk @@ -230,6 +246,7 @@ #Include Volantis.Entity\Factory\EntityTypeFactory.ahk #Include Volantis.Entity\LayeredData\EntityData.ahk #Include Volantis.Entity\LayerSource\EntityStorageLayerSource.ahk +#Include Volantis.Entity\LayerSource\ParentEntityLayerSource.ahk #Include Volantis.Entity\Query\EntityQuery.ahk #Include Volantis.Entity\Validator\BasicValidator.ahk #Include Volantis.Entity\Validator\ValidatorBase.ahk diff --git a/Lib/Shared/Includes.test.ahk b/launchpad_ahk/Lib/Shared/Includes.test.ahk similarity index 88% rename from Lib/Shared/Includes.test.ahk rename to launchpad_ahk/Lib/Shared/Includes.test.ahk index 2fe8682e..f157d4f8 100644 --- a/Lib/Shared/Includes.test.ahk +++ b/launchpad_ahk/Lib/Shared/Includes.test.ahk @@ -1,5 +1,4 @@ ; Automatically-generated file. Manual edits will be overwritten. -#Include Modules\Auth\AuthInfo\AuthInfo.test.ahk #Include Volantis.App\App\AppBase.test.ahk #Include Volantis.Base\Event\EventBase.test.ahk #Include Volantis.Utility\Debugger\Debugger.test.ahk diff --git a/launchpad_ahk/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/launchpad_ahk/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk new file mode 100644 index 00000000..1d349909 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk @@ -0,0 +1,24 @@ +class LaunchpadApiSubscriber extends EventSubscriberBase { + GetEventSubscribers() { + return Map( + WebServicesEvents.ENTITY_DATA_PARAMS, [ + ObjBindMethod(this, "EntityDataParams") + ] + ) + } + + EntityDataParams(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + ; TODO figure out how to access these values while the data layers are still being loaded + if (event.WebService["id"] == "launchpad_api") { + if (HasBase(event.Entity, LauncherEntity.Prototype)) { + event.Params["platformId"] := "Blizzard" ;event.Entity["Platform"]["id"] + } else if (HasBase(event.Entity, LaunchProcessEntity.Prototype)) { + event.Params["platformId"] := "Blizzard" ;event.Entity.ParentEntity["Platform"]["id"] + } + } + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/launchpad_ahk/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json new file mode 100644 index 00000000..3bb1307d --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -0,0 +1,164 @@ +{ + "module": { + "name": "Launchpad API", + "icon": "", + "category": "Web Services", + "tags": ["Launchpad", "LaunchpadBuilder"], + "description": "Connect with Launchpad.games to sync game details and share your launchers and themes", + "author": { + "name": "Ben McClure, Volantis Dev", + "url": "https://volantisdev.com" + }, + "website": "https://launchpad.games", + "version": "{{VERSION}}", + "appVersion": "", + "dependencies": ["WebServices"] + }, + "parameters": { + "app.supports_update_check": true, + "web_services.adapters.launchpad_api.account_info": { + "dataType": "account_info", + "requestPath": "/status", + "cacheResponse": false, + "readAuth": true, + "dataMap": { + "email": "account" + } + }, + "web_services.adapters.launchpad_api.hello": { + "dataType": "availability_check", + "requestPath": "/hello", + "cacheResponse": false + }, + "web_services.adapters.launchpad_api.game_process_launcher_defaults": { + "dataType": "entity_data", + "requestPath": "/games/{id}", + "dataSelector": "gameProcessDefaults", + "entityType": "game_process", + "requiredParams": ["id"], + "tags": ["defaults"], + "weight": 5 + }, + "web_services.adapters.launchpad_api.game_process_platform_defaults": { + "dataType": "entity_data", + "requestPath": "/platforms/{platformId}", + "dataSelector": "data.gameProcessDefaults", + "entityType": "game_process", + "requiredParams": ["platformId"], + "tags": ["defaults"], + "weight": 2 + }, + "web_services.adapters.launchpad_api.launcher_defaults": { + "dataType": "entity_data", + "requestPath": "/games/{id}", + "dataSelector": "defaults", + "entityType": "launcher", + "requiredParams": ["id"], + "tags": ["defaults"], + "weight": 5 + }, + "web_services.adapters.launchpad_api.launcher_list": { + "dataType": "entity_list", + "requestPath": "/game-keys", + "entityType": "launcher" + }, + "web_services.adapters.launchpad_api.launcher_lookup": { + "dataType": "entity_lookup", + "requestPath": "/lookup/{id}/{platformId}", + "dataSelector": "id", + "entityType": "launcher", + "requiredParams": ["id"] + }, + "web_services.adapters.launchpad_api.launcher_platform_defaults": { + "dataType": "entity_data", + "requestPath": "/platforms/{platformId}", + "dataSelector": "data.launcherDefaults", + "entityType": "launcher", + "requiredParams": ["platformId"], + "tags": ["defaults"], + "weight": 2 + }, + "web_services.adapters.launchpad_api.launcher_process_launcher_defaults": { + "dataType": "entity_data", + "requestPath": "/games/{id}", + "dataSelector": "launcherProcessDefaults", + "entityType": "launcher_process", + "requiredParams": ["id"], + "tags": ["defaults"], + "weight": 5 + }, + "web_services.adapters.launchpad_api.launcher_process_platform_defaults": { + "dataType": "entity_data", + "requestPath": "/platforms/{platformId}", + "dataSelector": "data.launcherProcessDefaults", + "entityType": "launcher_process", + "requiredParams": ["platformId"], + "tags": ["defaults"], + "weight": 2 + }, + "web_services.adapters.launchpad_api.platform_defaults": { + "dataType": "entity_data", + "requestPath": "/platforms/{id}", + "dataSelector": "data.defaults", + "entityType": "platform", + "requiredParams": ["id"], + "tags": ["defaults"] + }, + "web_services.adapters.launchpad_api.platform_list": { + "dataType": "entity_list", + "requestPath": "/platforms", + "entityType": "platform" + }, + "web_services.adapters.launchpad_api.release_info": { + "dataType": "release_info", + "requestPath": "/release-info/{version}", + "cacheMaxAge": 1800 + }, + "web_services.adapters.launchpad_api.submit_error": { + "dataType": "error_submission", + "requestPath": "/submit-error", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.adapters.launchpad_api.submit_feedback": { + "dataType": "feedback_submission", + "requestPath": "/submit-feedback", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.providers.launchpad_api": { + "name": "Launchpad API", + "EndpointUrl": "https://api.launchpad.games/v1", + "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", + "AuthenticationRefreshPath": "token", + "IconSrc": "logo", + "SupportsAuthentication": true, + "Authenticator": "jwt", + "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", + "LoginWindow": "LaunchpadLoginWindow" + }, + "web_services.services.launchpad_api": { + "name": "Launchpad API", + "Provider": "launchpad_api" + } + }, + "services": { + "cache_state.launchpad_api": { + "class": "CacheState", + "arguments": ["@{App}", "@@config.cache_dir", "API.json"] + }, + "cache.launchpad_api": { + "class": "FileCache", + "arguments": ["@{App}", "@cache_state.launchpad_api", "@@config.cache_dir", "API"] + }, + "event_subscriber.launchpad_api": { + "class": "LaunchpadApiSubscriber", + "arguments": ["@{}"], + "tags": ["event_subscriber"] + } + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk new file mode 100644 index 00000000..b4e81fd3 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk @@ -0,0 +1,287 @@ +class WebServiceAdapterManager { + container := "" + parameterPrefix := "" + adapterFactory := "" + entityTypeMgr := "" + eventMgr := "" + adapters := Map() + + __New(container, parameterPrefix, adapterFactory, entityTypeMgr, eventMgr) { + this.container := container + this.parameterPrefix := parameterPrefix + this.adapterFactory := adapterFactory + this.entityTypeMgr := entityTypeMgr + this.eventMgr := eventMgr + } + + AdapterRequest(params, filters, operation := "read", multiple := false, webService := "") { + if (!params) { + params := Map() + } + + if (!filters) { + filters := Map() + } + + if (Type(filters) == "String") { + filters := Map("dataType", filters) + } + + results := Map() + + for adapterKey, adapter in this.GetAdapters(filters, operation, 0, webService) { + result := adapter.SendRequest(operation, params) + + if (result) { + if (!multiple) { + results := result + + break + } + + results[adapterKey] := result + } + + if (IsNumber(multiple) && results.Count >= multiple) { + break + } + } + + return results + } + + HasAdapters(filters := "", operation := "", webService := "") { + return !!(this.GetAdapterIds(filters, operation, 1, webService).Length) + } + + GetAdapters(filters := "", operation := "", limit := 0, webService := "") { + adapterIds := this.GetAdapterIds(filters, operation, limit, webService) + + adapters := Map() + + for , adapterId in adapterIds { + adapters[adapterId] := this.GetAdapter(adapterId) + } + + return adapters + } + + GetAdapter(id) { + adapter := "" + + if (this.adapters.Has(id)) { + adapter := this.adapters[id] + } + + if (!adapter && InStr(id, ".")) { + idParts := StrSplit(id, ".") + webServiceId := idParts[1] + adapterKey := idParts[2] + + webService := this.entityTypeMgr.GetManager("web_service")[webServiceId] + + if (webService["Enabled"]) { + param := this.parameterPrefix . webService["Provider"]["id"] . "." . adapterKey + + if (this.container.HasParameter(param)) { + adapter := this.adapterFactory.CreateWebServiceAdapter(webService, this.container.GetParameter(param)) + this.adapters[id] := adapter + } + } + } + + return adapter + } + + HasAdapter(id) { + exists := this.adapters.Has(id) + + if (!exists) { + idParts := StrSplit(id, ".") + webServiceId := idParts[1] + adapterKey := idParts[2] + webService := this.entityTypeMgr.GetManager("web_service")[webServiceId] + param := this.parameterPrefix . webService["Provider"]["id"] . "." . id + exists := this.container.HasParameter(param) + } + + return exists + } + + GetAdapterIds(filters := "", operation := "", limit := 0, webService := "") { + if (!filters) { + filters := Map() + } + + if (Type(filters) == "String") { + filters := Map("dataType", filters) + } + + adapterIds := [] + weights := this._getFilterWeights(filters) + + for webServiceId, webService in this._getWebServicesForOperation(webService) { + providerId := webService["Provider"]["id"] + paramKey := "web_services.adapters." . providerId + + if (this.container.HasParameter(paramKey)) { + adapterData := this.container.GetParameter(this.parameterPrefix . providerId) + + for weightIndex, weight in weights { + filters["weight"] := weight + + for key, definition in adapterData { + adapterId := webServiceId . "." . key + adapter := this.GetAdapter(adapterId) + definition := adapter.definition + include := (!operation || adapter.SupportsOperation(operation)) + + if (include) { + for filterKey, filterVal in filters { + if (!definition.Has(filterKey)) { + include := false + + break + } + + include := this._filterValue(definition[filterKey], filterVal) + + if (!include) { + break + } + } + } + + if (include) { + adapterIds.Push(adapterId) + + if (limit && adapterIds.Length >= limit) { + break 2 + } + } + } + } + } + } + + return adapterIds + } + + _getWebServicesForOperation(webService) { + webServices := "" + + if (webService) { + webServices := Type(webService == "String") ? Map(webService["id"], webService) : webService + } else { + webServices := this.entityTypeMgr.GetManager("web_service") + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + } + + return webServices + } + + _getFilterWeights(filters) { + weights := filters.Has("weight") + ? filters["weight"] + : "" + + if (!weights) { + weights := [] + + startingWeight := -10 + maxWeight := 10 + + Loop (maxWeight - startingWeight) { + weights.Push(startingWeight + A_Index - 1) + } + } + + if (Type(weights) == "String") { + weights := [weights] + } + + return weights + } + + _filterArrayValues(definitionArray, filterArray) { + include := !filterArray || !!definitionArray + + if (include) { + if (Type(filterArray) == "String") { + filterArray := [filterArray] + } + + if (Type(definitionArray) == "String") { + definitionArray := [definitionArray] + } + + for , val in filterArray { + definitionHasVal := false + + for , definitionVal in definitionArray { + definitionHasVal := this._filterValue(definitionVal, val) + + if (definitionVal == val) { + definitionHasVal := true + + break + } + } + + if (!definitionHasVal) { + include := false + + break + } + } + } + + return include + } + + _filterMapValues(definitionMap, filterMap) { + include := !filterMap || !!definitionMap + + if (include) { + if (Type(filterMap) == "String") { + filterMap := [filterMap] + } + + if (Type(definitionMap) == "String") { + definitionMap := [definitionMap] + } + + for key, val in filterMap { + exists := definitionMap.Has(key) + + if (exists) { + exists := this._filterValue(definitionMap[key], val) + } + + if (!exists) { + include := false + + break + } + } + } + + return include + } + + _filterValue(definitionVal, filterVal) { + include := false + + if (Type(filterVal) == "Array") { + include := this._filterArrayValues(definitionVal, filterVal) + } else if (Type(filterVal) == "Map") { + include := this._filterMapValues(definitionVal, filterVal) + } else { + include := (definitionVal == filterVal) + } + + return include + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk new file mode 100644 index 00000000..d0caf51b --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -0,0 +1,309 @@ +class WebServiceEntity extends FieldableEntity { + cacheObj := "" + stateObj := "" + persistentStateObj := "" + statusIndicators := [] + isWebServiceEntity := true + + Authenticated { + get => this.IsAuthenticated() + } + + UserId { + get => this.AuthData["user_id"] + } + + AuthData[key] { + get => this.GetAuthData(key, true) + set => this.SetAuthData(key, value) + } + + __New(id, entityTypeId, container, cacheObj, stateObj, persistentStateObj, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { + this.cacheObj := cacheObj + this.stateObj := stateObj + this.persistentStateObj := persistentStateObj + + super.__New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, autoLoad, parentEntity, parentEntityStorage) + } + + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { + className := this.Prototype.__Class + + return %className%( + id, + entityTypeId, + container, + container.Get("cache.web_services"), + container.Get("state.web_services_tmp"), + container.Get("state.web_services"), + container.Get("entity_field_factory." . entityTypeId), + container.Get("entity_widget_factory." . entityTypeId), + eventMgr, + storageObj, + idSanitizer, + autoLoad, + parentEntity, + parentEntityStorage + ) + } + + BaseFieldDefinitions() { + definitions := super.BaseFieldDefinitions() + + if (this.idVal == "launchpad_api" && definitions.Has("name")) { + definitions["name"]["editable"] := false + } + + definitions["Provider"] := Map( + "type", "entity_reference", + "entityType", "web_service_provider", + "required", true, + "editable", false + ) + + definitions["AutoLogin"] := Map( + "type", "boolean", + "description", "Automatically authenticate with this service when Launchpad starts.", + "required", false, + "default", (this.idVal == "launchpad_api") + ) + + definitions["Enabled"] := Map( + "type", "boolean", + "required", false, + "default", true + ) + + definitions["StatusIndicator"] := Map( + "type", "boolean", + "required", false, + "default", (this.idVal == "launchpad_api") + ) + + definitions["StatusIndicatorExpanded"] := Map( + "type", "boolean", + "required", false, + "default", (this.idVal == "launchpad_api") + ) + + definitions["ResponseCache"] := Map( + "type", "boolean", + "required", false, + "default", true + ) + + definitions["ResponseCacheDefaultExpireSeconds"] := Map( + "title", "Response Cache - Default Expiration (seconds)", + "type", "string", + "required", false, + "default", 3600 + ) + + return definitions + } + + GetStatusIndicators() { + return this.statusIndicators + } + + AddStatusIndicator(statusIndicatorCtl) { + this.statusIndicators.Push(statusIndicatorCtl) + } + + UpdateStatusIndicators() { + for , statusIndicatorCtl in this.statusIndicators { + statusIndicatorCtl.UpdateStatusIndicator() + } + } + + IsAuthenticated() { + isAuthenticated := false + + if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) { + isAuthenticated := this["Provider"]["Authenticator"].IsAuthenticated(this) + } + + return isAuthenticated + } + + Login() { + if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) { + this["Provider"]["Authenticator"].Login(this) + } + } + + Logout() { + if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) { + this["Provider"]["Authenticator"].Logout(this) + } + } + + Request(path, method := "", data := "", useAuthentication := -1, cacheResponse := true) { + if (!method) { + method := this["Provider"]["DefaultMethod"] + } + + if (useAuthentication == -1) { + useAuthentication := this["Provider"]["AuthenticateRequestsByDefault"] + } + + return BasicWebServiceRequest(this.eventMgr, this, this.cacheObj, method, path, data, useAuthentication, cacheResponse) + } + + GetAuthData(key := "", includePersistent := true) { + val := this._getStateData(this.stateObj, key) + + if (!val && includePersistent) { + val := this._getStateData(this.persistentStateObj, key) + } + + return val + } + + SetAuthData(keyOrMap, value, persist := false) { + result := this._setStateData(this.stateObj, keyOrMap, value) + + if (persist) { + this._setStateData(this.persistentStateObj, keyOrMap, value) + } + + return this + } + + ResetAuthData(newData := "", persist := false) { + if (!newData) { + newData := Map() + } + + if (!newData.Has("authenticated")) { + newData["authenticated"] := false + } + + this._createStateParents(this.stateObj) + this.stateObj.State["WebServices"][this.Id]["AuthData"] := newData + this.stateObj.SaveState() + + if (persist) { + this._createStateParents(this.persistentStateObj) + this.persistentStateObj.State["WebServices"][this.Id]["AuthData"] := Map( + "authenticated", newData["authenticated"] + ) + } + + return this + } + + DeleteAuthData(key, persist := false) { + this._deleteStateData(this.stateObj, key) + + if (persist) { + this._deleteStateData(this.persistentStateObj, key) + } + + return this + } + + _getStateData(stateObj, key := "") { + save := this._createStateParents(stateObj) + + if (save) { + stateObj.SaveState() + } + + authData := stateObj.State["WebServices"][this.Id]["AuthData"] + + if (key) { + authData := (authData.Has(key) ? authData[key] : "") + } + + return authData + } + + _setStateData(stateObj, key, value) { + this._createStateParents(stateObj) + stateObj.State["WebServices"][this.Id]["AuthData"][key] := value + stateObj.SaveState() + + return this + } + + _deleteStateData(stateObj, key) { + created := this._createStateParents(stateObj) + save := created + + if (!created) { + parent := this._getStateData(stateObj) + + if (HasBase(parent, Map.Prototype) && parent.Has(key)) { + parent.Delete(key) + save := true + } + } + + if (save) { + stateObj.SaveState() + } + + return this + } + + _createStateParents(stateObj) { + modified := false + + if (!stateObj.State.Has("WebServices")) { + stateObj.State["WebServices"] := Map() + modified := true + } + + if (!stateObj.State["WebServices"].Has(this.Id)) { + stateObj.State["WebServices"][this.Id] := Map() + modified := true + } + + if (!stateObj.State["WebServices"][this.Id].Has("AuthData")) { + stateObj.State["WebServices"][this.Id]["AuthData"] := Map() + modified := true + } + + return modified + } + + GetStatusInfo() { + ; @todo fix this data + statusText := "Not logged in" + imgPath := "" + email := "" + + if (this.Authenticated) { + email := this.AuthData["email"] + + if (email) { + statusText := email + } else { + statusText := "Logged in" + } + + imgPath := this.AuthData["photo"] + + if (SubStr(imgPath, 1, 4) == "http") { + cachePath := "account--profile.jpg" + imgPath := this.app["manager.cache"]["file"].GetCachedDownload(cachePath, imgPath) + } + } + + return Map("name", statusText, "email", email, "photo", imgPath) + } + + ShowAccountDetails() { + accountResult := this.container["manager.gui"].Dialog(Map( + "type", "AccountInfoWindow", + "ownerOrParent", this.guiId, + "child", true, + "webService", this + )) + + if (accountResult == "OK" || accountResult == "Logout" || accountResult == "Login") { + this.UpdateStatusIndicators() + } + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk new file mode 100644 index 00000000..5c54ae3a --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk @@ -0,0 +1,92 @@ +class WebServiceProviderEntity extends FieldableEntity { + isWebServiceEntity := true + + BaseFieldDefinitions() { + definitions := super.BaseFieldDefinitions() + + definitions["EndpointUrl"] := Map( + "default", "", + "required", true + ) + + definitions["AuthenticationEndpointUrl"] := Map( + "default", "", + "required", false + ) + + definitions["AuthenticationRefreshPath"] := Map( + "default", "", + "required", false + ) + + definitions["IconSrc"] := Map( + "type", "icon_file", + "default", "webhook", + "required", true + ) + + definitions["SupportsAuthentication"] := Map( + "type", "boolean", + "required", false, + "default", false + ) + + definitions["Authenticator"] := Map( + "type", "service_reference", + "servicePrefix", "web_services_authenticator.", + "default", "", + "required", false + ) + + definitions["DefaultMethod"] := Map( + "default", "GET", + "required", false + ) + + definitions["AuthenticateRequestsByDefault"] := Map( + "type", "boolean", + "default", false, + "required", false + ) + + definitions["LoginWindow"] := Map( + "default", "", + "required", false + ) + + definitions["AppKey"] := Map( + "default", "", + "required", false + ) + + return definitions + } + + Url(path, queryParams := "") { + if (InStr(path, "/") != 1) { + path := "/" . path + } + + return UrlObj(this["EndpointUrl"] . path) + .AddQueryParams(queryParams) + } + + FullPath(path) { + url := this.Url(path) + return url.Path + } + + GetAuthenticationRefreshUrl(queryParams := "") { + endpointUrl := this["AuthenticationEndpointUrl"] + ? this["AuthenticationEndpointUrl"] + : this["EndpointUrl"] + refreshPath := this["AuthenticationRefreshPath"] + + if (refreshPath && InStr(refreshPath, "/") != 1) { + refreshPath := "/" . refreshPath + } + + return UrlObj(endpointUrl . refreshPath) + .AddQueryParams(queryParams) + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk new file mode 100644 index 00000000..ad6ba482 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk @@ -0,0 +1,20 @@ +class WebServicesEntityDataParamsEvent extends EntityEvent { + _webService := "" + _params := "" + + __New(eventName, entityTypeId, entityObj, webService, params) { + this._webService := webService + this._params := params + + super.__New(eventName, entityTypeId, entityObj) + } + + WebService { + get => this._webService + } + + Params { + get => this._params + set => this._params := value + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk new file mode 100644 index 00000000..d52a8c42 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk @@ -0,0 +1,17 @@ +class WebServicesRequestEvent extends EventBase { + _requestObj := "" + + __New(eventName, requestObj) { + this._requestObj := requestObj + + super.__New(eventName) + } + + Request { + get => this.requestObj + } + + HttpReq { + get => this.Request.GetHttpReq() + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk new file mode 100644 index 00000000..80010b44 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk @@ -0,0 +1,20 @@ +class WebServicesResponseEvent extends EventBase { + _requestObj := "" + _responseObj := "" + + __New(eventName, requestObj, responseObj) { + this._requestObj := requestObj + this._responseObj := responseObj + + super.__New(eventName) + } + + Request { + get => this._requestObj + } + + Response { + get => this._responseObj + set => this._responseObj := value + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk new file mode 100644 index 00000000..3e7a0598 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk @@ -0,0 +1,322 @@ +class WebServicesEventSubscriber extends EventSubscriberBase { + adapterMgr := "" + + __New(container, adapterMgr) { + this.adapterMgr := adapterMgr + + super.__New(container) + } + + GetEventSubscribers() { + return Map( + Events.APP_POST_STARTUP, [ + ObjBindMethod(this, "OnPostStartup") + ], + Events.APP_MENU_ITEMS_LATE, [ + ObjBindMethod(this, "OnMenuItemsLate") + ], + Events.APP_MENU_PROCESS_RESULT, [ + ObjBindMethod(this, "OnMenuProcessResult") + ], + EntityEvents.ENTITY_DATA_LAYERS, [ + ObjBindMethod(this, "EntityDataLayers") + ], + EntityEvents.ENTITY_LAYER_SOURCES, [ + ObjBindMethod(this, "EntityLayerSources") + ], + Events.APP_GET_RELEASE_INFO, [ + ObjBindMethod(this, "GetReleaseInfo") + ], + EntityEvents.ENTITY_FIELD_GROUPS, [ + ObjBindMethod(this, "EntityFieldGroups") + ], + EntityEvents.ENTITY_FIELD_DEFINITIONS, [ + ObjBindMethod(this, "EntityFieldDefinitions") + ], + EntityEvents.ENTITY_DETECT_VALUES, [ + ObjBindMethod(this, "EntityDetectValues") + ], + EntityEvents.ENTITY_LIST_ENTITIES, [ + ObjBindMethod(this, "ListEntities") + ] + ) + } + + OnPostStartup(event, extra, eventName, hwnd) { + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Condition(IsTrueCondition(), "AutoLogin") + .Execute() + + for key, webService in webServices { + webService.Login() + } + } + + OnMenuItemsLate(event, extra, eventName, hwnd) { + event.MenuItems.Push(Map( + "label", "Provide &Feedback", + "name", "ProvideFeedback" + )) + } + + OnMenuProcessResult(event, extra, eventName, hwnd) { + if (!event.IsFinished) { + if (event.Result == "ProvideFeedback") { + this.container["manager.gui"].Dialog(Map("type", "FeedbackWindow")) + event.IsFinished := true + } + } + } + + _getEntityLayerKey(webService, adapterId) { + return webService["id"] . "." . adapterId + } + + EntityDataLayers(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for webServiceId, webService in webServices { + adapterIds := this.adapterMgr.GetAdapterIds(Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId, + "tags", "defaults" + ), "", 0, webService) + + for , adapterId in adapterIds { + adapter := this.adapterMgr.GetAdapter(adapterId) + layerExists := false + layerKey := this._getEntityLayerKey(webService, adapterId) + + for , existingLayerKey in event.Layers { + if (existingLayerKey == layerKey) { + layerExists := true + + break + } + } + + if (!layerExists) { + event.Layers.Push(layerKey) + } + } + } + } + + EntityLayerSources(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for webServiceId, webService in webServices { + adapters := this.adapterMgr.GetAdapters(Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ), "", 0, webService) + + paramsEvent := WebServicesEntityDataParamsEvent( + WebServicesEvents.ENTITY_DATA_PARAMS, + event.EntityTypeId, + event.Entity, + webService, + Map("id", event.Entity.Id) + ) + this.container["manager.event"].DispatchEvent(paramsEvent) + + for key, adapter in adapters { + layerKey := this._getEntityLayerKey(webService, key) + + if (!event.LayerSources.Has(layerKey)) { + event.LayerSources[layerKey] := WebServiceAdapterLayerSource(adapter, paramsEvent.Params) + } + } + } + } + + GetReleaseInfo(event, extra, eventName, hwnd) { + if (!event.ReleaseInfo.Count && this.container.GetApp().Version != "{{VERSION}}") { + releaseInfo := this.adapterMgr.AdapterRequest("", "release_info") + + if (releaseInfo && releaseInfo.Count) { + event.ReleaseInfo := releaseInfo + } + } + } + + EntityFieldGroups(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + if (!event.FieldGroups.Has("web_services")) { + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + addGroup := false + + for key, webService in webServices { + filters := Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ) + operation := "read" + + if (this.adapterMgr.HasAdapters(filters, operation, webService)) { + addGroup := true + + break + } + } + + if (addGroup) { + event.FieldGroups["web_services"] := Map( + "name", "Web Services", + "weight", 100 + ) + } + } + } + + EntityFieldDefinitions(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for key, webService in webServices { + filters := Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ) + operation := "read" + + if (this.adapterMgr.HasAdapters(filters, operation, webService)) { + event.FieldDefinitions["web_service_" . webService["id"] . "_ref"] := Map( + "title", webService["name"] . " Reference", + "description", "The key that is used to look up the entity's data from the " . webService["name"] . " web service.", + "help", "It defaults to the entity ID, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same entity can exist by giving them different IDs but using the same " . webService["name"] . " reference.", + "group", "web_services", + "processValue", false, + "modes", Map("simple", Map("formField", false)) + ) + + break + } + } + } + + EntityDetectValues(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for key, webService in webServices { + fieldId := "web_service_" . webService["id"] . "_ref" + filters := Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ) + + if ( + this.adapterMgr.HasAdapters(filters, "read", webService) + && (!event.Values.Has(fieldId) || !event.Values[fieldId]) + && event.Entity.HasField(fieldId) + && (!event.Entity.RawData.Has(fieldId) || !event.Entity.RawData[fieldId]) + ) { + paramsEvent := WebServicesEntityDataParamsEvent( + WebServicesEvents.ENTITY_DATA_PARAMS, + event.EntityTypeId, + event.Entity, + webService, + Map("id", event.Entity.Id) + ) + this.container["manager.event"].DispatchEvent(paramsEvent) + + result := this.adapterMgr.AdapterRequest( + paramsEvent.Params, + Map( + "dataType", "entity_lookup", + "entityType", event.EntityTypeId + ), + "read", + false, + webService + ) + + if (!result || Type(result) != "String") { + result := "" ; TODO decide whether to default to blank or the entity ID + } + + event.Values[fieldId] := result + } + } + } + + ListEntities(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + if (event.includeExtended) { + entityMgr := this.container["entity_manager." . event.EntityTypeId] + results := this.adapterMgr.AdapterRequest("", Map( + "dataType", "entity_list", + "entityType", event.EntityTypeId + ), "read", true) + + if (results && HasBase(results, Array.Prototype)) { + managedIds := event.includeManaged + ? [] + : entityMgr.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute() + + for index, id in results { + exists := false + + for , existingId in event.EntityList { + if (existingId == id) { + exists := true + break + } + } + + if (!exists && !event.includeManaged) { + for , managedId in managedIds { + if (managedId == id) { + exists := true + break + } + } + } + + if (!exists) { + event.EntityList.Push(id) + } + } + } + } + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk new file mode 100644 index 00000000..bde6b479 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk @@ -0,0 +1,8 @@ +class WebServicesEvents { + static HTTP_REQ_ALTER := 0x4200 + static REQUEST_PRESEND := 0x4210 + static CACHED_RESPONSE_CREATED := 0x4215 + static HTTP_RESPONSE_CREATED := 0x4217 + static RESPONSE_ALTER := 0x4220 + static ENTITY_DATA_PARAMS := 0x4225 +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk new file mode 100644 index 00000000..d1c423a4 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk @@ -0,0 +1,41 @@ +class WebServiceAdapterFactory { + container := "" + + __New(container) { + this.container := container + } + + CreateWebServiceAdapter(webService, definition) { + adapterTypes := this.container.GetParameter("web_services.adapter_types") + + if (!definition.Has("adapterType") || !definition["adapterType"]) { + definition["adapterType"] := "json" + } + + if (adapterTypes.Has(definition["adapterType"])) { + defaults := adapterTypes[definition["adapterType"]] + + if (Type(defaults) == "String") { + defaults := Map("class", defaults) + } + + for key, val in defaults { + if (!definition.Has(key)) { + definition[key] := val + } + } + } + + if (!definition.Has("class")) { + throw AppException("Adapter class not known.") + } + + adapterClass := definition["class"] + + if (!HasMethod(%adapterClass%)) { + throw AppException("Adapter class " . adapterClass . " was not found.") + } + + return %adapterClass%.Create(this.container, webService, definition) + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk new file mode 100644 index 00000000..0145f0cb --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk @@ -0,0 +1,32 @@ +class LaunchpadLoginWindow extends FormGuiBase { + entityObj := "" + entityManager := "" + missingFields := Map() + + GetDefaultConfig(container, config) { + defaults := super.GetDefaultConfig(container, config) + defaults["title"] := "Login" + defaults["text"] := "Logging in allows enhanced features such as online backup, restore, personalization, and sharing with the community.`n`nIf you'd like to log in, click the `"Get token`" button to go to the launchpad.games site to retrieve a valid login token and then paste it below." + defaults["buttons"] := "*&Login|&Cancel" + return defaults + } + + Controls() { + super.Controls() + this.Add("ButtonControl", "xs y+m vGetAuthToken w150 h30", "Get Token") + this.AddHeading("Login Token") + this.guiObj.AddEdit("vAuthToken xs y+m r1 w" . this.windowSettings["contentWidth"] . " c" . this.themeObj.GetColor("editText")) + } + + OnGetAuthToken(btn, info) { + Run("https://launchpad.games/profile") + } + + ProcessResult(result, submittedData := "") { + if (result == "Login") { + result := this.guiObj["AuthToken"].Text + } + + return result + } +} diff --git a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk similarity index 56% rename from Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk rename to launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk index ba27dc1c..b790b0dc 100644 --- a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk @@ -1,11 +1,9 @@ class FeedbackWindow extends DialogBox { errorObj := "" notifierObj := "" - apiEndpointUrl := "" __New(container, themeObj, config) { this.notifierObj := container.Get("notifier").notifierObj - this.apiEndpointUrl := container.Get("config.app")["api_endpoint"] super.__New(container, themeObj, config) } @@ -38,20 +36,50 @@ class FeedbackWindow extends DialogBox { SendFeedback() { global appVersion - if (this.apiEndpointUrl) { - endpoint := this.apiEndpointUrl . "/submit-feedback" + filters := "feedback_submission" + operation := "create" + if ( + this.container.Has("web_services.adapter_manager") + && this.container["web_services.adapter_manager"].HasAdapters(filters, operation) + ) { body := Map() body["email"] := this.guiObj["Email"].Text body["version"] := appVersion body["feedback"] := this.guiObj["Feedback"].Text - request := WinHttpReq(endpoint) - response := request.Send("POST", body) - success := !!(request.GetStatusCode() == 200) + results := this.container["web_services.adapter_manager"].AdapterRequest( + Map("data", body), + filters, + operation, + true + ) - notification := success ? "Successfully sent feedback to Volantis Development" : "Failed to send feedback to Volantis Development" - this.notifierObj.Notify(notification, "Feedback Sent", success ? "info" : "error") + success := false + + for adapterId, adapterResult in results { + if (adapterResult) { + success := true + + break + } + } + + message := "" + + if (success) { + message := "Successfully sent feedback" + } else if (results.Count) { + message := "Failed to send feedback" + } else { + message := "No feedback adapters are enabled" + } + + this.notifierObj.Notify( + message, + "Feedback Submission", + success ? "info" : "error" + ) } } } diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk new file mode 100644 index 00000000..873a9596 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -0,0 +1,87 @@ +class ManageWebServicesWindow extends ManageEntitiesWindow { + listViewColumns := Array("SERVICE", "PROVIDER", "USER", "AUTHENTICATED", "AUTO-LOGIN") + + GetListViewData(lv) { + data := Map() + + for key, webService in this.entityMgr { + data[key] := [ + webService["name"], + webService["Provider"]["name"], + "", + webService.Authenticated ? "Yes" : "No", + webService["AutoLogin"] ? "Yes" : "No" + ] + } + + return data + } + + GetEntityIconSrc(entityObj) { + return entityObj["Provider"]["IconSrc"] + } + + GetContextMenuItems(entityObj) { + menuItems := super.GetContextMenuItems(entityObj) + + if (entityObj["Provider"]["SupportsAuthentication"]) { + if (entityObj.Authenticated) { + menuItems.InsertAt(1, Map("label", "&Logout", "name", "WebServiceLogout")) + } else { + menuItems.InsertAt(1, Map("label", "&Login", "name", "WebServiceLogin")) + } + } + + return menuItems + } + + _shouldShowButton(entityObj, buttonName) { + shouldShow := super._shouldShowButton(entityObj, buttonName) + + if (shouldShow && buttonName == "DeleteEntity") { + shouldShow := entityObj.Id != "launchpad_api" + } + + return shouldShow + } + + ProcessContextMenuResult(result, key) { + if (result == "WebServiceLogout") { + this.Logout(key) + } else if (result == "WebServiceLogin") { + this.Login(key) + } else { + super.ProcessContextMenuResult(result, key) + } + } + + Logout(key) { + result := this.entityMgr[key].Logout() + + this.UpdateListView() + + return result + } + + Login(key) { + result := this.entityMgr[key].Login() + + this.UpdateListView() + + return result + } + + ViewEntity(key) { + entityObj := this.entityMgr[key] + } + + AddEntity() { + ; @todo open add wizard + + this.UpdateListView() + } + + DeleteEntity(key) { + entityObj := this.entityMgr[key] + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk new file mode 100644 index 00000000..c4dab738 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk @@ -0,0 +1,51 @@ +class WebServiceAdapterLayerSource extends LayerSourceBase { + adapter := "" + params := "" + + __New(adapter, params := "") { + if (!params) { + params := Map() + } + + this.adapter := adapter + this.params := params + } + + SaveData(data := "") { + if (this.HasData()) { + if (this.adapter.definition["updateAllow"]) { + this.adapter.UpdateData(data, this.params) + } + } else if (this.adapter.definition["createAllow"]) { + this.adapter.CreateData(data, this.params) + } + + return this + } + + LoadData() { + data := "" + + if (this.adapter.definition["readAllow"]) { + data := this.adapter.ReadData(this.params) + } + + if (!data) { + data := Map() + } + + return data + } + + HasData() { + return this.adapter.DataExists(this.params) + } + + DeleteData() { + if (this.adapter.definitions["deleteAllow"]) { + this.adapter.DeleteData(this.params) + } + + return this + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk new file mode 100644 index 00000000..e922134a --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk @@ -0,0 +1,5 @@ +class FileWebServiceAdapter extends WebServiceAdapterBase { + dataClass := "" + + ; @todo Implement file downloading +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk new file mode 100644 index 00000000..5f63b104 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk @@ -0,0 +1,3 @@ +class JsonWebServiceAdapter extends WebServiceAdapterBase { + dataClass := "JsonData" +} \ No newline at end of file diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk new file mode 100644 index 00000000..8e30611e --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk @@ -0,0 +1,352 @@ +class WebServiceAdapterBase { + container := "" + webService := "" + definition := "" + dataClass := "" + merger := "" + operationTypes := ["create", "read", "update", "delete"] + + static ADAPTER_RESULT_DATA := "data" + static ADAPTER_RESULT_HTTP_STATUS := "httpStatus" + static ADAPTER_RESULT_SUCCESS := "success" + + __New(container, merger, webService, definition) { + this.container := container + this.merger := merger + this.webService := webService + + if (!definition) { + definition := Map() + } + + if (!HasBase(definition, Map.Prototype)) { + throw AppException("Definition must be a Map-like object.") + } + + this.definition := this.merger.Merge(this.GetDefaultDefinition(), definition) + } + + static Create(container, webService, definition) { + className := this.Prototype.__Class + + return %className%( + container, + container.Get("merger.list"), + webService, + definition + ) + } + + GetDefaultDefinition() { + return Map( + "dataType", "", + "adapterType", "json", + "requestPath", "", + "requestData", "", + "cacheResponse", true, + "cacheMaxAge", 186400, + "createAllow", false, + "createMethod", "POST", + "createAuth", true, + "readAllow", true, + "readMethod", "GET", + "readAuth", false, + "updateAllow", false, + "updateMethod", "POST", + "updateAuth", true, + "deleteAllow", false, + "deleteMethod", "PUT", + "deleteAuth", true, + "dataMap", Map(), + "dataSelector", "", + "weight", 0, + "entityType", "", + "tags", [], + "requiredParams", [], + ) + } + + SupportsOperation(operation) { + supported := false + + for index, operationType in this.operationTypes { + if (operation == operationType) { + supported := true + + break + } + } + + return supported + } + + SendRequest(operation, params := "") { + if (!this.SupportsOperation(operation)) { + throw AppException("The '" . operation . "' operation is not supported by this data adapter.") + } + + result := "" + data := params.Has("data") ? params["data"] : "" + + if (operation == "create") { + result := this.CreateData(data, params) + } else if (operation == "read") { + result := this.ReadData(params) + } else if (operation == "update") { + result := this.UpdateData(data, params) + } else if (operation == "delete") { + result := this.DeleteData(params) + } + + return result + } + + CreateData(data, params := "") { + if (!this.definition["createAllow"]) { + throw AppException("The 'create' operation is not allowed on this data adapter.") + } + + response := this._request( + params, + this.definition["createMethod"], + data ? data : this._getData(params), + this.definition["createAuth"], + false + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) + ) + } + + _getResultType(params, default) { + resultType := default + + if (params.Has("resultType") && params["resultType"]) { + resultType := params["resultType"] + } + + return resultType + } + + _getResult(params, response, resultType) { + result := "" + + if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_DATA) { + if (response.IsSuccessful()) { + data := response.GetResponseBody() + + if (data) { + result := this._mapData(this._parseData(data, params), params) + } + + } + } else if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_HTTP_STATUS) { + result := response.GetHttpStatusCode() + } else if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) { + result := response.IsSuccessful() + } + + return result + } + + DataExists(params := "") { + if (!this.definition["readAllow"]) { + throw AppException("The 'read' operation is not allowed on this data adapter.") + } + + return this._request( + params, + this.definition["readMethod"], + this._getData(params), + this.definition["readAuth"], + false + ).Send().IsSuccessful() + } + + ReadData(params := "") { + if (!this.definition["readAllow"]) { + throw AppException("The 'read' operation is not allowed on this data adapter.") + } + + response := this._request( + params, + this.definition["readMethod"], + this._getData(params), + this.definition["readAuth"], + this.definition["cacheResponse"] + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_DATA) + ) + } + + UpdateData(data, params := "") { + if (!this.definition["updateAllow"]) { + throw AppException("The 'update' operation is not allowed on this data adapter.") + } + + response := this._request( + params, + this.definition["updateMethod"], + data ? data : this._getData(params), + this.definition["updateAuth"], + false + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) + ) + } + + DeleteData(params := "") { + if (!this.definition["deleteAllow"]) { + throw AppException("The 'delete' operation is not allowed on this data adapter.") + } + + response := this._request( + params, + this.definition["deleteMethod"], + this._getData(params), + this.definition["deleteAuth"], + false + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) + ) + } + + _requestPath(params) { + requestPath := this.definition["requestPath"] + isFound := true + + while isFound { + match := "" + isFound := RegExMatch(requestPath, "({([^}]+)})", &match) + + if (isFound) { + key := match[2] + + replacement := (params && params.Has(key)) + ? params[key] + : "" + + requestPath := StrReplace(requestPath, match[1], replacement) + } + } + + return requestPath + } + + _validateParams(params) { + if (!params) { + params := Map() + } + + valid := true + requiredParams := this.definition["requiredParams"] + + if (requiredParams) { + if (Type(requiredParams) == "String") { + requiredParams := [requiredParams] + } + + for , requiredParam in requiredParams { + if (!params.Has(requiredParam) || !params[requiredParam]) { + valid := false + + break + } + } + } + + return valid + } + + _request(params, method, data, useAuthentication, cacheResponse) { + if (!this._validateParams(params)) { + throw AppException("The data adapter request was called with invalid or missing parameters.") + } + + requestPath := this.definition["requestPath"] + + for key, value in params { + + } + + return this.webService.Request( + this._requestPath(params), + method, + data, + useAuthentication, + cacheResponse + ) + } + + _mapData(data, params, reverse := false) { + if ( + data + && HasBase(data, Map.Prototype) + && this.definition["dataMap"] + && HasBase(this.definition["dataMap"], Map.Prototype) + ) { + for key1, key2 in this.definition["dataMap"] { + oldKey := reverse ? key2 : key1 + newKey := reverse ? key1 : key2 + + if (data.Has(oldKey)) { + data[newKey] := data[oldKey] + data.Delete(oldKey) + } + } + } + } + + _parseData(data, params) { + if (data && this.dataClass) { + dataClass := this.dataClass + data := %dataClass%().FromString(&data) + + if (this.definition["dataSelector"]) { + dataSelector := this.definition["dataSelector"] + + if (Type(dataSelector) == "String") { + dataSelector := StrSplit(dataSelector, ".") + } + + for index, pathPart in dataSelector { + if (data.Has(pathPart)) { + data := data[pathPart] + } else { + data := "" + break + } + } + } + } + + return data + } + + _getData(params, data := "") { + if (!data) { + if (params.Has("data") && params["data"]) { + data := params["data"] + } else if (this.definition["requestData"]) { + data := this.definition["requestData"] + } + } + + return data + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk new file mode 100644 index 00000000..ce99bdce --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk @@ -0,0 +1,178 @@ +class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { + Login(webServiceEnt, retryCount := 0) { + if (retryCount > this.maxRetries) { + this._handleLoginFailure("You have used " . retryCount . " of " . this.maxRetries + 1 . " login attempts. Canceling login.") + return false + } + + authResult := "" + + if (!this._hasRefreshToken(webServiceEnt)) { + authResult := this._reauthenticate(webServiceEnt) + } + + success := false + + if (authResult != "Cancel") { + if (this._hasRefreshToken(webServiceEnt)) { + success := this._refreshAuthentication(webServiceEnt) + } + + if (!success) { + success := this.Login(webServiceEnt, retryCount + 1) + } + } + + return success + } + + Logout(webServiceEnt) { + webServiceEnt + .ResetAuthData() + .DeleteAuthData("auth_token", true) + .DeleteAuthData("refresh_token", true) + .DeleteAuthData("expires", true) + .SetAuthData("authenticated", false, true) + + + return true + } + + RefreshAuthentication(webServiceEnt) { + if (this.NeedsRefresh(webServiceEnt)) { + this.Login(webServiceEnt) + } + } + + AlterRequest(webServiceEnt, httpReqObj) { + bearerToken := webServiceEnt.AuthData["auth_token"] + + if (bearerToken) { + httpReqObj.requestHeaders["Authorization"] := "Bearer " . bearerToken + } + } + + _hasRefreshToken(webServiceEnt) { + return !!(webServiceEnt.AuthData["refresh_token"]) + } + + _reauthenticate(webServiceEnt) { + refreshToken := this._authenticationGui(webServiceEnt) + + if (refreshToken != "Cancel") { + this._setRefreshToken(webServiceEnt, refreshToken) + } + + return refreshToken + } + + _getRefreshToken(webServiceEnt) { + return webServiceEnt.AuthData["refresh_token"] + } + + _setRefreshToken(webServiceEnt, refreshToken) { + webServiceEnt.SetAuthData("refresh_token", refreshToken, true) + } + + _extractAuthData(webServiceEnt, response) { + loginData := response.GetJsonData() + + if (!loginData.Has("authenticated")) { + loginData["authenticated"] := !!(loginData.Has("refresh_token") && loginData["refresh_token"]) + } + + keyMap := Map( + "id_token", "auth_token", + "expires_in", "expires" + ) + + persistentKeys := [ + "user_id", + "refresh_token", + "auth_token", + "access_token", + "authenticated", + "expires" + ] + + expiresInKeys := [ + "expires" + ] + + skipKeys := [] + + for key, val in loginData { + if (keyMap.Has(key)) { + key := keyMap[key] + } + + persist := false + + for , persistKey in persistentKeys { + if (key == persistKey) { + persist := true + break + } + } + + expires := false + + for , expiresKey in expiresInKeys { + if (key == expiresKey) { + val := DateAdd(A_Now, val, "S") + break + } + } + + skip := false + + for , skipKey in skipKeys { + if (key == skipKey) { + skip := true + break + } + } + + if (!skip) { + webServiceEnt.SetAuthData(key, val, persist) + } + } + } + + _refreshAuthentication(webServiceEnt) { + apiKey := webServiceEnt["Provider"]["AppKey"] + refreshToken := webServiceEnt.AuthData["refresh_token"] + refreshUrl := webServiceEnt["Provider"].GetAuthenticationRefreshUrl(Map("key", apiKey)) + response := "" + + if (!apiKey) { + throw OperationFailedException("Missing API key for auth refresh.") + } + + if (!refreshToken) { + throw OperationFailedException("Missing refresh token for auth refresh.") + } + + if (refreshUrl) { + payload := Map( + "grant_type", "refresh_token", + "refresh_token", refreshToken + ) + + response := webServiceEnt.Request(refreshUrl, "POST", payload, false, false).Send() + } + + success := response.IsSuccessful() + + if (response && success) { + this._extractAuthData(webServiceEnt, response) + } else { + url := response.httpReqObj.url.ToString(true) + webServiceEnt.SetAuthData("refresh_token", "", true) + + ; @todo handle common http error codes + } + + return success + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk new file mode 100644 index 00000000..91664f03 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk @@ -0,0 +1,83 @@ +class WebServiceAuthenticatorBase { + refreshThresholdSeconds := 600 + authenticatedStateKey := "authenticated" + expiresStateKey := "expires" + maxRetries := 3 + guiMgr := "" + + __New(guiMgr) { + this.guiMgr := guiMgr + } + + Login(webServiceEnt, retryCount := 0) { + + } + + Logout(webServiceEnt) { + + } + + RefreshAuthentication(webServiceEnt) { + + } + + AlterRequest(webServiceEnt, httpReqObj) { + + } + + IsAuthenticated(webServiceEnt) { + auth := webServiceEnt.AuthData[this.authenticatedStateKey] + expired := this.AuthenticationIsExpired(webServiceEnt) + + return auth && !expired + + } + + NeedsRefresh(webServiceEnt) { + needsRefresh := false + + if (this.IsAuthenticated(webServiceEnt)) { + expires := webServiceEnt.AuthData[this.expiresStateKey] + + if (expires) { + diff := DateDiff(A_Now, expires, "S") + needsRefresh := (diff >= (0 - this.refreshThresholdSeconds)) + } else { + needsRefresh := true + } + } else { + needsRefresh := true + } + + return needsRefresh + } + + AuthenticationIsExpired(webServiceEnt) { + expired := true + + if (webServiceEnt.AuthData[this.authenticatedStateKey] && webServiceEnt.AuthData[this.expiresStateKey]) { + expired := (DateDiff(A_Now, webServiceEnt.AuthData["expires"], "S") >= 0) + } + + return expired + } + + _authenticationGui(webServiceEnt) { + loginWindowGui := webServiceEnt["Provider"]["LoginWindow"] + result := "" + + if (loginWindowGui) { + result := this.guiMgr.Dialog(Map("type", loginWindowGui)) + } + + return result + } + + _handleLoginFailure(message) { + this.guiMgr.Dialog(Map( + "title", "Login Failure", + "text", message, + "buttons", "*&OK" + )) + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk new file mode 100644 index 00000000..a723ff4c --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk @@ -0,0 +1,3 @@ +class BasicWebServiceRequest extends WebServiceRequestBase { + +} \ No newline at end of file diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk new file mode 100644 index 00000000..e89cfce5 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -0,0 +1,201 @@ +class WebServiceRequestBase { + eventMgr := "" + webServiceEnt := "" + cacheObj := "" + path := "" + method := "" + data := "" + useAuthentication := false + httpReqObj := "" + responseObj := "" + cacheResponse := true + _url := "" + + Url { + get => this._getUrl() + } + + Response { + get => this.responseObj + } + + __New(eventMgr, webServiceEnt, cacheObj, method := "", path := "", data := "", useAuthentication := false, cacheResponse := true) { + this.eventMgr := eventMgr + this.webServiceEnt := webServiceEnt + this.cacheObj := cacheObj + + if (HasBase(path, UrlObj.Prototype)) { + this._url := path + path := this._url.Path + } + + if (!method) { + method := "GET" + } + + this.method := method + + if (path) { + this.path := path + } + + if (data) { + this.data := data + } + + this.useAuthentication := useAuthentication + this.cacheResponse := cacheResponse + } + + GetPath() { + return this.path + } + + SetPath(path := "") { + this.path := path + + if (this._url) { + this._url.Path := this.webServiceEnt["Provider"].Path(path) + } + + return this + } + + GetMethod() { + return this.method + } + + SetMethod(method := "GET") { + this.method := method + + return this + } + + GetData() { + return this.data + } + + SetData(data := "", clearCache := false) { + this.data := data + + if (clearCache) { + this.cacheObj.RemoveItem(this.GetPath()) + } + + return this + } + + GetUseAuthentication() { + return this.useAuthentication + } + + SetUseAuthentication(useAuthentication := false) { + this.useAuthentication := useAuthentication + + return this + } + + GetHttpReq() { + if (!this.httpReqObj) { + this.httpReqObj := WinHttpReq(this.Url) + } + + return this.httpReqObj + } + + SetHttpReq(httpReqObj := "") { + this.httpReqObj := httpReqObj + } + + _getUrl() { + if (!this._url) { + this._url := this.webServiceEnt["Provider"].Url(this.path) + } + + return this._url + } + + Send(resend := false) { + if (resend || !this.responseObj) { + if (this.RequestIsCached()) { + this.responseObj := this._createCachedResponse() + } else { + httpReqObj := this.GetHttpReq() + + if (this.GetUseAuthentication() && this.webServiceEnt["Provider"].Has("Authenticator", false)) { + authenticator := this.webServiceEnt["Provider"]["Authenticator"] + + if (authenticator.NeedsRefresh(this.webServiceEnt)) { + authenticator.RefreshAuthentication(this.webServiceEnt) + } + + authenticator.AlterRequest(this.webServiceEnt, httpReqObj) + } + + if (!this.cacheResponse) { + httpReqObj.requestHeaders["Cache-Control"] := "no-cache" + } + + event := WebServicesRequestEvent(WebServicesEvents.HTTP_REQ_ALTER, this) + this.eventMgr.DispatchEvent(event) + + event := WebServicesRequestEvent(WebServicesEvents.REQUEST_PRESEND, this) + this.eventMgr.DispatchEvent(event) + + httpReqObj.Send(this.GetMethod(), this.GetData()) + this.responseObj := this._createHttpReqResponse() + this._cacheResponse() + } + + event := WebServicesResponseEvent(WebServicesEvents.RESPONSE_ALTER, this, this.responseObj) + this.eventMgr.DispatchEvent(event) + + this.responseObj := event.Response + } + + return this.responseObj + } + + RequestIsCached() { + path := this.GetPath() + + return (this.cacheObj.ItemExists(path) && !this.cacheObj.ItemNeedsUpdate(path)) + } + + _createCachedResponse() { + response := CachedWebServiceResponse(this.webServiceEnt, this) + + event := WebServicesResponseEvent(WebServicesEvents.CACHED_RESPONSE_CREATED, this, response) + this.eventMgr.DispatchEvent(event) + + return event.Response + } + + _createHttpReqResponse() { + response := HttpReqWebServiceResponse(this.webServiceEnt, this) + + event := WebServicesResponseEvent(WebServicesEvents.HTTP_RESPONSE_CREATED, this, response) + this.eventMgr.DispatchEvent(event) + + return event.Response + } + + _cacheResponse() { + if (this.responseObj && this.cacheResponse) { + path := this.GetPath() + + if (this.responseObj.IsSuccessful()) { + body := this.responseObj.GetResponseBody() + + if (body) { + this.cacheObj.WriteItem(path, body, this.responseObj.GetHttpStatusCode()) + } else { + ; Response is empty, delete any existing cache for this item + this.cacheObj.RemoveItem(path) + } + } else if (this.responseObj.IsNotFound()) { + this.cacheObj.SetNotFound(path) + } + } + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk new file mode 100644 index 00000000..551b7efa --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk @@ -0,0 +1,30 @@ +class CachedWebServiceResponse extends WebServiceResponseBase { + cacheObj := "" + + __New(webServiceEnt, webServiceReq) { + this.cacheObj := webServiceEnt.cacheObj + + super.__New(webServiceEnt, webServiceReq) + } + + GetHttpStatusCode() { + responseCode := 0 + + if (this.cacheObj) { + responseCode := this.cacheObj.GetResponseCode(this.webServiceReq.GetPath()) + } + + return responseCode + } + + GetResponseBody() { + body := "" + path := this.webServiceReq.GetPath() + + if (this.cacheObj.ItemExists(path)) { + body := this.cacheObj.ReadItem(path) + } + + return body + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk new file mode 100644 index 00000000..d7857400 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk @@ -0,0 +1,17 @@ +class HttpReqWebServiceResponse extends WebServiceResponseBase { + httpReqObj := "" + + __New(webServiceEnt, webServiceReq) { + this.httpReqObj := webServiceReq.GetHttpReq() + + super.__New(webServiceEnt, webServiceReq) + } + + GetHttpStatusCode() { + return this.httpReqObj.GetStatusCode() + } + + GetResponseBody() { + return Trim(this.httpReqObj.GetResponseData()) + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk new file mode 100644 index 00000000..e7413bfa --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk @@ -0,0 +1,57 @@ +class WebServiceResponseBase { + webServiceEnt := "" + webServiceReq := "" + successCodes := [200] + notFoundCodes := [404] + + __New(webServiceEnt, webServiceReq) { + this.webServiceEnt := webServiceEnt + this.webServiceReq := webServiceReq + } + + GetHttpStatusCode() { + return "" + } + + GetResponseBody() { + return "" + } + + GetJsonData() { + body := this.GetResponseBody() + + if (!body) { + body := "{}" + } + + return JsonData().FromString(&body) + } + + IsSuccessful() { + httpCode := this.GetHttpStatusCode() + success := false + + for , successCode in this.successCodes { + if (httpCode == successCode) { + success := true + break + } + } + + return success + } + + IsNotFound() { + httpCode := this.GetHttpStatusCode() + notFound := false + + for , notFoundCode in this.notFoundCodes { + if (httpCode == notFoundCode) { + notFound := true + break + } + } + + return notFound + } +} diff --git a/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServices.module.json b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServices.module.json new file mode 100644 index 00000000..1f19e86b --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -0,0 +1,123 @@ +{ + "module": { + "name": "Web Services", + "type": "AppModule", + "icon": "", + "category": "Web Services", + "tags": ["Launchpad", "LaunchpadBuilder"], + "description": "Enables Launchpad to connect to and authenticate with remove services on the Internet for additional functionality.", + "author": { + "name": "Ben McClure, Volantis Dev", + "url": "https://volantisdev.com" + }, + "website": "https://launchpad.games", + "version": "{{VERSION}}", + "appVersion": "", + "dependencies": [] + }, + "parameters": { + "config.web_services_file": "@@{data_dir}\\WebServices.json", + "config.web_services_state_path": "@@{data_dir}\\WebServicesState.json", + "config.web_services_view_mode": "Report", + "entity_type.web_service": { + "name_singular": "Web Service", + "name_plural": "Web Services", + "entity_class": "WebServiceEntity", + "storage_config_storage_parent_key": "WebServices", + "storage_config_path_parameter": "config.web_services_file", + "manager_view_mode_parameter": "config.web_services_view_mode", + "definition_loader_parameter_key": "web_services.services", + "default_icon": "webhook", + "allow_add": true, + "allow_edit": true, + "allow_delete": true, + "manager_gui": "ManageWebServicesWindow", + "manager_link_in_tools_menu": true + }, + "entity_type.web_service_provider": { + "name_singular": "Service Provider", + "name_plural": "Service Providers", + "entity_class": "WebServiceProviderEntity", + "definition_loader_class": "ParameterEntityDefinitionLoader", + "definition_loader_parameter_key": "web_services.providers", + "storage_type": "runtime" + }, + "web_services.data_types.account_info": { + "name": "Account Info", + "description": "Account information related to an authentication session." + }, + "web_services.data_types.error_submission": { + "name": "Error Submission", + "description": "An endpoint to submit application errors to." + }, + "web_services.data_types.feedback_submission": { + "name": "Feedback Submission", + "description": "An endpoint to submit feedback to." + }, + "web_services.data_types.game_submission": { + "name": "Game Submission", + "description": "An endpoint to game launcher data to for sharing with the community." + }, + "web_services.data_types.release_info": { + "name": "Release Info", + "description": "Information about the latest (or any) version of Launchpad" + }, + "web_services.data_types.entity_list": { + "name": "Entity List", + "description": "A listing of entities from a web service." + }, + "web_services.data_types.entity_lookup": { + "name": "Entity Lookup", + "description": "Searches for the ID of a remote entity from the provided data." + }, + "web_services.data_types.entity_data": { + "name": "Entity Data", + "description": "Data to be imported into an entity within the application." + }, + "web_services.data_types.availability_check": { + "name": "Availability check", + "description": "Simply checks for any response from the web service." + }, + "web_services.adapter_types.json": { + "class": "JsonWebServiceAdapter" + }, + "web_services.adapter_types.file": { + "class": "FileWebServiceAdapter" + } + }, + "services": { + "cache.web_services": { + "class": "FileCache", + "arguments": ["@{App}", "@cache_state.web_services", "@@config.cache_dir", "WebServices"] + }, + "cache_state.web_services": { + "class": "CacheState", + "arguments": ["@{App}", "@@config.cache_dir", "WebServices.json"] + }, + "event_subscriber.web_services": { + "class": "WebServicesEventSubscriber", + "arguments": ["@{}", "@web_services.adapter_manager"], + "tags": ["event_subscriber"] + }, + "state.web_services": { + "class": "JsonState", + "arguments": ["@{App}", "@@config.web_services_state_path"] + }, + "state.web_services_tmp": { + "class": "ParameterState", + "arguments": ["@{App}", "web_services.state.tmp"] + }, + "web_services_authenticator.jwt": { + "class": "JwtWebServiceAuthenticator", + "arguments": ["@manager.gui"] + }, + "web_services.adapter_factory": { + "class": "WebServiceAdapterFactory", + "arguments": ["@{}"] + }, + "web_services.adapter_manager": { + "class": "WebServiceAdapterManager", + "arguments": ["@{}", "web_services.adapters.", "@web_services.adapter_factory", "@manager.entity_type", "@manager.event"] + } + } +} diff --git a/Lib/Shared/Vendor/Gdip_All.ahk b/launchpad_ahk/Lib/Shared/Vendor/Gdip_All.ahk similarity index 100% rename from Lib/Shared/Vendor/Gdip_All.ahk rename to launchpad_ahk/Lib/Shared/Vendor/Gdip_All.ahk diff --git a/Lib/Shared/Vendor/LV_Constants.ahk b/launchpad_ahk/Lib/Shared/Vendor/LV_Constants.ahk similarity index 100% rename from Lib/Shared/Vendor/LV_Constants.ahk rename to launchpad_ahk/Lib/Shared/Vendor/LV_Constants.ahk diff --git a/Lib/Shared/Volantis.App/App/AppBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/App/AppBase.ahk similarity index 68% rename from Lib/Shared/Volantis.App/App/AppBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/App/AppBase.ahk index 14051eb9..307ceb47 100644 --- a/Lib/Shared/Volantis.App/App/AppBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/App/AppBase.ahk @@ -1,5 +1,4 @@ class AppBase { - developer := "" versionStr := "" appName := "" appDir := "" @@ -8,9 +7,9 @@ class AppBase { configObj := "" stateObj := "" serviceContainerObj := "" - customTrayMenu := false themeReady := false startConfig := "" + isSetup := false static Instance := "" @@ -25,11 +24,20 @@ class AppBase { } Config { - get => this.Service("config.app") + get => this["config.app"] } State { - get => this.Service("state.app") + get => this["state.app"] + } + + Parameter[key] { + get => this.Services.GetParameter(key) + set => this.Services.SetParameter(key, value) + } + + __Item[serviceId] { + get => this.Service(serviceId) } __New(config := "", autoStart := true) { @@ -57,6 +65,16 @@ class AppBase { "app_dir", this.appDir, "data_dir", this.dataDir, "tmp_dir", this.tmpDir, + "app.website_url", "", + "app.custom_tray_menu", false, + "app.developer", "", + "app.has_settings", false, + "app.settings_window", "", + "app.show_restart_menu_item", true, + "app.supports_update_check", false, + "app.show_about_menu_item", false, + "app.about_window", "", + "app.show_website_menu_item", false, "resources_dir", "@@{app_dir}\Resources", "config_path", "@@{app_dir}\" . this.appName . ".json", "config_key", "config", @@ -374,10 +392,6 @@ class AppBase { config["appName"] := appBaseName } - if (!config.Has("developer")) { - config["developer"] := "" - } - if (!config.Has("appDir") || !config["appDir"]) { config["appDir"] := A_ScriptDir } @@ -396,7 +410,6 @@ class AppBase { this.appName := config["appName"] this.versionStr := config["version"] - this.developer := config["developer"] this.appDir := config["appDir"] this.tmpDir := config["tmpDir"] this.dataDir := config["dataDir"] @@ -412,26 +425,26 @@ class AppBase { this.LoadServices(config) if (!config.Has("useShell") || config("useShell")) { - this.Service("shell") + this["shell"] } OnError(ObjBindMethod(this, "OnException")) event := AppRunEvent(Events.APP_PRE_INITIALIZE, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) this.InitializeApp(config) event := AppRunEvent(Events.APP_POST_INITIALIZE, this, config) - this.Service("manager.event").DispatchEvent(event) - - event := AppRunEvent(Events.APP_POST_STARTUP, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) event := AppRunEvent(Events.APP_PRE_RUN, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) this.RunApp(config) + + event := AppRunEvent(Events.APP_POST_STARTUP, this, config) + this["manager.event"].DispatchEvent(event) } LoadServices(config) { @@ -441,18 +454,18 @@ class AppBase { )) this.Services.LoadDefinitions(MapDefinitionLoader(config)) - sdFactory := this.Service("factory.structured_data") - serviceFile := this.Services.GetParameter("service_files.app") + sdFactory := this["factory.structured_data"] + serviceFile := this.Parameter["service_files.app"] if (FileExist(serviceFile)) { this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, serviceFile)) } - this.Service("config.app") + this["config.app"] this.InitializeTheme() this.InitializeModules(config) - for index, moduleServiceFile in this.Service("manager.module").GetModuleServiceFiles() { + for index, moduleServiceFile in this["manager.module"].GetModuleServiceFiles() { if (FileExist(moduleServiceFile)) { this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, moduleServiceFile)) } else { @@ -461,49 +474,49 @@ class AppBase { } ; Reload user config files to ensure they are the active values - this.Service("config.app").LoadConfig(true) + this["config.app"].LoadConfig(true) ; Register early event subscribers (e.g. modules) - this.Service("manager.event").RegisterServiceSubscribers(this.Services) + this["manager.event"].RegisterServiceSubscribers(this.Services) - this.Service("manager.event").Register(Events.APP_SERVICES_LOADED, "AppServices", ObjBindMethod(this, "OnServicesLoaded")) + this["manager.event"].Register(Events.APP_SERVICES_LOADED, "AppServices", ObjBindMethod(this, "OnServicesLoaded")) event := ServiceDefinitionsEvent(Events.APP_SERVICE_DEFINITIONS, "", "", config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) if (event.Services.Count || event.Parameters.Count) { this.Services.LoadDefinitions(SimpleDefinitionLoader(event.Services, event.Parameters)) } - serviceFile := this.Services.GetParameter("service_files.user") + serviceFile := this.Parameter["service_files.user"] if (FileExist(serviceFile)) { this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, serviceFile)) } ; Register any missing late-loading event subscribers - this.Service("manager.event").RegisterServiceSubscribers(this.Services) + this["manager.event"].RegisterServiceSubscribers(this.Services) event := AppRunEvent(Events.APP_SERVICES_LOADED, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) } OnServicesLoaded(event, extra, eventName, hwnd) { - this.Service("manager.cache") - this.Service("manager.entity_type").All() - this.Service("manager.installer").RunInstallers(InstallerBase.INSTALLER_TYPE_REQUIREMENT) + this["manager.cache"] + this["manager.entity_type"].All() + this["manager.installer"].RunInstallers(InstallerBase.INSTALLER_TYPE_REQUIREMENT) } InitializeModules(config) { - includeFiles := this.Services.GetParameter("include_files") - updated := this.Service("manager.module").UpdateModuleIncludes(includeFiles["modules"], includeFiles["module_tests"]) + includeFiles := this.Parameter["include_files"] + updated := this["manager.module"].UpdateModuleIncludes(includeFiles["modules"], includeFiles["module_tests"]) if (updated) { message := A_IsCompiled ? "Your modules have been updated. Currently, you must recompile " this.appName . " yourself for the changes to take effect. Would you like to exit now (highly recommended)?" : "Your modules have been updated, and " this.appName . " must be restarted for the changes to take effect. Would you like to restart now?" - response := this.app.Service("manager.gui").Dialog(Map( + response := this.app["manager.gui"].Dialog(Map( "title", "Module Includes Updated", "text", message )) @@ -519,16 +532,16 @@ class AppBase { } InitializeTheme() { - this.Service("gdip", "manager.gui", "manager.theme") + this[["gdip", "manager.gui", "manager.theme"]] this.themeReady := true } InitializeApp(config) { A_AllowMainWindow := false - if (this.customTrayMenu) { + if (this.Parameter["app.custom_tray_menu"]) { A_TrayMenu.Delete() - this.Service("manager.event").Register(Events.AHK_NOTIFYICON, "TrayClick", ObjBindMethod(this, "OnTrayIconRightClick"), 1) + this["manager.event"].Register(Events.AHK_NOTIFYICON, "TrayClick", ObjBindMethod(this, "OnTrayIconRightClick"), 1) } } @@ -537,19 +550,19 @@ class AppBase { this.CheckForUpdates(false) } - if (this.Services.HasParameter("config_path") && !FileExist(this.Parameter("config_path"))) { + if (this.Services.HasParameter("config_path") && !FileExist(this.Parameter["config_path"])) { this.InitialSetup(config) } } OpenApp() { - mainWin := this.Parameter("config.main_window") + mainWin := this.Parameter["config.main_window"] if (mainWin) { - if (this.Service("manager.gui").Has(mainWin)) { - WinActivate("ahk_id " . this.Service("manager.gui")[mainWin].GetHwnd()) + if (this["manager.gui"].Has(mainWin)) { + WinActivate("ahk_id " . this["manager.gui"][mainWin].GetHwnd()) } else { - this.Service("manager.gui").OpenWindow(Map( + this["manager.gui"].OpenWindow(Map( "type", mainWin, "title", this.appName )) @@ -559,7 +572,7 @@ class AppBase { ExitApp() { event := AppRunEvent(Events.APP_SHUTDOWN, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) if (this.Services.Has("gdip")) { Gdip_Shutdown(this.Services["gdip"].GetHandle()) @@ -570,7 +583,7 @@ class AppBase { RestartApp() { event := AppRunEvent(Events.APP_RESTART, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) if (this.Services.Has("gdip")) { Gdip_Shutdown(this.Services["gdip"].GetHandle()) @@ -586,7 +599,7 @@ class AppBase { throw AppException("The shell is disabled, so shell commands cannot currently be run.") } - result := this.Service("shell").Exec(A_ComSpec . " /C " . command).StdOut.ReadAll() + result := this["shell"].Exec(A_ComSpec . " /C " . command).StdOut.ReadAll() if (trimOutput) { result := Trim(result, " `r`n`t") @@ -606,12 +619,12 @@ class AppBase { } for index, arrName in name { - results[arrName] := this.Service(arrName) + results[arrName] := this[arrName] } if (params && params.Length) { for index, arrName in params { - results[arrName] := this.Service(arrName) + results[arrName] := this[arrName] } } @@ -621,14 +634,15 @@ class AppBase { return this.Services.Get(name) } - Parameter(name) { - return this.Services.GetParameter(name) - } - OnException(e, mode) { extra := (e.HasProp("Extra") && e.Extra != "") ? "`n`nExtra information:`n" . e.Extra : "" occurredIn := e.What ? " in " . e.What : "" - developer := this.developer ? this.developer : "the developer(s)" + + developer := this.Parameter["app.developer"] + + if (!developer) { + developer := "the developer(s)" + } errorText := this.appName . " has experienced an unhandled exception. You can find the details below." errorText .= "`n`n" . e.Message . extra @@ -646,7 +660,7 @@ class AppBase { } if (this.Services.Has("logger")) { - this.Service("logger").Error(errorText) + this["logger"].Error(errorText) } errorText .= "`n" @@ -659,7 +673,7 @@ class AppBase { if (this.themeReady) { btns := allowContinue ? "*&Continue|&Reload|&Exit" : "*&Reload|&Exit" - this.Service("manager.gui").Dialog(Map( + this["manager.gui"].Dialog(Map( "type", "ErrorDialog", "title", "Unhandled Exception", "text", errorText, @@ -683,7 +697,7 @@ class AppBase { OnTrayIconRightClick(wParam, lParam, msg, hwnd) { if (lParam == Events.MOUSE_RIGHT_UP) { - if (this.customTrayMenu) { + if (this.Parameter["app.custom_tray_menu"]) { this.ShowTrayMenu() return 0 } @@ -691,11 +705,7 @@ class AppBase { } InitialSetup(config) { - ; Override this to set config values as needed - } - - CheckForUpdates(notify := true) { - ; Optional method to override + this.isSetup := true } ShowTrayMenu() { @@ -706,7 +716,7 @@ class AppBase { menuItems.Push(Map("label", "Restart", "name", "RestartApp")) menuItems.Push(Map("label", "Exit", "name", "ExitApp")) - result := this.Service("manager.gui").Menu(menuItems, this) + result := this["manager.gui"].Menu(menuItems, this) this.HandleTrayMenuClick(result) } @@ -730,4 +740,238 @@ class AppBase { this.ExitApp() super.__Delete() } + + MainMenu(parentGui, parentCtl, showOpenAppItem := false) { + menuItems := this.GetMainMenuItems(showOpenAppItem) + + if (menuItems.Length) { + this.HandleMainMenuClick(this["manager.gui"].Menu( + menuItems, + parentGui, + parentCtl + )) + } + } + + GetMainMenuItems(showOpenAppItem := false) { + menuItems := [] + menuItems := this.AddMainMenuEarlyItems(menuItems, showOpenAppItem) + + if (menuItems.Length) { + menuItems.Push("") + } + + length := menuItems.Length + + toolsItems := this.GetToolsMenuItems() + + if (toolsItems.Length) { + menuItems.Push(Map("label", "&Tools", "name", "ToolsMenu", "childItems", toolsItems)) + } + + aboutItems := this.GetAboutMenuItems() + + if (aboutItems.Length) { + menuItems.Push(Map("label", "&About", "name", "About", "childItems", aboutItems)) + } + + menuItems := this.AddMainMenuMiddleItems(menuItems) + + if (menuItems.Length > length) { + menuItems.Push("") + } + + length := menuItems.Length + menuItems := this.AddMainMenuLateItems(menuItems) + + if (menuItems.Length > length) { + menuItems.Push("") + } + + if (this.Parameter["app.show_restart_menu_item"]) { + menuItems.Push(Map("label", "&Restart", "name", "Reload")) + } + + menuItems.Push(Map("label", "E&xit", "name", "Exit")) + + event := MenuItemsEvent(Events.APP_MENU_ITEMS_ALTER, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + + return menuItems + } + + GetAboutMenuItems() { + aboutItems := [] + + if (this.Parameter["app.show_about_menu_item"]) { + aboutItems.Push(Map("label", "&About " . this.appName, "name", "About")) + } + + if (this.Parameter["app.show_website_menu_item"]) { + aboutItems.Push(Map("label", "Open &Website", "name", "OpenWebsite")) + } + + event := MenuItemsEvent(Events.APP_MENU_ABOUT_ITEMS_ALTER, aboutItems) + this.Dispatch(event) + aboutItems := event.MenuItems + + return aboutItems + } + + GetToolsMenuItems() { + toolsItems := this.AddEntityManagerMenuLinks([]) + event := MenuItemsEvent(Events.APP_MENU_TOOLS_ITEMS_ALTER, toolsItems) + this.Dispatch(event) + toolsItems := event.MenuItems + + return toolsItems + } + + AddMainMenuEarlyItems(menuItems, showOpenAppItem := false) { + if (showOpenAppItem) { + menuItems.Push(Map("label", "Open " . this.appName, "name", "OpenApp")) + menuItems.Push("") + } + + event := MenuItemsEvent(Events.APP_MENU_ITEMS_EARLY, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + + return menuItems + } + + AddMainMenuMiddleItems(menuItems) { + event := MenuItemsEvent(Events.APP_MENU_ITEMS_MIDDLE, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + return menuItems + } + + AddMainMenuLateItems(menuItems) { + if (this.Parameter["app.has_settings"]) { + menuItems.Push(Map("label", "&Settings", "name", "Settings")) + } + + if (this.Parameter["app.supports_update_check"]) { + menuItems.Push(Map("label", "Check for &Updates", "name", "CheckForUpdates")) + } + + event := MenuItemsEvent(Events.APP_MENU_ITEMS_LATE, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + + return menuItems + } + + AddEntityManagerMenuLinks(menuItems) { + menuEntityTypes := this._getToolsMenuEntityTypes() + + for key, entityType in menuEntityTypes { + menuLinkText := entityType.definition["manager_menu_link_text"] + + if (!menuLinkText) { + menuLinkText := "&" . entityType.definition["name_plural"] + } + + menuItems.Push(Map("label", menuLinkText, "name", "manage_" . key)) + } + + return menuItems + } + + Dispatch(event) { + this["manager.event"].DispatchEvent(event) + } + + _getToolsMenuEntityTypes() { + entityTypes := Map() + + for key, entityType in this["manager.entity_type"] { + if (entityType.definition["manager_link_in_tools_menu"]) { + entityTypes[key] := entityType + } + } + + return entityTypes + } + + HandleMainMenuClick(result) { + event := MenuResultEvent(Events.APP_MENU_PROCESS_RESULT, result) + this.Dispatch(event) + result := event.Result + + if (!event.IsFinished) { + if (result == "About") { + this.ShowAbout() + } else if (result == "OpenWebsite") { + this.OpenWebsite() + } else if (result == "Settings") { + this.ShowSettings() + } else if (result == "CheckForUpdates") { + this.CheckForUpdates() + } else if (result == "Reload") { + this.restartApp() + } else if (result == "Exit") { + this.ExitApp() + } else { + for key, entityType in this._getToolsMenuEntityTypes() { + if (result == "manage_" . key) { + this["entity_type." . key].OpenManageWindow() + break + } + } + } + } + + return result + } + + ShowSettings() { + windowName := this.Parameter["app.settings_window"] + + if (windowName) { + this["manager.gui"].Dialog(Map("type", windowName, "unique", false)) + } + } + + ShowAbout() { + windowName := this.Parameter["app.about_window"] + + if (windowName) { + this["manager.gui"].Dialog(Map("type", windowName)) + } + } + + OpenWebsite() { + websiteUrl := this.Parameter["app.website_url"] + + if (websiteUrl) { + Run(websiteUrl) + } + } + + CheckForUpdates(notify := true) { + if (this.Parameter["app.supports_update_check"]) { + updateAvailable := false + + event := ReleaseInfoEvent(Events.APP_GET_RELEASE_INFO, this) + this.Dispatch(event) + releaseInfo := event.ReleaseInfo + + if ( + releaseInfo + && releaseInfo.Has("version") + && releaseInfo["version"] + && this["version_checker"].VersionIsOutdated(releaseInfo["version"], this.Version) + ) { + updateAvailable := true + this["manager.gui"].Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo) + } + + if (!updateAvailable && notify) { + this["notifier"].Info("You're running the latest version of " . this.appName . ". Shiny!") + } + } + } } diff --git a/Lib/Shared/Volantis.App/App/AppBase.test.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/App/AppBase.test.ahk similarity index 100% rename from Lib/Shared/Volantis.App/App/AppBase.test.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/App/AppBase.test.ahk diff --git a/Lib/Shared/Volantis.App/App/TestApp.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/App/TestApp.ahk similarity index 100% rename from Lib/Shared/Volantis.App/App/TestApp.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/App/TestApp.ahk diff --git a/Lib/Shared/Volantis.App/App/TestAppBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/App/TestAppBase.ahk similarity index 68% rename from Lib/Shared/Volantis.App/App/TestAppBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/App/TestAppBase.ahk index a5263d94..25120777 100644 --- a/Lib/Shared/Volantis.App/App/TestAppBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/App/TestAppBase.ahk @@ -1,13 +1,13 @@ class TestAppBase extends AppBase { ExitApp() { event := AppRunEvent(Events.APP_SHUTDOWN, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) ; Don't actually exit } RestartApp() { event := AppRunEvent(Events.APP_SHUTDOWN, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) ; Don't actually restart } } diff --git a/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk similarity index 92% rename from Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk index a9406e35..6f85723d 100644 --- a/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk @@ -40,7 +40,7 @@ class BulkOperationBase { } if (this.app.Services.Has("logger")) { - this.app.Service("logger").Debug(Type(this) . ": Starting bulk operation...") + this.app["logger"].Debug(Type(this) . ": Starting bulk operation...") } this.running := true @@ -56,7 +56,7 @@ class BulkOperationBase { LogResults() { if (this.app.Services.Has("logger")) { - this.app.Service("logger").Info(Type(this) . " Results: " . this.GetResultMessage()) + this.app["logger"].Info(Type(this) . " Results: " . this.GetResultMessage()) } } @@ -89,7 +89,7 @@ class BulkOperationBase { ownerOrParent := this.parent } - this.progress := this.app.Service("manager.gui").OpenWindow(Map( + this.progress := this.app["manager.gui"].OpenWindow(Map( "type", "ProgressIndicator", "title", this.progressTitle, "text", this.progressText, @@ -115,7 +115,7 @@ class BulkOperationBase { Notify() { if (this.shouldNotify && this.app.Services.Has("notifier")) { - this.app.Service("notifier").Info(this.GetResultMessage()) + this.app["notifier"].Info(this.GetResultMessage()) } } diff --git a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk similarity index 96% rename from Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk index 6c4d0aad..b093857a 100644 --- a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk @@ -17,7 +17,7 @@ class InstallOp extends BulkOperationBase { for index, name in this.installers { name := "installer." . name - installer := this.app.Service(name) + installer := this.app[name] if (!HasBase(installer, InstallerBase.Prototype)) { throw AppException("Provided installer is not valid: " . name) diff --git a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/UpdateOp.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/InstallOp/UpdateOp.ahk similarity index 100% rename from Lib/Shared/Volantis.App/BulkOperation/InstallOp/UpdateOp.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/InstallOp/UpdateOp.ahk diff --git a/Lib/Shared/Volantis.App/BulkOperation/OpError/BasicOpError.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpError/BasicOpError.ahk similarity index 100% rename from Lib/Shared/Volantis.App/BulkOperation/OpError/BasicOpError.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpError/BasicOpError.ahk diff --git a/Lib/Shared/Volantis.App/BulkOperation/OpError/OpErrorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpError/OpErrorBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/BulkOperation/OpError/OpErrorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpError/OpErrorBase.ahk diff --git a/Lib/Shared/Volantis.App/BulkOperation/OpStatus/BasicOpStatus.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpStatus/BasicOpStatus.ahk similarity index 100% rename from Lib/Shared/Volantis.App/BulkOperation/OpStatus/BasicOpStatus.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpStatus/BasicOpStatus.ahk diff --git a/Lib/Shared/Volantis.App/BulkOperation/OpStatus/OpStatusBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpStatus/OpStatusBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/BulkOperation/OpStatus/OpStatusBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/BulkOperation/OpStatus/OpStatusBase.ahk diff --git a/Lib/Shared/Volantis.App/Cache/CacheBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Cache/CacheBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Cache/CacheBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Cache/CacheBase.ahk diff --git a/Lib/Shared/Volantis.App/Cache/FileCache.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Cache/FileCache.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Cache/FileCache.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Cache/FileCache.ahk diff --git a/Lib/Shared/Volantis.App/Cache/ObjectCache.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Cache/ObjectCache.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Cache/ObjectCache.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Cache/ObjectCache.ahk diff --git a/Lib/Shared/Volantis.App/Config/AppConfig.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Config/AppConfig.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Config/AppConfig.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Config/AppConfig.ahk diff --git a/Lib/Shared/Volantis.App/Container/ServiceComponentContainer.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Container/ServiceComponentContainer.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Container/ServiceComponentContainer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Container/ServiceComponentContainer.ahk diff --git a/Lib/Shared/Volantis.App/Container/WindowContainer.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Container/WindowContainer.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Container/WindowContainer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Container/WindowContainer.ahk diff --git a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk similarity index 80% rename from Lib/Shared/Volantis.App/Entity/BackupEntity.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk index 9ff0d0b0..9dfc0cf0 100644 --- a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk @@ -1,11 +1,12 @@ -class BackupEntity extends AppEntityBase { +class BackupEntity extends FieldableEntity { backup := "" - __New(app, key, config, parentEntity := "", requiredConfigKeys := "") { - super.__New(app, key, config, parentEntity, requiredConfigKeys) - backupClass := config.Has("BackupClass") ? config["BackupClass"] : "FileBackup" + SetupEntity() { + super.SetupEntity() if (!this.backup) { + backupClass := this.config.Has("BackupClass") ? this.config["BackupClass"] : "FileBackup" + this.CreateBackupObject(backupClass) } } @@ -13,10 +14,6 @@ class BackupEntity extends AppEntityBase { BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() - if (definitions.Has("DataSourceKeys")) { - definitions["DataSourceKeys"]["default"] := [] - } - definitions["IsEditable"] := Map( "type", "boolean", "default", true @@ -25,7 +22,7 @@ class BackupEntity extends AppEntityBase { definitions["IconSrc"] := Map( "type", "icon_file", "description", "The path to this an icon (.ico or .exe).", - "default", this.app.Service("manager.theme")[].GetIconPath("Backup") + "default", this.app["manager.theme"][].GetIconPath("backup") ) definitions["Source"] := Map( @@ -96,12 +93,12 @@ class BackupEntity extends AppEntityBase { this.CreateBackupObject() } - AutoDetectValues(recurse := true) { + AutoDetectValues() { if (!this.backup) { this.CreateBackupObject() } - detectedValues := super.AutoDetectValues(recurse) + detectedValues := super.AutoDetectValues() return detectedValues } diff --git a/launchpad_ahk/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk new file mode 100644 index 00000000..3f294960 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk @@ -0,0 +1,3 @@ +class TaskEntity extends FieldableEntity { + +} diff --git a/Lib/Shared/Volantis.App/Event/AlterComponentsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/AlterComponentsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/AlterComponentsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/AlterComponentsEvent.ahk diff --git a/Lib/Shared/Volantis.App/Event/AppRunEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/AppRunEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/AppRunEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/AppRunEvent.ahk diff --git a/Lib/Shared/Volantis.App/Event/ComponentEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/ComponentEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/ComponentEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/ComponentEvent.ahk diff --git a/Lib/Shared/Volantis.App/Event/ComponentInfoEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/ComponentInfoEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/ComponentInfoEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/ComponentInfoEvent.ahk diff --git a/Lib/Shared/Volantis.App/Event/DefineComponentsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/DefineComponentsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/DefineComponentsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/DefineComponentsEvent.ahk diff --git a/Lib/Shared/Volantis.App/Event/LoadComponentEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/LoadComponentEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/LoadComponentEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/LoadComponentEvent.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk new file mode 100644 index 00000000..23c1f89f --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk @@ -0,0 +1,16 @@ +class MenuItemsEvent extends EventBase { + menuItemsObj := "" + + MenuItems { + get => this.menuItemsObj + } + + __New(eventName, menuItems := "") { + if (!menuItems) { + menuItems := [] + } + + this.menuItemsObj := menuItems + super.__New(eventName) + } +} diff --git a/launchpad_ahk/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk new file mode 100644 index 00000000..8d9011d6 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk @@ -0,0 +1,20 @@ +class MenuResultEvent extends EventBase { + resultItem := "" + finished := false + + Result { + get => this.resultItem + set => this.resultItem := value + } + + IsFinished { + get => this.finished + set => !!(value) + } + + __New(eventName, result) { + this.resultItem := result + + super.__New(eventName) + } +} diff --git a/Lib/Shared/Volantis.App/Event/RegisterComponentsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/RegisterComponentsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/RegisterComponentsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/RegisterComponentsEvent.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk new file mode 100644 index 00000000..84af7d5c --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk @@ -0,0 +1,23 @@ +class ReleaseInfoEvent extends EventBase { + appObj := "" + releaseInfoObj := Map() + + App { + get => this.appObj + } + + ReleaseInfo { + get => this.releaseInfoObj + set => this.releaseInfoObj := value + } + + __New(eventName, app, releaseInfo := "") { + this.appObj := app + + if (releaseInfo) { + this.releaseInfoObj := releaseInfo + } + + super.__New(eventName) + } +} diff --git a/Lib/Shared/Volantis.App/Event/ServiceDefinitionsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Event/ServiceDefinitionsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Event/ServiceDefinitionsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Event/ServiceDefinitionsEvent.ahk diff --git a/Lib/Shared/Volantis.App/Events/Events.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Events/Events.ahk similarity index 65% rename from Lib/Shared/Volantis.App/Events/Events.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Events/Events.ahk index 35992f39..74ee9b65 100644 --- a/Lib/Shared/Volantis.App/Events/Events.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Events/Events.ahk @@ -19,5 +19,15 @@ class Events { static APP_SHUTDOWN := 0x1020 static APP_RESTART := 0x1025 + static APP_MENU_ITEMS_EARLY := 0x1030 + static APP_MENU_ITEMS_MIDDLE := 0x1032 + static APP_MENU_ITEMS_LATE := 0x1034 + static APP_MENU_ITEMS_ALTER := 0x1036 + static APP_MENU_TOOLS_ITEMS_ALTER := 0x1038 + static APP_MENU_ABOUT_ITEMS_ALTER := 0x1040 + static APP_MENU_PROCESS_RESULT := 0x1042 + + static APP_GET_RELEASE_INFO := 0x1050 + static AHK_NOTIFYICON := 0x404 } diff --git a/Lib/Shared/Volantis.App/Exception/AppException.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Exception/AppException.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Exception/AppException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Exception/AppException.ahk diff --git a/Lib/Shared/Volantis.App/Exception/LoginFailedException.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Exception/LoginFailedException.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Exception/LoginFailedException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Exception/LoginFailedException.ahk diff --git a/Lib/Shared/Volantis.App/Exception/ServiceNotFoundException.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Exception/ServiceNotFoundException.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Exception/ServiceNotFoundException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Exception/ServiceNotFoundException.ahk diff --git a/Lib/Shared/Volantis.App/Exception/WindowNotFoundException.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Exception/WindowNotFoundException.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Exception/WindowNotFoundException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Exception/WindowNotFoundException.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/DialogBox.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/DialogBox.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Dialog/DialogBox.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/DialogBox.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk similarity index 97% rename from Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk index 73d274dc..f5a6a6cd 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk @@ -2,7 +2,6 @@ entityObj := "" entityManager := "" missingFields := Map() - dataSource := "" __New(container, themeObj, config, entityObj, entityManager) { this.entityObj := entityObj diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk similarity index 80% rename from Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk index 566e0964..a97a6e87 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk @@ -1,7 +1,6 @@ class ErrorDialog extends DialogBox { errorObj := "" notifierObj := "" - apiEndpoint := "" formShown := false formH := 0 guiH := 0 @@ -9,15 +8,6 @@ class ErrorDialog extends DialogBox { __New(container, themeObj, config, errorObj) { this.errorObj := errorObj this.notifierObj := container.Get("notifier").notifierObj - - if (container.Has("manager.data_source")) { - dsManager := container.Get("manager.data_source") - - if (dsManager.GetDefaultDataSource()) { - this.apiEndpoint := container.Get("manager.data_source").GetDefaultDataSource() - } - } - this.formShown := config.Has("submitError") ? config["submitError"] : false super.__New(container, themeObj, config) @@ -118,9 +108,14 @@ class ErrorDialog extends DialogBox { SendError() { global appVersion - if (this.apiEndpoint) { - endpoint := this.apiEndpoint.endpointUrl . "/submit-error" + ; @todo Move the API connection stuff into the LaunchpadApi module + filters := "error_submission" + operation := "create" + if ( + this.container.Has("web_services.adapter_manager") + && this.container["web_services.adapter_manager"].HasAdapters(filters, operation) + ) { body := Map() body["message"] := this.errorObj.Message body["what"] := this.errorObj.What @@ -132,12 +127,25 @@ class ErrorDialog extends DialogBox { body["version"] := appVersion ? appVersion : "" body["details"] := this.guiObj["ErrorDetails"].Text - request := WinHttpReq(endpoint) - response := request.Send("POST", body) - success := !!(request.GetStatusCode() == 200) + results := this.container["web_services.adapter_manager"].AdapterRequest( + Map("data", body), + filters, + operation, + true + ) + + success := false + + for adapterId, adapterResult in results { + if (adapterResult) { + success := true + + break + } + } - notification := success ? "Successfully sent error to Volantis Development" : "Failed to send error to Volantis Development" - this.notifierObj.Notify(notification, "Error Sent", success ? "info" : "error") + notification := success ? "Successfully sent error details for further investigation" : "Failed to send error details" + this.notifierObj.Notify(notification, "Error Submission", success ? "info" : "error") } } } diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/SingleInputBox.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/SingleInputBox.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Dialog/SingleInputBox.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/SingleInputBox.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk similarity index 87% rename from Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk index 8e0a2a83..d0d8c4a1 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk @@ -21,9 +21,9 @@ class UpdateAvailableWindow extends FormGuiBase { super.Controls() this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin*2), "Current version: " . appVersion) this.SetFont("normal", "Bold") - this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), "Latest version: " . this.releaseInfo["data"]["version"]) + this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), "Latest version: " . this.releaseInfo["version"]) this.SetFont() - this.guiObj.AddLink("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), 'View release notes') + this.guiObj.AddLink("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), 'View release notes') this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin*2), "Would you like to update " . this.app.appName . " now?") } @@ -36,14 +36,14 @@ class UpdateAvailableWindow extends FormGuiBase { } ApplyUpdate() { - downloadUrl := this.releaseInfo["data"].Has("installer") ? this.releaseInfo["data"]["installer"] : "" + downloadUrl := this.releaseInfo.Has("installer") ? this.releaseInfo["installer"] : "" if (!DirExist(this.app.tmpDir . "\Installers")) { DirCreate(this.app.tmpDir . "\Installers") } if (downloadUrl) { - localFile := this.app.tmpDir . "\Installers\" . this.app.appName . "-" . this.releaseInfo["data"]["version"] . ".exe" + localFile := this.app.tmpDir . "\Installers\" . this.app.appName . "-" . this.releaseInfo["version"] . ".exe" FileDelete(this.app.tmpDir . "\Installers\" . this.app.appName . "-*") Download(downloadUrl, localFile) Run(localFile) diff --git a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk similarity index 93% rename from Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk index 7ad3903b..fd681036 100644 --- a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk @@ -9,7 +9,6 @@ class EntityEditorBase extends FormGuiBase { entityObj := "" missingFields := Map() - dataSource := "" entityFormFactory := "" entityForm := "" @@ -75,9 +74,4 @@ class EntityEditorBase extends FormGuiBase { AddEntityCtl(heading, fieldName, showDefaultCheckbox, params*) { return this.Add("EntityControl", "", heading, this.entityObj, fieldName, showDefaultCheckbox, params*) } - - Create() { - super.Create() - this.dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() - } } diff --git a/Lib/Shared/Volantis.App/Gui/EntityEditor/SimpleEntityEditor.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/EntityEditor/SimpleEntityEditor.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/EntityEditor/SimpleEntityEditor.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/EntityEditor/SimpleEntityEditor.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Form/FormGuiBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Form/FormGuiBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Form/FormGuiBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Form/FormGuiBase.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Form/IconSelector.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Form/IconSelector.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Form/IconSelector.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Form/IconSelector.ahk diff --git a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/GuiBase.ahk similarity index 94% rename from Lib/Shared/Volantis.App/Gui/GuiBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/GuiBase.ahk index 04c1ab3b..412fb7eb 100644 --- a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/GuiBase.ahk @@ -30,10 +30,12 @@ class GuiBase { isShown := false config := "" merger := "" + addedControls := [] GetDefaultConfig(container, config) { return Map( "id", Type(this), + "resizable", false, "titlebar", true, "waitForResult", false, "titleIsMenu", false, @@ -81,6 +83,10 @@ class GuiBase { extraOptions["Border"] := true } + if (this.config["resizable"]) { + extraOptions["Resize"] := true + } + if (this.owner != "") { extraOptions["Owner" . this.owner.Hwnd] := true } @@ -106,7 +112,6 @@ class GuiBase { this.margin := this.windowSettings["spacing"]["margin"] this.guiId := this.config["id"] - this.RegisterCallbacks() this.Create() } @@ -124,7 +129,7 @@ class GuiBase { RegisterCallbacks() { guiId := "Gui" . this.guiId - this.app.Service("manager.event") + this.app["manager.event"] .Register(Events.MOUSE_MOVE, guiId, ObjBindMethod(this, "OnMouseMove")) .Register(Events.WM_NCCALCSIZE, guiId, ObjBindMethod(this, "OnCalcSize")) .Register(Events.WM_NCACTIVATE, guiId, ObjBindMethod(this, "OnActivate")) @@ -137,10 +142,10 @@ class GuiBase { __Delete() { if (this.app) { - this.app.Service("manager.event").Unregister(Events.MOUSE_MOVE, "Gui" . this.guiId) - this.app.Service("manager.event").Unregister(Events.WM_NCCALCSIZE, "Gui" . this.guiId) - this.app.Service("manager.event").Unregister(Events.WM_NCACTIVATE, "Gui" . this.guiId) - this.app.Service("manager.event").Unregister(Events.WM_NCHITTEST, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.MOUSE_MOVE, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.WM_NCCALCSIZE, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.WM_NCACTIVATE, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.WM_NCHITTEST, "Gui" . this.guiId) } if (this.activeTooltip) { @@ -169,7 +174,9 @@ class GuiBase { } Add(ctlClass, options := "", params*) { - return %ctlClass%(this, options, params*) + ctlObj := %ctlClass%(this, options, params*) + this.addedControls.Push(ctlObj) + return ctlObj } OnCalcSize(wParam, lParam, msg, hwnd) { @@ -342,20 +349,12 @@ class GuiBase { return this.guiObj.AddEdit(opts, defaultValue) } - UpdateStatusIndicator() { + UpdateStatusIndicator(webService) { if (this.config["showStatusIndicator"]) { - this.titlebar.statusIndicator.UpdateStatusIndicator(this.GetStatusInfo(), this.StatusWindowIsOnline() ? "status" : "statusOffline") + webService.UpdateStatusIndicators() } } - StatusWindowIsOnline() { - return false - } - - GetStatusInfo() { - return Map("name", "", "photo", "") - } - SetFont(fontPreset := "normal", extraStyles := "", colorName := "text") { this.guiObj.SetFont() this.guiObj.SetFont("c" . this.themeObj.GetColor(colorName) . " " . this.themeObj.GetFont(fontPreset) . " " . extraStyles) @@ -568,22 +567,22 @@ class GuiBase { } if (!this.isClosed && WinExist("ahk_id " . this.guiObj.Hwnd)) { - this.app.Service("manager.gui").StoreWindowState(this) + this.app["manager.gui"].StoreWindowState(this) WinClose("ahk_id " . this.guiObj.Hwnd) } else { this.Destroy() } - this.app.Service("manager.gui").CleanupWindow(this.guiId) + this.app["manager.gui"].CleanupWindow(this.guiId) } Destroy() { if (!this.isClosed && this.config["saveWindowState"]) { - this.app.Service("manager.gui").StoreWindowState(this) + this.app["manager.gui"].StoreWindowState(this) } if (this.owner) { - this.app.Service("manager.gui").ReleaseFromParent(this.guiId) + this.app["manager.gui"].ReleaseFromParent(this.guiId) } this.Cleanup() @@ -595,7 +594,7 @@ class GuiBase { } Cleanup() { - this.app.Service("manager.gui").UnloadComponent(this.guiId) + this.app["manager.gui"].UnloadComponent(this.guiId) ; Extend to clear any global variables used } @@ -762,8 +761,12 @@ class GuiBase { } OnSize(guiObj, minMax, width, height) { - if (this.config["titlebar"]) { - this.titlebar.OnSize(minMax, width, height) + for index, ctlObj in this.addedControls { + ctlObj.OnSize(guiObj, minMax, width, height) } + + ; if (this.config["titlebar"]) { + ; this.titlebar.OnSize(minMax, width, height) + ; } } } diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk similarity index 87% rename from Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk index 4d18b352..1a4b3c09 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk @@ -24,6 +24,7 @@ class ManageEntitiesWindow extends ManageWindowBase { defaults := super.GetDefaultConfig(container, config) defaults["entity_type"] := this.entityTypeId defaults["title"] := this.entityType.definition["name_plural"] + return defaults } @@ -74,10 +75,9 @@ class ManageEntitiesWindow extends ManageWindowBase { defaultIcon := this.themeObj.GetIconPath(defaultIconName) iconNum := 1 - iconField := this.entityType.definition["icon_field"] for key, entityObj in this.entityMgr { - iconSrc := entityObj[iconField] + iconSrc := this.GetEntityIconSrc(entityObj) if (!InStr(iconSrc, ":\")) { iconSrc := this.themeObj.GetIconPath(iconSrc) @@ -94,6 +94,17 @@ class ManageEntitiesWindow extends ManageWindowBase { return IL } + GetEntityIconSrc(entityObj) { + iconSrc := "" + iconField := this.entityType.definition["icon_field"] + + if (iconField && entityObj.Has(iconField)) { + iconSrc := entityObj[iconField] + } + + return iconSrc + } + OnDoubleClick(LV, rowNum) { key := this.listView.GetRowKey(rowNum) @@ -149,31 +160,35 @@ class ManageEntitiesWindow extends ManageWindowBase { this.AutoXYWH("y", ["AddButton"]) } - GetContextMenuItems() { + GetContextMenuItems(entityObj) { definition := this.entityType.definition menuItems := [] - if (definition["allow_view"]) { + if (definition["allow_view"] && this._shouldShowButton(entityObj, "ViewEntity")) { menuItems.Push(Map("label", "&View", "name", "ViewEntity")) } - if (definition["allow_edit"]) { + if (definition["allow_edit"] && this._shouldShowButton(entityObj, "EditEntity")) { menuItems.Push(Map("label", "Edit", "name", "EditEntity")) } - if (definition["allow_delete"]) { + if (definition["allow_delete"] && this._shouldShowButton(entityObj, "DeleteEntity")) { menuItems.Push(Map("label", "Delete", "name", "DeleteEntity")) } return menuItems } + _shouldShowButton(entityObj, buttonName) { + return true + } + ShowListViewContextMenu(lv, item, isRightClick, X, Y) { key := this.listView.GetRowKey(item) entityObj := this.entityMgr[key] - menuItems := this.GetContextMenuItems() - result := this.app.Service("manager.gui").Menu(menuItems, this) + menuItems := this.GetContextMenuItems(entityObj) + result := this.app["manager.gui"].Menu(menuItems, this) this.ProcessContextMenuResult(result, key) } diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk similarity index 96% rename from Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk index 56c85348..72cb7f01 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk @@ -6,11 +6,13 @@ lvWidth := 0 showDetailsPane := false detailsFields := [] + lvResizeOpts := "wh" GetDefaultConfig(container, config) { defaults := super.GetDefaultConfig(container, config) defaults["frameShadow"] := false defaults["saveWindowState"] := true + defaults["resizable"] := true return defaults } @@ -74,8 +76,7 @@ opts.Push("w" . this.lvWidth) } - this.listView := this.Add("ListViewControl", opts, "", this.listViewColumns, "GetListViewData", "GetListViewImgList", "InitListView", "ShouldHighlightRow") - this.listView.resizeOpts := "h" + this.listView := this.Add("ListViewControl", opts, "", this.listViewColumns, "GetListViewData", "GetListViewImgList", "InitListView", "ShouldHighlightRow", this.lvResizeOpts) return this.listView } diff --git a/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk similarity index 98% rename from Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk index 13f2c878..a7b5d689 100644 --- a/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk @@ -133,7 +133,7 @@ if (btn.ChildItems) { this.childOpen := true - this.result := this.app.Service("manager.gui").Menu(Map( + this.result := this.app["manager.gui"].Menu(Map( "parent", this, "child", true, "openAtCtlSide", "right" diff --git a/Lib/Shared/Volantis.App/Gui/Progress/MiniProgressIndicator.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Progress/MiniProgressIndicator.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Progress/MiniProgressIndicator.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Progress/MiniProgressIndicator.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicator.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicator.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicator.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicator.ahk diff --git a/Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicatorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicatorBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicatorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Gui/Progress/ProgressIndicatorBase.ahk diff --git a/Lib/Shared/Volantis.App/GuiControl/BasicControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/BasicControl.ahk similarity index 100% rename from Lib/Shared/Volantis.App/GuiControl/BasicControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/BasicControl.ahk diff --git a/Lib/Shared/Volantis.App/GuiControl/ButtonControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ButtonControl.ahk similarity index 100% rename from Lib/Shared/Volantis.App/GuiControl/ButtonControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ButtonControl.ahk diff --git a/Lib/Shared/Volantis.App/GuiControl/CheckboxControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/CheckboxControl.ahk similarity index 100% rename from Lib/Shared/Volantis.App/GuiControl/CheckboxControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/CheckboxControl.ahk diff --git a/Lib/Shared/Volantis.App/GuiControl/ComboBoxControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ComboBoxControl.ahk similarity index 100% rename from Lib/Shared/Volantis.App/GuiControl/ComboBoxControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ComboBoxControl.ahk diff --git a/Lib/Shared/Volantis.App/GuiControl/EditControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/EditControl.ahk similarity index 100% rename from Lib/Shared/Volantis.App/GuiControl/EditControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/EditControl.ahk diff --git a/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk similarity index 97% rename from Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk index fb3688c8..7c159b01 100644 --- a/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk @@ -66,7 +66,7 @@ class EntityControl extends GuiControlBase { this.widget.WriteValueToEntity() if (this.refreshDataOnChange && (!this.dependentFields || this.dependentFields.Length == 0)) { - this.entityObj.UpdateDataSourceDefaults() + this.entityObj.UpdateDefaults() } this.SetDependentFieldValues() @@ -117,7 +117,7 @@ class EntityControl extends GuiControlBase { SetDependentFieldValues() { if (this.dependentFields && this.dependentFields.Length > 0) { - this.entityObj.UpdateDataSourceDefaults() + this.entityObj.UpdateDefaults() for index, field in this.dependentFields { this.guiObj.guiObj[field].Value := this.entityObj.GetField(field).GetRawValue() diff --git a/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk similarity index 98% rename from Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk index 399d18cc..9d58675e 100644 --- a/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk @@ -1,5 +1,6 @@ class GuiControlBase { app := "" + container := "" guiObj := "" ctl := "" defaultH := 20 @@ -12,6 +13,7 @@ class GuiControlBase { __New(guiObj, options := "", heading := "", params*) { InvalidParameterException.CheckTypes("GuiControlBase", "guiObj", guiObj, "GuiBase") this.app := guiObj.app + this.container := this.app.Services this.guiObj := guiObj if (HasBase(options, GuiControlParameters.Prototype)) { diff --git a/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk similarity index 99% rename from Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk index 2d5c06f8..650cad6e 100644 --- a/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk @@ -11,7 +11,7 @@ class ListViewControl extends GuiControlBase { imgListL := "" resizeOpts := "wh" - CreateControl(columns, dataCallback, imgListCallback := "", initCallback := "", highlightRowCallback := "", resizeOpts := "") { + CreateControl(columns, dataCallback, imgListCallback := "", initCallback := "", highlightRowCallback := "", resizeOpts := "wh") { global LVM_GETHEADER super.CreateControl(false) columns.InsertAt(this.keyCol, "") diff --git a/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk similarity index 98% rename from Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk index 40f371d8..3f535567 100644 --- a/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk @@ -83,7 +83,7 @@ class LocationBlock extends GuiControlBase { } OnLocationOptions(btn, info) { - result := this.app.Service("manager.gui").Menu(Map( + result := this.app["manager.gui"].Menu(Map( "parent", this.guiObj, "child", true ), btn.MenuItems, btn) diff --git a/Lib/Shared/Volantis.App/GuiControl/SelectControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/SelectControl.ahk similarity index 100% rename from Lib/Shared/Volantis.App/GuiControl/SelectControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/SelectControl.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk new file mode 100644 index 00000000..12bceb51 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk @@ -0,0 +1,120 @@ +class StatusIndicatorControl extends GuiControlBase { + statusIndicatorW := 40 + statusIndicatorMinW := 40 + statusIndicatorExpandedMinW := 120 + innerControl := "" + webService := "" + + CreateControl(webService, handler := "") { + super.CreateControl(false) + this.webService := webService + + if (!handler) { + handler := ObjBindMethod(this, "OnStatusIndicatorClick") + } + + if (handler == "" && HasMethod(this.guiObj, "OnStatusIndicatorClick")) { + handler := "OnStatusIndicatorClick" + } + + this.statusIndicatorW := webService["StatusIndicatorExpanded"] ? this.statusIndicatorExpandedMinW : this.statusIndicatorMinW + + options := this.parameters.SetDefaultOptions(this.parameters["options"].Clone(), [ + "x+" . this.guiObj.margin, + "yp", + "w" . this.statusIndicatorW, + "h26", + "vStatusIndicator" . webService.Id + ]) + + statusInfo := webService.GetStatusInfo() + statusStyle := webService.Authenticated ? "status" : "statusOffline" + name := (statusInfo && statusInfo.Has("name") && webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : "" + photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : "" + + this.innerControl := this.guiObj.Add("ButtonControl", options, name, handler, statusStyle, Map("photo", photo)) + this.ctl := this.innerControl.ctl + return this.ctl + } + + OnStatusIndicatorClick(btn, info) { + webService := this.webService + menuItems := [] + + if (webService) { + if (webService.Authenticated) { + menuItems.Push(Map("label", "Account Details", "name", "AccountDetails")) + menuItems.Push(Map("label", "Logout", "name", "Logout")) + } else { + menuItems.Push(Map("label", "Login", "name", "Login")) + } + } + + result := this.container["manager.gui"].Menu(menuItems, this, btn) + + if (result == "AccountDetails") { + accountResult := this.container["manager.gui"].Dialog(Map( + "type", "AccountInfoWindow", + "webService", this.webService, + "ownerOrParent", this.guiObj.guiId, + "child", true + )) + + if (accountResult == "OK") { + this.UpdateStatusIndicator(webService) + } + } else if (result == "Logout") { + if (webService) { + webService.Logout() + } + } else if (result == "Login") { + if (webService) { + webService.Login() + } + } + } + + UpdateStatusIndicator() { + statusInfo := this.webService.GetStatusInfo() + statusStyle := this.webService.Authenticated ? "status" : "statusOffline" + + oldW := this.statusIndicatorW + newW := this.CalculateWidth(statusInfo) + this.statusIndicatorW := newW + difference := 0 + + if (oldW != newW) { + this.ctl.GetPos(&statusX,, &statusW) + difference := newW - oldW + this.ctl.Move(statusX - difference,, statusW + difference) + } + + name := (statusInfo && statusInfo.Has("name") && this.webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : "" + photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : "" + + this.guiObj.themeObj.DrawButton(this.ctl, name, statusStyle, Map("photo", photo)) + return difference + } + + CalculateWidth(statusInfo) { + expanded := this.webService["StatusIndicatorExpanded"] + width := expanded ? this.statusIndicatorExpandedMinW : this.statusIndicatorMinW + requiredW := 10 + + if (statusInfo) { + if (statusInfo.Has("name") && expanded) { + requiredW += this.guiObj.themeObj.CalculateTextWidth(statusInfo["name"]) + } + + if (StatusInfo.Has("photo") || !expanded) { + requiredW += 26 + } + } + + if (requiredW > width) { + width := requiredW + } + + return Ceil(width) + } +} diff --git a/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk similarity index 99% rename from Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk index 3b6a1e62..591a2a05 100644 --- a/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk @@ -122,6 +122,5 @@ class TabsControl extends GuiControlBase { } this.guiObj.AutoXYWH("wh", [this.ctl.Name]) - this.ResizeColumns() } } diff --git a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk similarity index 77% rename from Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk index 3e4ed8cb..872dccae 100644 --- a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk @@ -5,10 +5,12 @@ class TitlebarControl extends GuiControlBase { iconW := 16 titlebarH := 31 titlebarButtonW := 16 - initialStatusIndicatorW := 120 + initialStatusIndicatorW := 40 + initialStatusIndicatorExpandedW := 120 titleButton := "" titleText := "" statusIndicator := "" + statusIndicators := [] minBtn := "" maxBtn := "" unmaxBtn := "" @@ -33,9 +35,21 @@ class TitlebarControl extends GuiControlBase { buttonsW := 0 statusIndicatorW := this.guiObj.config["showStatusIndicator"] ? this.initialStatusIndicatorW : 0 + statusIndicatorExpandedW := this.guiObj.config["showStatusIndicator"] ? this.initialStatusIndicatorExpandedW : 0 + + serviceMgr := this.container["entity_manager.web_service"] + webServices := Map() if (this.guiObj.config["showStatusIndicator"]) { - buttonsW += statusIndicatorW + (this.guiObj.margin * 2) + webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Condition(IsTrueCondition(), "StatusIndicator") + .Execute() + + for serviceId, webService in webServices { + serviceStatusW := (webService["StatusIndicatorExpanded"]) ? statusIndicatorExpandedW : statusIndicatorW + buttonsW += serviceStatusW + (this.guiObj.margin * 2) + } } if (this.guiObj.config["showMinimize"]) { @@ -84,20 +98,19 @@ class TitlebarControl extends GuiControlBase { this.titleText := this.guiObj.guiObj.AddText(opts, titleText) } - if (this.guiObj.config["showStatusIndicator"]) { - opts := "x" . buttonsX . " y" . (this.topMargin - 5) . " w" . statusIndicatorW - statusStyle := this.guiObj.StatusWindowIsOnline() ? "status" : "statusOffline" - initialInfo := Map() - statusInfo := this.guiObj.GetStatusInfo() - - if (statusInfo) { - initialInfo := statusInfo.Clone() - initialInfo["name"] := "" + if (this.guiObj.config["showStatusIndicator"] && webServices.Count) { + for serviceId, service in webServices { + expanded := service["StatusIndicatorExpanded"] + serviceStatusW := expanded ? statusIndicatorExpandedW : statusIndicatorW + opts := "x" . buttonsX . " y" . (this.topMargin - 5) . " w" . serviceStatusW + + statusIndicator := this.guiObj.Add("StatusIndicatorControl", opts, "", service, "") + this.statusIndicators.Push(statusIndicator) + service.AddStatusIndicator(statusIndicator) + + difference := statusIndicator.UpdateStatusIndicator() + buttonsX += serviceStatusW + (this.guiObj.margin * 2) } - - this.statusIndicator := this.guiObj.Add("StatusIndicatorControl", opts, "", initialInfo, "", statusStyle) - difference := this.statusIndicator.UpdateStatusIndicator(statusInfo, statusStyle) - buttonsX += this.initialStatusIndicatorW + (this.guiObj.margin * 2) } handler := this.RegisterCallback("OnTitlebarButtonClick") @@ -182,7 +195,7 @@ class TitlebarControl extends GuiControlBase { } } - OnSize(minMax, width, height) { + OnSize(guiObj, minMax, width, height) { if (minMax == 1 and this.guiObj.config["showMaximize"]) { this.guiObj.guiObj["WindowUnmaxButton"].Visible := true this.guiObj.guiObj["WindowMaxButton"].Visible := false @@ -198,7 +211,15 @@ class TitlebarControl extends GuiControlBase { this.guiObj.AutoXYWH("w", ["WindowTitlebar"]) if (this.guiObj.config["showStatusIndicator"]) { - this.guiObj.AutoXYWH("x*", ["StatusIndicator"]) + indicatorCtlNames := [] + + for index, statusIndicator in this.statusIndicators { + indicatorCtlNames.Push(statusIndicator.ctl.Name) + } + + if (indicatorCtlNames.Length) { + this.guiObj.AutoXYWH("x*", indicatorCtlNames) + } } if (this.guiObj.config["showClose"]) { @@ -209,7 +230,7 @@ class TitlebarControl extends GuiControlBase { this.guiObj.AutoXYWH("x*", ["WindowMaxButton", "WindowUnmaxButton"]) } - if (this.guiObj.config["showMaximize"]) { + if (this.guiObj.config["showMinimize"]) { this.guiObj.AutoXYWH("x*", ["WindowMinButton"]) } } diff --git a/Lib/Shared/Volantis.App/Installer/InstallerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/InstallerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerBase.ahk diff --git a/Lib/Shared/Volantis.App/Installer/InstallerComponent/CopyableInstallerComponent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/CopyableInstallerComponent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/InstallerComponent/CopyableInstallerComponent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/CopyableInstallerComponent.ahk diff --git a/Lib/Shared/Volantis.App/Installer/InstallerComponent/DownloadableInstallerComponent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/DownloadableInstallerComponent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/InstallerComponent/DownloadableInstallerComponent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/DownloadableInstallerComponent.ahk diff --git a/Lib/Shared/Volantis.App/Installer/InstallerComponent/FileInstallerComponentBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/FileInstallerComponentBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/InstallerComponent/FileInstallerComponentBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/FileInstallerComponentBase.ahk diff --git a/Lib/Shared/Volantis.App/Installer/InstallerComponent/GitHubReleaseInstallerComponent.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/GitHubReleaseInstallerComponent.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/InstallerComponent/GitHubReleaseInstallerComponent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/GitHubReleaseInstallerComponent.ahk diff --git a/Lib/Shared/Volantis.App/Installer/InstallerComponent/InstallerComponentBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/InstallerComponentBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/InstallerComponent/InstallerComponentBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/InstallerComponent/InstallerComponentBase.ahk diff --git a/Lib/Shared/Volantis.App/Installer/ThemeInstaller.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Installer/ThemeInstaller.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Installer/ThemeInstaller.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Installer/ThemeInstaller.ahk diff --git a/Lib/Shared/Volantis.App/Service/AppServiceBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/AppServiceBase.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/AppServiceBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/AppServiceBase.ahk diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/CacheManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/CacheManager.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/ComponentManager/CacheManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/CacheManager.ahk diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk similarity index 99% rename from Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk index cfbcfd9e..022e8688 100644 --- a/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk @@ -278,7 +278,7 @@ class GuiManager extends ComponentManagerBase { guiObj := obj } else if (HasBase(obj, GuiBase.Prototype)) { guiObj := obj.guiObj - } else if (Type(guiObj) == "String" && this.Has(obj)) { + } else if (Type(obj) == "String" && this.Has(obj)) { guiObj := this[obj].guiObj } diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/InstallerManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/InstallerManager.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/ComponentManager/InstallerManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/InstallerManager.ahk diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/ThemeManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/ThemeManager.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/ComponentManager/ThemeManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/ComponentManager/ThemeManager.ahk diff --git a/Lib/Shared/Volantis.App/Service/EntityManager/BackupManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/EntityManager/BackupManager.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/EntityManager/BackupManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/EntityManager/BackupManager.ahk diff --git a/Lib/Shared/Volantis.App/Service/EventManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/EventManager.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/EventManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/EventManager.ahk diff --git a/Lib/Shared/Volantis.App/Service/LoggerService.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/LoggerService.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/LoggerService.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/LoggerService.ahk diff --git a/Lib/Shared/Volantis.App/Service/NotificationService.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/Service/NotificationService.ahk similarity index 100% rename from Lib/Shared/Volantis.App/Service/NotificationService.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/Service/NotificationService.ahk diff --git a/Lib/Shared/Volantis.App/State/AppState.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/State/AppState.ahk similarity index 100% rename from Lib/Shared/Volantis.App/State/AppState.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/State/AppState.ahk diff --git a/Lib/Shared/Volantis.App/State/CacheState.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/State/CacheState.ahk similarity index 100% rename from Lib/Shared/Volantis.App/State/CacheState.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/State/CacheState.ahk diff --git a/Lib/Shared/Volantis.App/State/JsonState.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/State/JsonState.ahk similarity index 100% rename from Lib/Shared/Volantis.App/State/JsonState.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/State/JsonState.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.App/State/ParameterState.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/State/ParameterState.ahk new file mode 100644 index 00000000..7e573004 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.App/State/ParameterState.ahk @@ -0,0 +1,39 @@ +class ParameterState extends StateBase { + parameterKey := "" + + __New(app, parameterKey, autoLoad := false) { + this.parameterKey := parameterKey + super.__New(app, "", autoLoad) + } + + SaveState(newState := "") { + if (newState != "") { + this.stateMap := newState + } + + if (this.parameterKey) { + this.container.Parameters[this.parameterKey] := this.stateMap + } + + return this.stateMap + } + + LoadState() { + if (this.parameterKey && !this.stateLoaded) { + newState := super.LoadState() + + if (this.container.HasParameter(this.parameterKey)) { + paramValue := this.container.Parameters[this.parameterKey] + + if (HasBase(paramValue, Map.Prototype)) { + newState := paramValue + } + } + + this.stateMap := newState + this.stateLoaded := true + } + + return this.stateMap + } +} diff --git a/Lib/Shared/Volantis.App/State/StateBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.App/State/StateBase.ahk similarity index 87% rename from Lib/Shared/Volantis.App/State/StateBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.App/State/StateBase.ahk index 4aaf8b0a..c19f07e9 100644 --- a/Lib/Shared/Volantis.App/State/StateBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.App/State/StateBase.ahk @@ -1,5 +1,6 @@ class StateBase { app := "" + container := "" stateMap := Map() stateLoaded := false @@ -14,13 +15,14 @@ class StateBase { } IsStateOutdated() { - return this.app.Service("version_checker").VersionIsOutdated(this.app.Version, this.Version) + return this.app["version_checker"].VersionIsOutdated(this.app.Version, this.Version) } __New(app, state := "", autoLoad := false) { InvalidParameterException.CheckTypes("StateBase", "app", app, "AppBase") this.app := app + this.container := app.Services if (state != "") { InvalidParameterException.CheckTypes("StateBase", "state", state, "Map") diff --git a/Lib/Shared/Volantis.App/Volantis.App.library.json b/launchpad_ahk/Lib/Shared/Volantis.App/Volantis.App.library.json similarity index 100% rename from Lib/Shared/Volantis.App/Volantis.App.library.json rename to launchpad_ahk/Lib/Shared/Volantis.App/Volantis.App.library.json diff --git a/Lib/Shared/Volantis.Base/CLR/CLR.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/CLR/CLR.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/CLR/CLR.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/CLR/CLR.ahk diff --git a/Lib/Shared/Volantis.Base/Event/EventBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Event/EventBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Event/EventBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Event/EventBase.ahk diff --git a/Lib/Shared/Volantis.Base/Event/EventBase.test.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Event/EventBase.test.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Event/EventBase.test.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Event/EventBase.test.ahk diff --git a/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk similarity index 76% rename from Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk index 871cef25..3043d56d 100644 --- a/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk @@ -2,6 +2,12 @@ Extending this class is optional as its main purpose is to document the API */ class EventSubscriberBase { + container := "" + + __New(container) { + this.container := container + } + /* Format: Map( @@ -12,6 +18,6 @@ class EventSubscriberBase { ) */ GetEventSubscribers() { - return [] + return Map() } } diff --git a/Lib/Shared/Volantis.Base/Exception/ExceptionBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Exception/ExceptionBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Exception/ExceptionBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Exception/ExceptionBase.ahk diff --git a/Lib/Shared/Volantis.Base/Exception/FileSystemException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Exception/FileSystemException.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Exception/FileSystemException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Exception/FileSystemException.ahk diff --git a/Lib/Shared/Volantis.Base/Exception/InvalidParameterException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Exception/InvalidParameterException.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Exception/InvalidParameterException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Exception/InvalidParameterException.ahk diff --git a/Lib/Shared/Volantis.Base/Exception/MethodNotImplementedException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Exception/MethodNotImplementedException.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Exception/MethodNotImplementedException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Exception/MethodNotImplementedException.ahk diff --git a/Lib/Shared/Volantis.Base/Exception/OperationFailedException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/Exception/OperationFailedException.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/Exception/OperationFailedException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/Exception/OperationFailedException.ahk diff --git a/Lib/Shared/Volantis.Base/HttpReq/HttpReqBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/HttpReq/HttpReqBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/HttpReq/HttpReqBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/HttpReq/HttpReqBase.ahk diff --git a/Lib/Shared/Volantis.Base/HttpReq/WinHttpReq.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/HttpReq/WinHttpReq.ahk similarity index 100% rename from Lib/Shared/Volantis.Base/HttpReq/WinHttpReq.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/HttpReq/WinHttpReq.ahk diff --git a/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk b/launchpad_ahk/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk similarity index 82% rename from Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk index 18a0e7e4..1b011249 100644 --- a/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk @@ -51,19 +51,21 @@ class UrlObj { urlMap := Map() urlParts := "" - regexStr := "^((P[^:/?#]+):)?(//(P[^/?#]*))?(P[^?#]*)(\?(P[^#]*))?(#(P.*))?" - isUrl := RegExMatch(urlStr, regexStr, urlParts) - - loop urlParts.Count { - matchName := urlParts.Name[A_Index] - matchVal := urlParts[A_Index] - - if (matchName) { - if (matchName == "query") { - matchVal := this._splitQueryStr(matchVal) + regexStr := "^((?P[^:/?#]+):)?(//(?P[^/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?" + isUrl := RegExMatch(urlStr, regexStr, &urlParts) + + if (urlParts) { + loop urlParts.Count { + matchName := urlParts.Name[A_Index] + matchVal := urlParts[A_Index] + + if (matchName) { + if (matchName == "query") { + matchVal := this._splitQueryStr(matchVal) + } + + urlMap[matchName] := matchVal } - - urlMap[matchName] := matchVal } } diff --git a/Lib/Shared/Volantis.Base/Volantis.Base.library.json b/launchpad_ahk/Lib/Shared/Volantis.Base/Volantis.Base.library.json similarity index 100% rename from Lib/Shared/Volantis.Base/Volantis.Base.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Base/Volantis.Base.library.json diff --git a/Lib/Shared/Volantis.Component/ComponentManager/ComponentManagerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Component/ComponentManager/ComponentManagerBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Component/ComponentManager/ComponentManagerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Component/ComponentManager/ComponentManagerBase.ahk diff --git a/Lib/Shared/Volantis.Component/ComponentManager/SimpleComponentManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.Component/ComponentManager/SimpleComponentManager.ahk similarity index 100% rename from Lib/Shared/Volantis.Component/ComponentManager/SimpleComponentManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Component/ComponentManager/SimpleComponentManager.ahk diff --git a/Lib/Shared/Volantis.Component/Event/ComponentDefinitionsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Component/Event/ComponentDefinitionsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Component/Event/ComponentDefinitionsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Component/Event/ComponentDefinitionsEvent.ahk diff --git a/Lib/Shared/Volantis.Component/Event/ComponentManagerEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Component/Event/ComponentManagerEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Component/Event/ComponentManagerEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Component/Event/ComponentManagerEvent.ahk diff --git a/Lib/Shared/Volantis.Component/Events/ComponentEvents.ahk b/launchpad_ahk/Lib/Shared/Volantis.Component/Events/ComponentEvents.ahk similarity index 100% rename from Lib/Shared/Volantis.Component/Events/ComponentEvents.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Component/Events/ComponentEvents.ahk diff --git a/Lib/Shared/Volantis.Component/Exception/ComponentException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Component/Exception/ComponentException.ahk similarity index 100% rename from Lib/Shared/Volantis.Component/Exception/ComponentException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Component/Exception/ComponentException.ahk diff --git a/Lib/Shared/Volantis.Component/README.md b/launchpad_ahk/Lib/Shared/Volantis.Component/README.md similarity index 100% rename from Lib/Shared/Volantis.Component/README.md rename to launchpad_ahk/Lib/Shared/Volantis.Component/README.md diff --git a/Lib/Shared/Volantis.Component/Volantis.Component.library.json b/launchpad_ahk/Lib/Shared/Volantis.Component/Volantis.Component.library.json similarity index 100% rename from Lib/Shared/Volantis.Component/Volantis.Component.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Component/Volantis.Component.library.json diff --git a/Lib/Shared/Volantis.Config/Config/ChildConfig.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/Config/ChildConfig.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/Config/ChildConfig.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/Config/ChildConfig.ahk diff --git a/Lib/Shared/Volantis.Config/Config/ConfigBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/Config/ConfigBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/Config/ConfigBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/Config/ConfigBase.ahk diff --git a/Lib/Shared/Volantis.Config/Config/ContainerConfigBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/Config/ContainerConfigBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/Config/ContainerConfigBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/Config/ContainerConfigBase.ahk diff --git a/Lib/Shared/Volantis.Config/Config/PersistentConfig.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/Config/PersistentConfig.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/Config/PersistentConfig.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/Config/PersistentConfig.ahk diff --git a/Lib/Shared/Volantis.Config/Config/RuntimeConfig.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/Config/RuntimeConfig.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/Config/RuntimeConfig.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/Config/RuntimeConfig.ahk diff --git a/Lib/Shared/Volantis.Config/ConfigStorage/ConfigStorageBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/ConfigStorage/ConfigStorageBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/ConfigStorage/ConfigStorageBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/ConfigStorage/ConfigStorageBase.ahk diff --git a/Lib/Shared/Volantis.Config/ConfigStorage/JsonConfigStorage.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/ConfigStorage/JsonConfigStorage.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/ConfigStorage/JsonConfigStorage.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/ConfigStorage/JsonConfigStorage.ahk diff --git a/Lib/Shared/Volantis.Config/Exception/ConfigException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Config/Exception/ConfigException.ahk similarity index 100% rename from Lib/Shared/Volantis.Config/Exception/ConfigException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Config/Exception/ConfigException.ahk diff --git a/Lib/Shared/Volantis.Config/Volantis.Config.library.json b/launchpad_ahk/Lib/Shared/Volantis.Config/Volantis.Config.library.json similarity index 100% rename from Lib/Shared/Volantis.Config/Volantis.Config.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Config/Volantis.Config.library.json diff --git a/Lib/Shared/Volantis.Container/Container/BasicContainer.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Container/BasicContainer.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Container/BasicContainer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Container/BasicContainer.ahk diff --git a/Lib/Shared/Volantis.Container/Container/ContainerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Container/ContainerBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Container/ContainerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Container/ContainerBase.ahk diff --git a/Lib/Shared/Volantis.Container/Container/ParameterContainer.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Container/ParameterContainer.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Container/ParameterContainer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Container/ParameterContainer.ahk diff --git a/Lib/Shared/Volantis.Container/Container/ServiceContainer.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Container/ServiceContainer.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Container/ServiceContainer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Container/ServiceContainer.ahk diff --git a/Lib/Shared/Volantis.Container/ContainerRef/AppRef.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/AppRef.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ContainerRef/AppRef.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/AppRef.ahk diff --git a/Lib/Shared/Volantis.Container/ContainerRef/ContainerRef.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ContainerRef.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ContainerRef/ContainerRef.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ContainerRef.ahk diff --git a/Lib/Shared/Volantis.Container/ContainerRef/ContainerRefBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ContainerRefBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ContainerRef/ContainerRefBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ContainerRefBase.ahk diff --git a/Lib/Shared/Volantis.Container/ContainerRef/DataRef.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/DataRef.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ContainerRef/DataRef.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/DataRef.ahk diff --git a/Lib/Shared/Volantis.Container/ContainerRef/ParameterRef.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ParameterRef.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ContainerRef/ParameterRef.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ParameterRef.ahk diff --git a/Lib/Shared/Volantis.Container/ContainerRef/ServiceRef.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ServiceRef.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ContainerRef/ServiceRef.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ContainerRef/ServiceRef.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/ClassDiscoveryDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/ClassDiscoveryDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/ClassDiscoveryDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/ClassDiscoveryDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/DefinitionLoaderBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/DefinitionLoaderBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/DefinitionLoaderBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/DefinitionLoaderBase.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/DirDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/DirDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/DirDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/DirDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/FileDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/FileDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/FileDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/FileDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/FileDiscoveryDefinitionLoaderBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/FileDiscoveryDefinitionLoaderBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/FileDiscoveryDefinitionLoaderBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/FileDiscoveryDefinitionLoaderBase.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/MapDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/MapDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/MapDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/MapDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/SimpleDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/SimpleDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/SimpleDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/SimpleDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Container/DefinitionLoader/StructuredDataDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/StructuredDataDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/DefinitionLoader/StructuredDataDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/DefinitionLoader/StructuredDataDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Container/Exception/ContainerException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Exception/ContainerException.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Exception/ContainerException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Exception/ContainerException.ahk diff --git a/Lib/Shared/Volantis.Container/Exception/ParameterNotFoundException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Exception/ParameterNotFoundException.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Exception/ParameterNotFoundException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Exception/ParameterNotFoundException.ahk diff --git a/Lib/Shared/Volantis.Container/ParameterBag/ParameterBag.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/ParameterBag/ParameterBag.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/ParameterBag/ParameterBag.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/ParameterBag/ParameterBag.ahk diff --git a/Lib/Shared/Volantis.Container/Query/ContainerQuery.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/Query/ContainerQuery.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/Query/ContainerQuery.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/Query/ContainerQuery.ahk diff --git a/Lib/Shared/Volantis.Container/QueryCondition/HasServiceTagsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/QueryCondition/HasServiceTagsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/QueryCondition/HasServiceTagsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/QueryCondition/HasServiceTagsCondition.ahk diff --git a/Lib/Shared/Volantis.Container/QueryCondition/NamespaceMatchesCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Container/QueryCondition/NamespaceMatchesCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Container/QueryCondition/NamespaceMatchesCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Container/QueryCondition/NamespaceMatchesCondition.ahk diff --git a/Lib/Shared/Volantis.Container/README.md b/launchpad_ahk/Lib/Shared/Volantis.Container/README.md similarity index 100% rename from Lib/Shared/Volantis.Container/README.md rename to launchpad_ahk/Lib/Shared/Volantis.Container/README.md diff --git a/Lib/Shared/Volantis.Container/Volantis.Container.library.json b/launchpad_ahk/Lib/Shared/Volantis.Container/Volantis.Container.library.json similarity index 100% rename from Lib/Shared/Volantis.Container/Volantis.Container.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Container/Volantis.Container.library.json diff --git a/Lib/Shared/Volantis.Data/ContainerContext/DataContext.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/ContainerContext/DataContext.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/ContainerContext/DataContext.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/ContainerContext/DataContext.ahk diff --git a/Lib/Shared/Volantis.Data/DataProcessor/DataProcessorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/DataProcessorBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataProcessor/DataProcessorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/DataProcessorBase.ahk diff --git a/Lib/Shared/Volantis.Data/DataProcessor/PlaceholderExpander.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/PlaceholderExpander.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataProcessor/PlaceholderExpander.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/PlaceholderExpander.ahk diff --git a/Lib/Shared/Volantis.Data/DataProcessor/StringSanitizer.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/StringSanitizer.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataProcessor/StringSanitizer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/StringSanitizer.ahk diff --git a/Lib/Shared/Volantis.Data/DataProcessor/TokenReplacer.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/TokenReplacer.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataProcessor/TokenReplacer.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataProcessor/TokenReplacer.ahk diff --git a/Lib/Shared/Volantis.Data/DataTemplate/AhkTemplate.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataTemplate/AhkTemplate.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataTemplate/AhkTemplate.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataTemplate/AhkTemplate.ahk diff --git a/Lib/Shared/Volantis.Data/DataTemplate/DataTemplateBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataTemplate/DataTemplateBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataTemplate/DataTemplateBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataTemplate/DataTemplateBase.ahk diff --git a/Lib/Shared/Volantis.Data/DataTemplate/StringTemplate.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DataTemplate/StringTemplate.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DataTemplate/StringTemplate.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DataTemplate/StringTemplate.ahk diff --git a/Lib/Shared/Volantis.Data/DiffResult.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/DiffResult.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/DiffResult.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/DiffResult.ahk diff --git a/Lib/Shared/Volantis.Data/Exception/DataException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/Exception/DataException.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/Exception/DataException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/Exception/DataException.ahk diff --git a/Lib/Shared/Volantis.Data/Factory/StructuredDataFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/Factory/StructuredDataFactory.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/Factory/StructuredDataFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/Factory/StructuredDataFactory.ahk diff --git a/Lib/Shared/Volantis.Data/LayerSource/LayerSourceBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/LayerSource/LayerSourceBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/LayerSource/LayerSourceBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/LayerSource/LayerSourceBase.ahk diff --git a/Lib/Shared/Volantis.Data/LayerSource/MapLayerSource.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/LayerSource/MapLayerSource.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/LayerSource/MapLayerSource.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/LayerSource/MapLayerSource.ahk diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk similarity index 90% rename from Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk index 56d06d51..60a4095d 100644 --- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk @@ -8,7 +8,7 @@ * * Example: * - Layer 1: Initial defaults - * - Layer 2: Defaults from datasource + * - Layer 2: Defaults from external sources * - Layer 3: Auto-detected defaults * - Layer 4: User configuration values * - Processor 1: Token expander @@ -25,10 +25,12 @@ class LayeredDataBase { cloner := "" userLayers := ["data"] loadingLayers := Map() + extraDataLayer := "data" + extraDataKey := "_extra" static NO_VALUE := ":NO_VAL:" - __New(cloner, processors, layerNames, layerSources) { + __New(cloner, processors, layerNames := "", layerSources := "") { this.cloner := cloner if (processors) { @@ -255,6 +257,54 @@ class LayeredDataBase { } } + GetExtraData(key := "") { + extraData := this.GetValue(this.extraDataKey, false, this.extraDataLayer, Map()) + + if (key) { + extraData := extraData.Has(key) ? extraData[key] : Map() + } + + return extraData + } + + SetExtraData(value, key := "") { + if (key) { + extraData := this.GetExtraData() + extraData[key] := value + value := extraData + } + + this.SetValue(this.extraDataKey, value, this.extraDataLayer) + + return this + } + + HasExtraData(key := "") { + hasData := this.HasValue(this.extraDataKey, this.extraDataLayer, false) + + if (hasData && key) { + extraData := this.GetExtraData() + hasData := extraData.Has(key) + } + + return hasData + } + + DeleteExtraData(key := "") { + if (key) { + extraData := this.GetExtraData() + + if (extraData.Has(key)) { + extraData.Delete(key) + this.SetExtraData(extraData) + } + } else { + this.DeleteValue(this.extraDataKey, this.extraDataLayer) + } + + return this + } + /** key: The key to retrieve @@ -486,8 +536,22 @@ class LayeredDataBase { return data } + GetUserLayers() { + layerNames := this.userLayers + + layers := Map() + + for index, layerName in layerNames { + layers[layerName] := this.GetLayer(layerName) + } + + return layers + } + CloneLayers(layers := "") { if (layers == "") { + layers := this.GetUserLayers() + } else if (layers == "*") { this.LoadAllLayers() layers := this.layers } else if (Type(layers) == "String") { @@ -500,8 +564,10 @@ class LayeredDataBase { cloned := Map() - for key, layer in layers { - cloned[key] := this.CloneData(layer) + if (layers) { + for key, layer in layers { + cloned[key] := this.CloneData(layer) + } } return cloned diff --git a/Lib/Shared/Volantis.Data/StructuredData/AhkVariable.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/AhkVariable.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/AhkVariable.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/AhkVariable.ahk diff --git a/Lib/Shared/Volantis.Data/StructuredData/BasicData.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/BasicData.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/BasicData.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/BasicData.ahk diff --git a/Lib/Shared/Volantis.Data/StructuredData/JsonData.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/JsonData.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/JsonData.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/JsonData.ahk diff --git a/Lib/Shared/Volantis.Data/StructuredData/ProtobufData.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/ProtobufData.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/ProtobufData.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/ProtobufData.ahk diff --git a/Lib/Shared/Volantis.Data/StructuredData/StructuredDataBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/StructuredDataBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/StructuredDataBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/StructuredDataBase.ahk diff --git a/Lib/Shared/Volantis.Data/StructuredData/VdfData.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/VdfData.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/VdfData.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/VdfData.ahk diff --git a/Lib/Shared/Volantis.Data/StructuredData/Xml.ahk b/launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/Xml.ahk similarity index 100% rename from Lib/Shared/Volantis.Data/StructuredData/Xml.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Data/StructuredData/Xml.ahk diff --git a/Lib/Shared/Volantis.Data/Volantis.Container.library.json b/launchpad_ahk/Lib/Shared/Volantis.Data/Volantis.Container.library.json similarity index 100% rename from Lib/Shared/Volantis.Data/Volantis.Container.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Data/Volantis.Container.library.json diff --git a/Lib/Shared/Volantis.Entity/ComponentManager/EntityTypeManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/ComponentManager/EntityTypeManager.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/ComponentManager/EntityTypeManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/ComponentManager/EntityTypeManager.ahk diff --git a/Lib/Shared/Volantis.Entity/DefinitionLoader/DiscoveryEntityDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/DiscoveryEntityDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/DefinitionLoader/DiscoveryEntityDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/DiscoveryEntityDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Entity/DefinitionLoader/EntityDefinitionLoaderBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/EntityDefinitionLoaderBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/DefinitionLoader/EntityDefinitionLoaderBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/EntityDefinitionLoaderBase.ahk diff --git a/Lib/Shared/Volantis.Entity/DefinitionLoader/EntityTypeDefinitionLoaderBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/EntityTypeDefinitionLoaderBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/DefinitionLoader/EntityTypeDefinitionLoaderBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/EntityTypeDefinitionLoaderBase.ahk diff --git a/Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityTypeDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityTypeDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityTypeDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/DefinitionLoader/ParameterEntityTypeDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk similarity index 62% rename from Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index a92092c7..079af972 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -2,15 +2,21 @@ class EntityBase { idVal := "" entityTypeIdVal := "" parentEntityObj := "" + parentEntityTypeId := "" + parentEntityId := "" + parentEntityStorage := false container := "" + app := "" eventMgr := "" dataObj := "" storageObj := "" idSanitizer := "" sanitizeId := true + loading := false loaded := false - merger := "" dataLayer := "data" + dataLoaded := false + merger := "" cloner := "" Id { @@ -20,14 +26,16 @@ class EntityBase { EntityTypeId { get => this.entityTypeIdVal + set => this.entityTypeIdVal := value } EntityType { get => this.GetEntityType() + set => this.EntityTypeId := value } FieldData { - Get => this.GetData().GetMergedData() + get => this.GetData().GetMergedData() } Name { @@ -35,14 +43,25 @@ class EntityBase { set => this.SetValue("name", value) } - UnmergedFieldData { + RawData { get => this.GetData().GetLayer(this.dataLayer) set => this.GetData().SetLayer(this.dataLayer, value) } ParentEntity { get => this.GetParentEntity() - set => this.SetParentEntity(value) + } + + ReferencedEntities { + get => this.GetReferencedEntities(false) + } + + ChildEntities { + get => this.GetReferencedEntities(true) + } + + ChildEntityData { + get => this.GetAllChildEntityData() } __Item[key := ""] { @@ -54,7 +73,8 @@ class EntityBase { return this.GetAllValues().__Enum(numberOfVars) } - __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "") { + __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "", parentEntityStorage := false) { + this.app := container.GetApp() this.idSanitizer := idSanitizer if (this.sanitizeId && this.idSanitizer) { @@ -68,25 +88,44 @@ class EntityBase { this.storageObj := storageObj this.merger := container.Get("merger.list") this.cloner := container.Get("cloner.list") + this.parentEntityStorage := parentEntityStorage - if (!parentEntity) { - parentEntity := this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) + if (!parentEntity && this.parentEntityObj) { + parentEntity := this.parentEntityObj } - if (parentEntity) { - this.SetParentEntity(parentEntity) - } + this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity) this._createEntityData() this.SetupEntity() if (autoLoad) { - this.LoadEntity(false, true) + this.LoadEntity() } } + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { + className := this.Prototype.__Class + + return %className%( + id, + entityTypeId, + container, + eventMgr, + storageObj, + idSanitizer, + autoLoad, + parentEntity, + parentEntityStorage + ) + } + _createEntityData() { - this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources()) + if (!this.dataLoaded) { + this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources()) + } + + this.dataLoaded := true } _getLayerNames() { @@ -95,39 +134,51 @@ class EntityBase { } _getLayerSources() { + layerSource := this.parentEntityStorage + ? ParentEntityLayerSource(this) + : EntityStorageLayerSource(this.storageObj, this.GetStorageId()) + return Map( "defaults", ObjBindMethod(this, "InitializeDefaults"), "auto", ObjBindMethod(this, "AutoDetectValues"), - "data", EntityStorageLayerSource(this.storageObj, this.GetStorageId()) + "data", layerSource ) } - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { - className := this.Prototype.__Class - - return %className%( - id, - entityTypeId, - container, - eventMgr, - storageObj, - idSanitizer, - parentEntity - ) + /** + * Get an array of all IDs + * + * List managed IDs and give modules a chance to add others. + */ + ListEntities(includeManaged := true, includeExtended := true) { + return this.container["entity_manager." . this.EntityTypeId] + .ListEntities(includeManaged, includeExtended) } - DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { - return "" + DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") { + event := EntityParentEvent(EntityEvents.ENTITY_DISCOVER_PARENT, this.entityTypeId, this, parentEntity) + this.eventMgr.DispatchEvent(event) + + if (event.ParentEntity) { + this.parentEntityObj := event.ParentEntity + } else if (event.ParentEntityId) { + this.parentEntityTypeId := event.ParentEntityTypeId + this.parentEntityId := event.ParentEntityId + this.parentEntityMgr := event.ParentEntityManager + ? event.ParentEntityManager + : container.Get("entity_manager." . event.ParentEntityTypeId) + + } + + this.parentEntityObj := event.ParentEntity + + return event.ParentEntity } GetParentEntity() { return this.parentEntityObj } - SetParentEntity(parentEntity) { - this.parentEntityObj := parentEntity - } - SetupEntity() { event := EntityEvent(EntityEvents.ENTITY_PREPARE, this.entityTypeId, this) this.eventMgr.DispatchEvent(event) @@ -137,26 +188,16 @@ class EntityBase { return this.GetData().GetMergedData(!raw) } - GetEntityTypeId() { - return this.entityTypeId - } - GetEntityType() { ; @todo Inject entity type manager service return this.container.Get("manager.entity_type")[this.EntityTypeId] } - InitializeDefaults(recurse := true) { + InitializeDefaults() { defaults := Map( "name", this.Id ) - if (recurse) { - for key, referencedEntity in this.GetReferencedEntities(true) { - this.merger.Merge(defaults, referencedEntity.InitializeDefaults()) - } - } - return defaults } @@ -202,26 +243,27 @@ class EntityBase { return this.GetData().DeleteValue(key, this.dataLayer) } - CreateSnapshot(name, recurse := true) { - this.GetData().CreateSnapshot(name) - + CreateSnapshot(name, recurse := false) { if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { - if (entityObj.HasOwnDataStorage()) { - entityObj.GetData().CreateSnapshot(name, recurse) - } + for index, entityObj in this.ChildEntities { + entityObj.GetData().CreateSnapshot(name, recurse) } } - return this - } + this.GetData().CreateSnapshot(name) - HasOwnDataStorage() { - return this.dataObj + return this } - RestoreSnapshot(name, recurse := true) { + RestoreSnapshot(name, recurse := false) { this.GetData().RestoreSnapshot(name) + + if (recurse) { + for index, entityObj in this.ChildEntities { + entityObj.GetData().RestoreSnapshot(name, recurse) + } + } + return this } @@ -230,24 +272,28 @@ class EntityBase { } LoadEntity(reload := false, recurse := false) { - loaded := false + if (this.loading) { + throw AppException("Attempting to load entity with a circular reference.") + } - if (!this.loaded || reload) { - this.RefreshEntityData(true) + if (!this.loading && this.dataLoaded && (!this.loaded || reload)) { + this.loading := true + this.RefreshEntityData(recurse) this.CreateSnapshot("original") this.loaded := true loaded := true - } + this.loading := false - if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { - entityObj.LoadEntity(reload, recurse) + if (recurse) { + for index, entityObj in this.ChildEntities { + entityObj.LoadEntity(reload, recurse) + } } - } - if (loaded) { - event := EntityEvent(EntityEvents.ENTITY_LOADED, this.entityTypeId, this) - this.eventMgr.DispatchEvent(event) + if (loaded) { + event := EntityEvent(EntityEvents.ENTITY_LOADED, this.entityTypeId, this) + this.eventMgr.DispatchEvent(event) + } } } @@ -255,7 +301,7 @@ class EntityBase { this.GetData().UnloadAllLayers(reloadUserData) if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { + for index, entityObj in this.ChildEntities { entityObj.RefreshEntityData(recurse, reloadUserData) } } @@ -264,16 +310,16 @@ class EntityBase { this.eventMgr.DispatchEvent(event) } - AutoDetectValues(recurse := true) { + AutoDetectValues() { values := Map() - if (recurse) { - for key, referencedEntity in this.GetReferencedEntities(true) { - this.merger.Merge(values, referencedEntity.AutoDetectValues(recurse)) - } - } + event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES, this.EntityTypeId, this, values) + this.eventMgr.DispatchEvent(event) + + event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES_ALTER, this.EntityTypeId, this, event.Values) + this.eventMgr.DispatchEvent(event) - return values + return event.Values } SaveEntity(recurse := true) { @@ -285,15 +331,15 @@ class EntityBase { event := EntityEvent(EntityEvents.ENTITY_PRESAVE, this.entityTypeId, this) this.eventMgr.DispatchEvent(event) - - this.GetData().SaveData() - this.CreateSnapshot("original") if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { + for index, entityObj in this.ChildEntities { entityObj.SaveEntity(recurse) } } + + this.GetData().SaveData() + this.CreateSnapshot("original") if (alreadyExists) { event := EntityEvent(EntityEvents.ENTITY_UPDATED, this.entityTypeId, this) @@ -317,11 +363,17 @@ class EntityBase { } } - DeleteEntity() { + DeleteEntity(recurse := false) { if (this.storageObj.HasData(this.GetStorageId())) { event := EntityEvent(EntityEvents.ENTITY_PREDELETE, this.entityTypeId, this) this.eventMgr.DispatchEvent(event) + if (recurse) { + for index, entityObj in this.ChildEntities { + entityObj.DeleteEntity(recurse) + } + } + this.storageObj.DeleteData(this.GetStorageId()) event := EntityEvent(EntityEvents.ENTITY_DELETED, this.entityTypeId, this) @@ -344,14 +396,14 @@ class EntityBase { return !!(changes.GetAdded().Count || changes.GetModified().Count || changes.GetDeleted().Count) } - DiffChanges(recursive := true) { + DiffChanges(recurse := true) { diff := this.GetData().DiffChanges("original", this.dataLayer) - if (recursive) { + if (recurse) { diffs := [diff] - for index, referencedEntity in this.GetReferencedEntities(true) { - diffs.Push(referencedEntity.DiffChanges(recursive)) + for index, referencedEntity in this.ChildEntities { + diffs.Push(referencedEntity.DiffChanges(recurse)) } diff := DiffResult.Combine(diffs) @@ -391,7 +443,7 @@ class EntityBase { result := "Cancel" while (mode) { - result := this.app.Service("manager.gui").Dialog(Map( + result := this.app["manager.gui"].Dialog(Map( "type", "SimpleEntityEditor", "mode", mode, "child", !!(ownerOrParent), @@ -435,4 +487,52 @@ class EntityBase { return text } + + UpdateDefaults(recurse := false) { + if (recurse) { + for key, child in this.ChildEntities { + child.UpdateDefaults(recurse) + } + } + + this.GetData().UnloadAllLayers(false) + } + + GetAllChildEntityData() { + return this.GetData().GetExtraData() + } + + GetChildEntityData(entityTypeId, entityId) { + dataKey := entityTypeId . "." . entityId + + childData := this.GetData().GetExtraData(dataKey) + + return childData ? childData : Map() + } + + SetChildEntityData(entityTypeId, entityId, data) { + dataKey := entityTypeId . "." . entityId + + if (!data) { + data := Map() + } + + this.GetData().SetExtraData(data, dataKey) + + return this + } + + HasChildEntityData(entityTypeId, entityId) { + dataKey := entityTypeId . "." . entityId + + return this.GetData().HasExtraData(dataKey) + } + + DeleteChildEntityData(entityTypeId, entityId) { + dataKey := entityTypeId . "." . entityId + + this.GetData().DeleteExtraData(dataKey) + + return this + } } diff --git a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk similarity index 86% rename from Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk index 2776d249..38709289 100644 --- a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk @@ -11,10 +11,29 @@ class FieldableEntity extends EntityBase { get => this.GetFieldDefinitions() } - __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true) { - this.entityFieldFactory := container.Get("entity_field_factory." . entityTypeId) - this.entityWidgetFactory := container.Get("entity_widget_factory." . entityTypeId) - super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad) + __New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "", parentEntityStorage := false) { + this.entityFieldFactory := fieldFactory + this.entityWidgetFactory := widgetFactory + + super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad, parentEntity, parentEntityStorage) + } + + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { + className := this.Prototype.__Class + + return %className%( + id, + entityTypeId, + container, + container.Get("entity_field_factory." . entityTypeId), + container.Get("entity_widget_factory." . entityTypeId), + eventMgr, + storageObj, + idSanitizer, + autoLoad, + parentEntity, + parentEntityStorage + ) } GetDefaultFieldGroups() { @@ -235,8 +254,8 @@ class FieldableEntity extends EntityBase { } } - InitializeDefaults(recurse := true) { - defaults := super.InitializeDefaults(recurse) + InitializeDefaults() { + defaults := super.InitializeDefaults() for key, fieldObj in this.GetFields() { defaults[fieldObj.Definition["storageKey"]] := fieldObj.Definition["default"] diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk new file mode 100644 index 00000000..91d37e01 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk @@ -0,0 +1,62 @@ +class BooleanEntityField extends EntityFieldBase { + DefinitionDefaults(fieldDefinition) { + defaults := super.DefinitionDefaults(fieldDefinition) + defaults["widget"] := "checkbox" + defaults["default"] := false + return defaults + } + + GetValue(index := "") { + value := super.GetValue(index) + + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + newValues := [] + + for singleIndex, singleValue in value { + isTrue := StrLower(singleValue) + + if (isTrue == "true" || isTrue == "false") { + isTrue := (isTrue == "true") + } + + newValues.Push(!!isTrue) + } + + value := newValues + + if (!value.Length) { + value.Push("") + } + + if (index && !value.Has(index)) { + throw AppException("Index out of range") + } + + if (index) { + return value[index] + } else if (this.multiple) { + return value + } else { + return value[1] + } + } + + SetValue(value, index := "") { + if (index || !this.multiple || !HasBase(value, Array.Prototype)) { + value := !!value + } else { + newValues := [] + + for singleIndex, singleValue in value { + newValues.Push(!!singleValue) + } + + value := newValues + } + + super.SetValue(value, index) + } +} diff --git a/Lib/Shared/Volantis.Entity/EntityField/ClassNameEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/ClassNameEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/ClassNameEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/ClassNameEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/DirEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/DirEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/DirEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/DirEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk similarity index 79% rename from Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk index a13646a4..c4856c10 100644 --- a/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk @@ -10,6 +10,7 @@ class EntityFieldBase { userLayer := "data" cloner := "" merger := "" + multiple := false needsEntityRefresh := false static VALUE_TYPE_DATA := "data" @@ -19,6 +20,10 @@ class EntityFieldBase { get => this.fieldDefinition set => this.fieldDefinition := value } + + IsMultiple { + get => this.multiple + } __New(fieldTypeId, entityObj, container, eventMgr, dataObj, fieldKey, fieldDefinition) { this.fieldTypeId := fieldTypeId @@ -31,6 +36,11 @@ class EntityFieldBase { this.merger := container.Get("merger.list") this.Definition := ParameterBag(this.DefinitionDefaults(fieldDefinition)) this.Definition.Add(fieldDefinition) + this.multiple := (this.Definition["cardinality"] == 0 || this.Definition["cardinality"] > 1) + + if (this.multiple && this.Definition["default"] && !HasBase(this.Definition["default"], Array.Prototype)) { + this.Definition["default"] := [this.Definition["default"]] + } } static Create(container, entityTypeId, entityObj, dataObj, fieldId, definition) { @@ -98,9 +108,8 @@ class EntityFieldBase { "formField", true, "group", "general", "help", "", - "limit", false, + "cardinality", 1, "modes", Map(), - "multiple", false, "processValue", false, "refreshEntityOnChange", false, "required", false, @@ -139,15 +148,49 @@ class EntityFieldBase { return result } - GetValue() { - return this.GetRawValue() + GetValue(index := "") { + return this.GetRawValue(index) } - GetRawValue() { - return this._callback("GetValue") + GetRawValue(index := "") { + value := this._callback("GetValue") + + if (this.multiple && !HasBase(value, Array.Prototype)) { + value := [value] + } + + if (this.multiple && index && !value.Has(index)) { + throw AppException("Index " . index . " does not exist in field " . this.fieldKey . ".") + } + + if (this.multiple && index) { + value := value[index] + } + + return value } - SetValue(value) { + SetValue(value, index := "") { + if (index && this.multiple) { + existingValues := this.GetRawValue() + + if (existingValues.Length < (index + 1)) { + throw AppException("Index to set is too high, there are only " . existingValues.Length . " values in field " . this.fieldKey . ".") + } + + if (existingValues.Length < index) { + existingValues.Push(value) + } else { + existingValues[index] := value + } + + value := existingValues + } + + if (this.multiple && !HasBase(value, Array.Prototype)) { + value := [value] + } + this._callback("SetValue", value) this.RefreshEntity() return this @@ -175,9 +218,19 @@ class EntityFieldBase { } Validate(value) { - return this - .CreateValidator(this.GetValidators(value)) - .Validate(value) + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + results := [] + + validator := this.CreateValidator(this.GetValidators(value)) + + for index, singleValue in value { + results.Push(validator.Validate(singleValue)) + } + + return this.multiple ? results : results[1] } /** @@ -187,11 +240,9 @@ class EntityFieldBase { _parseLayer(layer := "", allowAll := true) { if (!layer) { layer := this.Definition["dataLayer"] + } else if (layer == "*" && !allowAll) { + throw EntityException("Cannot pass wildcard for this layer value.") } else if (layer == "*") { - if (!allowAll) { - throw EntityException("Cannot pass wildcard for this layer value.") - } - layer := "" } @@ -234,6 +285,14 @@ class EntityFieldBase { allowEmpty ) + if (this.multiple && HasBase(val, Array.Prototype)) { + if (val.Length) { + val := (val[1] != "") + } else { + val := false + } + } + if (negate) { val := !val } @@ -244,6 +303,10 @@ class EntityFieldBase { _hasDefaultValue(allowEmpty := true, negate := false) { hasValue := allowEmpty ? true : !!(this.Definition["default"]) + if (hasValue && !allowEmpty && this.multiple && HasBase(this.Definition["default"], Array.Prototype)) { + hasValue := !!(this.Definition["default"][1]) + } + if (negate) { hasValue := !hasValue } diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk new file mode 100644 index 00000000..af424717 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk @@ -0,0 +1,131 @@ +class EntityReferenceField extends EntityFieldBase { + managerObj := "" + + DefinitionDefaults(fieldDefinition) { + entityTypeId := fieldDefinition["entityType"] + + if (!entityTypeId) { + throw EntityException("Entity reference fields require an entityType mapping.") + } + + defaults := super.DefinitionDefaults(fieldDefinition) + + defaults["entityType"] := entityTypeId + defaults["widget"] := "select" + defaults["child"] := false + defaults["storeEntityData"] := false + defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetEntitySelectOptions") + defaults["selectConditions"] := [] + + return defaults + } + + GetValidators(value) { + validators := super.GetValidators(value) + + if (value) { + ; @todo check if entity exists + + if (this.Definition["child"]) { + ; @todo Add validator for child entity + } + } + + return validators + } + + GetValue(index := "") { + value := super.GetValue(index) + + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + entities := [] + entityManager := this._entityManager() + + for entityIndex, entityId in value { + if (!entityId) { + entities.Push("") + } else if (entityManager.Has(entityId)) { + entities.Push(entityManager[entityId]) + } else { + throw AppException("Entity with ID '" . entityId . "' does not exist.") + } + } + + if (!this.multiple || index) { + value := entities.Length ? entities[1] : "" + } else { + value := entities + } + + return value + } + + SetValue(value, index := "") { + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + newValues := [] + + for singleIndex, singleValue in value { + if (HasBase(singleValue, EntityBase.Prototype)) { + newValues.Push(singleValue.Id) + } else if (Type(singleValue) == "String") { + newValues.Push(singleValue) + } else { + throw AppException("Invalid entity reference data.") + } + } + + value := newValues + + if (!this.multiple || index) { + value := value.Length ? value[1] : "" + } + + super.SetValue(value, index) + return this + } + + GetEntitySelectOptions() { + options := this._getSelectQuery().Execute() + + if (!this.Definition["required"]) { + options.InsertAt(1, "") + } + + return options + } + + _entityManager(entityTypeId := "") { + if (!this.managerObj) { + if (!entityTypeId) { + entityTypeId := this.Definition["entityType"] + } + + this.managerObj := this.container["entity_manager." . entityTypeId] + } + + return this.managerObj + } + + _getSelectQuery() { + query := this._entityManager().EntityQuery(EntityQuery.RESULT_TYPE_IDS) + conditions := this.Definition["selectConditions"] + + if (conditions) { + if (Type(conditions) != "Array") { + conditions := [conditions] + } + + for index, condition in conditions { + query.Condition(condition) + } + } + + return query + } +} diff --git a/Lib/Shared/Volantis.Entity/EntityField/FileEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/FileEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/FileEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/FileEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/FileEntityFieldBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/FileEntityFieldBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/FileEntityFieldBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/FileEntityFieldBase.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/HotkeyEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/HotkeyEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/HotkeyEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/HotkeyEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/IconFileEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/IconFileEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/IconFileEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/IconFileEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/IdEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/IdEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/IdEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/IdEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/NumberEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/NumberEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/NumberEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/NumberEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk similarity index 64% rename from Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk index 10ed2747..b0c21423 100644 --- a/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk @@ -1,6 +1,13 @@ class ServiceReferenceField extends EntityFieldBase { - ReferencedObject { - get => this.GetValue() + DefinitionDefaults(fieldDefinition) { + defaults := super.DefinitionDefaults(fieldDefinition) + + defaults["servicePrefix"] := "" + defaults["widget"] := "select" + defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetServiceSelectOptions") + defaults["selectConditions"] := [] + + return defaults } GetValidators(value) { @@ -13,30 +20,34 @@ class ServiceReferenceField extends EntityFieldBase { return validators } - DefinitionDefaults(fieldDefinition) { - defaults := super.DefinitionDefaults(fieldDefinition) + GetValue(index := "") { + value := super.GetValue(index) - defaults["servicePrefix"] := "" - defaults["widget"] := "select" - defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetEntitySelectOptions") - defaults["selectConditions"] := [] - - return defaults - } + if (!HasBase(value, Array.Prototype)) { + value := [value] + } - GetValue() { - serviceObj := "" - serviceId := super.GetValue() + newValues := [] - if (serviceId ) { - if (Type(serviceId) != "String") { - serviceObj := serviceId + for singleIndex, singleValue in value { + if (Type(singleValue) != "String") { + serviceObj := singleValue } else { - serviceObj := this._getService(serviceId) + serviceObj := this._getService(singleValue) + } + + if (serviceObj) { + newValues.Push(serviceObj) } } - return serviceObj + value := newValues + + if (!this.multiple || index) { + value := value.Length ? value[1] : "" + } + + return value } _getService(serviceId) { @@ -53,8 +64,20 @@ class ServiceReferenceField extends EntityFieldBase { return serviceObj } - SetValue(value) { - super.SetValue(this._getServiceId(value)) + SetValue(value, index := "") { + if (index || !this.multiple || !HasBase(value, Array.Prototype)) { + value := this._getServiceId(singleValue) + } else { + newValues := [] + + for singleIndex, singleValue in value { + newValues[singleIndex] = this._getServiceId(singleValue) + } + + value := newValues + } + + super.SetValue(value, index) } _getServiceId(value) { @@ -69,7 +92,7 @@ class ServiceReferenceField extends EntityFieldBase { return this.container.Query(this.Definition["servicePrefix"], ContainerQuery.RESULT_TYPE_NAMES, false, true) } - GetEntitySelectOptions() { + GetServiceSelectOptions() { query := this._getSelectQuery() conditions := this.Definition["selectConditions"] diff --git a/Lib/Shared/Volantis.Entity/EntityField/StringEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/StringEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/StringEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/StringEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/TimeOffsetEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/TimeOffsetEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/TimeOffsetEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/TimeOffsetEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityField/UrlEntityField.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/UrlEntityField.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityField/UrlEntityField.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityField/UrlEntityField.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/CheckboxEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/CheckboxEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/CheckboxEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/CheckboxEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/ComboBoxEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/ComboBoxEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/ComboBoxEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/ComboBoxEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/DirectoryEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/DirectoryEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/DirectoryEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/DirectoryEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFieldWidgetBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFieldWidgetBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFieldWidgetBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFieldWidgetBase.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFormEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFormEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFormEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntityFormEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntitySelectEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntitySelectEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/EntitySelectEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/EntitySelectEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/FileEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/FileEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/FileEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/FileEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/HotkeyEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/HotkeyEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/HotkeyEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/HotkeyEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/LocationEntityFieldWidgetBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/LocationEntityFieldWidgetBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/LocationEntityFieldWidgetBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/LocationEntityFieldWidgetBase.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/NumberEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/NumberEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/NumberEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/NumberEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/SelectEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/SelectEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/SelectEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/SelectEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/TextEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/TextEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/TextEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/TextEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/TimeOffsetEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/TimeOffsetEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/TimeOffsetEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/TimeOffsetEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityFieldWidget/UrlEntityFieldWidget.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/UrlEntityFieldWidget.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityFieldWidget/UrlEntityFieldWidget.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityFieldWidget/UrlEntityFieldWidget.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityForm/EntityFormBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityForm/EntityFormBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityForm/EntityFormBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityForm/EntityFormBase.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityForm/SimpleEntityForm.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityForm/SimpleEntityForm.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityForm/SimpleEntityForm.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityForm/SimpleEntityForm.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityManager/BasicEntityManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityManager/BasicEntityManager.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityManager/BasicEntityManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityManager/BasicEntityManager.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk similarity index 79% rename from Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk index c84eacc2..00399489 100644 --- a/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk @@ -94,4 +94,30 @@ class EntityManagerBase extends ComponentManagerBase { childManager.LoadComponents(reloadComponents) } } + + /** + * Get an array of all IDs + * + * List managed IDs and give modules a chance to add others. + */ + ListEntities(includeManaged := true, includeExtended := true) { + entities := includeManaged + ? this.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute() + : [] + + if (includeExtended) { + event := EntityListEvent( + EntityEvents.ENTITY_LIST_ENTITIES, + this.entityTypeId, + entities, + includeManaged, + includeExtended + ) + this.eventMgr.DispatchEvent(event) + + entities := event.EntityList + } + + return entities + } } diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/ConfigEntityStorage.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/ConfigEntityStorage.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityStorage/ConfigEntityStorage.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/ConfigEntityStorage.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk similarity index 94% rename from Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk index b471ec6f..ebf04287 100644 --- a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk @@ -24,6 +24,7 @@ class EntityStorageBase { id := this._dereferenceId(idOrObj) data := this._dereferenceData(idOrObj, data) this._saveEntityData(id, data) + return this } _saveEntityData(id, data) { @@ -48,6 +49,7 @@ class EntityStorageBase { DeleteData(idOrObj) { this._deleteEntityData(this._dereferenceId(idOrObj)) + return this } _dereferenceId(idOrObj) { @@ -64,7 +66,7 @@ class EntityStorageBase { _dereferenceData(idOrObj, data := "") { if (HasBase(idOrObj, EntityBase.Prototype) && !data) { - data := idOrObj.UnmergedFieldData + data := idOrObj.RawData } return data diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk new file mode 100644 index 00000000..38cb4c2b --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk @@ -0,0 +1,3 @@ +class NullEntityStorage extends EntityStorageBase { + +} diff --git a/Lib/Shared/Volantis.Entity/EntityType/BasicEntityType.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityType/BasicEntityType.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityType/BasicEntityType.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityType/BasicEntityType.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityType/EntityTypeBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/EntityType/EntityTypeBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/EntityType/EntityTypeBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/EntityType/EntityTypeBase.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityDataProcessorsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityDataProcessorsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityDataProcessorsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityDataProcessorsEvent.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk new file mode 100644 index 00000000..2a5bf92c --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk @@ -0,0 +1,17 @@ +class EntityDetectValuesEvent extends EntityEvent { + _valuesMap := "" + + Values { + get => this._valuesMap + } + + __New(eventName, entityTypeId, entityObj, values := "") { + if (!values) { + values := Map() + } + + this._valuesMap := values + + super.__New(eventName, entityTypeId, entityObj) + } +} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityFieldDefinitionsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityFieldDefinitionsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityFieldDefinitionsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityFieldDefinitionsEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityFieldGroupsEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityFieldGroupsEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityFieldGroupsEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityFieldGroupsEvent.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk new file mode 100644 index 00000000..f8431925 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk @@ -0,0 +1,13 @@ +class EntityLayerSourcesEvent extends EntityEvent { + _layerSourcesObj := "" + + LayerSources { + get => this._layerSourcesObj + } + + __New(eventName, entityTypeId, entityObj, layerSourcesObj) { + this._layerSourcesObj := layerSourcesObj + + super.__New(eventName, entityTypeId, entityObj) + } +} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityLayersEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityLayersEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityLayersEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityLayersEvent.ahk diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk new file mode 100644 index 00000000..9782b0be --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk @@ -0,0 +1,32 @@ +class EntityListEvent extends EventBase { + _entityTypeId := "" + _entityList := [] + _includeManaged := false + _includeExtended := false + + __New(eventName, entityTypeId, entityList, includeManaged, includeExtended) { + this._entityTypeId := entityTypeId + this._entityList := entityList + this._includeManaged := includeManaged + this._includeExtended := includeExtended + + super.__New(eventName) + } + + EntityTypeId { + get => this._entityTypeId + } + + EntityList { + get => this._entityList + set => this._entityList := value + } + + IncludeManaged { + get => this._includeManaged + } + + IncludeExtended { + get => this._includeExtended + } +} diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk new file mode 100644 index 00000000..c95ed335 --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk @@ -0,0 +1,28 @@ +class EntityParentEvent extends EntityEvent { + _parentEntity := "" + _parentEntityTypeId := "" + _parentEntityId := "" + + __New(eventName, entityTypeId, entityObj, parentEntity := "", parentEntityTypeId := "", parentEntityId := "") { + this._parentEntity := parentEntity + this._parentEntityTypeId := parentEntityTypeId + this._parentEntityId := parentEntityId + + super.__New(eventName, entityTypeId, entityObj) + } + + ParentEntity { + get => this._parentEntity + set => this._parentEntity := value + } + + ParentEntityTypeId { + get => this._parentEntityTypeId + set => this._parentEntityTypeId := value + } + + ParentEntityId { + get => this._parentEntityId + set => this._parentEntityId := value + } +} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityReferenceEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityReferenceEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityReferenceEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityReferenceEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityRefreshEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityRefreshEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityRefreshEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityRefreshEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityValidateEvent.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityValidateEvent.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Event/EntityValidateEvent.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Event/EntityValidateEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk similarity index 75% rename from Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk index 1076b822..2d2ed47a 100644 --- a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk @@ -1,6 +1,6 @@ class EntityEvents { - static ENTITY_STORAGE_OBJECTS := 0x4020 - static ENTITY_STORAGE_OBJECTS_ALTER := 0x4022 + static ENTITY_LAYER_SOURCES := 0x4020 + static ENTITY_LAYER_SOURCES_ALTER := 0x4022 static ENTITY_DATA_PROCESSORS := 0x4030 static ENTITY_DATA_PROCESSORS_ALTER := 0x4032 static ENTITY_PREPARE := 0x4035 @@ -15,10 +15,14 @@ class EntityEvents { static ENTITY_REFRESH := 0x4065 static ENTITY_DATA_LAYERS := 0x4070 static ENTITY_DATA_LAYERS_ALTER := 0x4071 + static ENTITY_DETECT_VALUES := 0x4075 + static ENTITY_DETECT_VALUES_ALTER := 0x4076 static ENTITY_VALIDATE := 0x4080 static ENTITY_FIELD_DEFINITIONS := 0x4085 static ENTITY_FIELD_DEFINITIONS_ALTER := 0x4087 static ENTITY_FIELD_GROUPS := 0x4090 static ENTITY_FIELD_GROUPS_ALTER := 0x4092 static ENTITY_REFERENCE_ENTITY_SAVED := 0x4095 + static ENTITY_LIST_ENTITIES := 0x4098 + static ENTITY_DISCOVER_PARENT := 0x4100 } diff --git a/Lib/Shared/Volantis.Entity/Exception/EntityException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Exception/EntityException.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Exception/EntityException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Exception/EntityException.ahk diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk similarity index 96% rename from Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk index 19af31bc..7d45933b 100644 --- a/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk @@ -110,6 +110,6 @@ class EntityFactory { throw EntityException("Unable to create entity '" . id . "' of type '" . entityTypeObj . "' in EntityFactory") } - return %entityTypeObj%.Create(this.container, this.eventMgr, id, this.entityTypeId, this.storageObj, this.idSanitizer, parentEntity) + return %entityTypeObj%.Create(this.container, this.eventMgr, id, this.entityTypeId, this.storageObj, this.idSanitizer, true, parentEntity, this.definition["parent_entity_storage"]) } } diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityFieldFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFieldFactory.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Factory/EntityFieldFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFieldFactory.ahk diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityFieldWidgetFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFieldWidgetFactory.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Factory/EntityFieldWidgetFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFieldWidgetFactory.ahk diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityFormFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFormFactory.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Factory/EntityFormFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityFormFactory.ahk diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk similarity index 90% rename from Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk index 88d866bd..fe637a5b 100644 --- a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk @@ -30,6 +30,7 @@ class EntityTypeFactory { "event_manager", "manager.event", "notifier", "notifier", "parent_entity_type", "", + "parent_entity_storage", false, "default_icon", "cube-outline", "icon_field", "IconSrc", "allow_view", false, @@ -40,7 +41,8 @@ class EntityTypeFactory { "manager_view_mode_parameter", "", "manager_gui", "ManageEntitiesWindow", "manager_link_in_tools_menu", false, - "manager_menu_link_text", "" + "manager_menu_link_text", "", + "storage_type", "persistent" ) } @@ -97,7 +99,7 @@ class EntityTypeFactory { ), ) - if (definition["storage_class"] == "ConfigEntityStorage" && definition["storage_config_path_parameter"]) { + if (definition["storage_type"] == "persistent" && definition["storage_class"] == "ConfigEntityStorage" && definition["storage_config_path_parameter"]) { services["config_storage." . id] := Map( "class", definition["storage_config_storage_class"], "arguments", ["@@" . definition["storage_config_path_parameter"], definition["storage_config_storage_parent_key"]] @@ -107,6 +109,11 @@ class EntityTypeFactory { "class", "PersistentConfig", "arguments", ["@config_storage." . id, "@{}", "entity_data." . id] ) + } else if (definition["storage_type"] == "runtime") { + services["config." . id] := Map( + "class", "RuntimeConfig", + "arguments", ["@{}", "entity_data." . id] + ) } entityClass := definition["entity_class"] diff --git a/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk similarity index 96% rename from Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk index b58e5bb9..ec182c25 100644 --- a/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk @@ -24,5 +24,7 @@ class EntityStorageLayerSource extends LayerSourceBase { if (this.HasData()) { this.storageObj.DeleteData(this.storageId) } + + return this } } diff --git a/launchpad_ahk/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk new file mode 100644 index 00000000..dfa685aa --- /dev/null +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk @@ -0,0 +1,55 @@ +class ParentEntityLayerSource extends LayerSourceBase { + entityObj := "" + + __New(entityObj) { + this.entityObj := entityObj + } + + SaveData(data := "") { + this._validateParentEntity() + + this.entityObj.ParentEntity + .SetChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id, data) + + return this + } + + LoadData() { + this._validateParentEntity() + + return this.entityObj.ParentEntity + .GetChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id) + } + + HasData() { + this._validateParentEntity() + + return this.entityObj.ParentEntity + .HasChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id) + } + + DeleteData() { + this._validateParentEntity() + + this.entityObj.ParentEntity + .DeleteChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id) + + return this + } + + _validateParentEntity() { + if (!this.entityObj.ParentEntity) { + throw AppException("Parent entity not set.") + } + + if (!HasBase(this.entityObj.ParentEntity, EntityBase.Prototype)) { + throw AppException("Parent entity is not an entity.") + } + + parentData := this.entityObj.ParentEntity.GetData() + + if (!parentData) { + throw AppException("Parent entity data is not set.") + } + } +} diff --git a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk similarity index 74% rename from Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk index 2a3c041d..26addd79 100644 --- a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk @@ -3,31 +3,60 @@ class EntityData extends LayeredDataBase { entity := "" eventMgr := "" - __New(entity, layerNames, layerSources) { + __New(entity, layerNames := "", layerSources := "") { this.entityTypeId := entity.EntityTypeId this.entity := entity this.eventMgr := entity.eventMgr super.__New( entity.cloner, - this._createProcessors(), - this._getLayerNames(layerNames), - this._collectEntityStorage(layerSources) + this._createProcessors(), + layerNames, + layerSources ) } - _collectEntityStorage(layerSources) { + InitializeLayers(layerNames) { + if (!layerNames) { + layerNames := [] + } + + this._appendLayerNames(["defaults"], layerNames) + + event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS, this.entityTypeId, this.entity, layerNames) + this.eventMgr.DispatchEvent(event) + + layerNames := event.Layers + this._appendLayerNames(["auto", "data"], layerNames) + + event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS_ALTER, this.entityTypeId, this.entity, layerNames) + this.eventMgr.DispatchEvent(event) + + layerNames := event.Layers + layers := Map() + + for index, layerName in layerNames { + this.layerPriority.Push(layerName) + layers[layerName] := Map() + } + + this.SetLayers(layers) + } + + SetLayerSources(layerSources) { if (!layerSources.Has("defaults")) { layerSources["defaults"] := ObjBindMethod(this.entity, "InitializeDefaults") } - event := EntityStorageEvent(EntityEvents.ENTITY_STORAGE_OBJECTS, this.entityTypeId, this.entity, layerSources) + event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES, this.entityTypeId, this.entity, layerSources) this.eventMgr.DispatchEvent(event) - event := EntityStorageEvent(EntityEvents.ENTITY_STORAGE_OBJECTS_ALTER, this.entityTypeId, this.entity, event.Storage) + event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES_ALTER, this.entityTypeId, this.entity, event.LayerSources) this.eventMgr.DispatchEvent(event) - return event.Storage + for key, source in event.LayerSources { + this.SetLayerSource(key, source) + } } _createProcessors() { @@ -45,25 +74,6 @@ class EntityData extends LayeredDataBase { return event.Processors } - _getLayerNames(layerNames) { - if (!layerNames) { - layerNames := [] - } - - this._appendLayerNames(["defaults"], layerNames) - - event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS, this.entityTypeId, this.entity, layerNames) - this.eventMgr.DispatchEvent(event) - - layerNames := event.Layers - this._appendLayerNames(["auto", "data"], layerNames) - - event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS_ALTER, this.entityTypeId, this.entity, layerNames) - this.eventMgr.DispatchEvent(event) - - return event.Layers - } - _appendLayerNames(namesToAppend, existingNames) { for index, name in namesToAppend { exists := false diff --git a/Lib/Shared/Volantis.Entity/Query/EntityQuery.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Query/EntityQuery.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Query/EntityQuery.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Query/EntityQuery.ahk diff --git a/Lib/Shared/Volantis.Entity/Validator/BasicValidator.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Validator/BasicValidator.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Validator/BasicValidator.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Validator/BasicValidator.ahk diff --git a/Lib/Shared/Volantis.Entity/Validator/ValidatorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Entity/Validator/ValidatorBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Entity/Validator/ValidatorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Validator/ValidatorBase.ahk diff --git a/Lib/Shared/Volantis.Entity/Volantis.Entity.library.json b/launchpad_ahk/Lib/Shared/Volantis.Entity/Volantis.Entity.library.json similarity index 100% rename from Lib/Shared/Volantis.Entity/Volantis.Entity.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Entity/Volantis.Entity.library.json diff --git a/Lib/Shared/Volantis.File/ArchiveFile/ArchiveFileBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.File/ArchiveFile/ArchiveFileBase.ahk similarity index 100% rename from Lib/Shared/Volantis.File/ArchiveFile/ArchiveFileBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.File/ArchiveFile/ArchiveFileBase.ahk diff --git a/Lib/Shared/Volantis.File/ArchiveFile/ZipArchive.ahk b/launchpad_ahk/Lib/Shared/Volantis.File/ArchiveFile/ZipArchive.ahk similarity index 100% rename from Lib/Shared/Volantis.File/ArchiveFile/ZipArchive.ahk rename to launchpad_ahk/Lib/Shared/Volantis.File/ArchiveFile/ZipArchive.ahk diff --git a/Lib/Shared/Volantis.File/ArchiveFile/ZipArchive7z.ahk b/launchpad_ahk/Lib/Shared/Volantis.File/ArchiveFile/ZipArchive7z.ahk similarity index 100% rename from Lib/Shared/Volantis.File/ArchiveFile/ZipArchive7z.ahk rename to launchpad_ahk/Lib/Shared/Volantis.File/ArchiveFile/ZipArchive7z.ahk diff --git a/Lib/Shared/Volantis.File/Backup/BackupBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.File/Backup/BackupBase.ahk similarity index 100% rename from Lib/Shared/Volantis.File/Backup/BackupBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.File/Backup/BackupBase.ahk diff --git a/Lib/Shared/Volantis.File/Backup/FileBackup.ahk b/launchpad_ahk/Lib/Shared/Volantis.File/Backup/FileBackup.ahk similarity index 100% rename from Lib/Shared/Volantis.File/Backup/FileBackup.ahk rename to launchpad_ahk/Lib/Shared/Volantis.File/Backup/FileBackup.ahk diff --git a/Lib/Shared/Volantis.File/ExeProcess/ExeProcess.ahk b/launchpad_ahk/Lib/Shared/Volantis.File/ExeProcess/ExeProcess.ahk similarity index 100% rename from Lib/Shared/Volantis.File/ExeProcess/ExeProcess.ahk rename to launchpad_ahk/Lib/Shared/Volantis.File/ExeProcess/ExeProcess.ahk diff --git a/Lib/Shared/Volantis.File/Volantis.File.library.json b/launchpad_ahk/Lib/Shared/Volantis.File/Volantis.File.library.json similarity index 100% rename from Lib/Shared/Volantis.File/Volantis.File.library.json rename to launchpad_ahk/Lib/Shared/Volantis.File/Volantis.File.library.json diff --git a/Lib/Shared/Volantis.Module/ComponentManager/ModuleManager.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/ComponentManager/ModuleManager.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/ComponentManager/ModuleManager.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/ComponentManager/ModuleManager.ahk diff --git a/Lib/Shared/Volantis.Module/DefinitionLoader/ModuleDefinitionLoader.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/DefinitionLoader/ModuleDefinitionLoader.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/DefinitionLoader/ModuleDefinitionLoader.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/DefinitionLoader/ModuleDefinitionLoader.ahk diff --git a/Lib/Shared/Volantis.Module/Exception/ModuleException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/Exception/ModuleException.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/Exception/ModuleException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/Exception/ModuleException.ahk diff --git a/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk similarity index 98% rename from Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk index b873f5a9..e5b73f15 100644 --- a/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk @@ -22,7 +22,7 @@ class ModuleFactory { ), "module." . key, Map( "class", this.classMap.Has(key) ? this.classMap[key] : "SimpleModule", - "arguments", [key, "@module_info." . key, "@module_config." . key], + "arguments", [key, "@module_info." . key, "@module_config." . key, isCore], "file", file, "enabled", enabled, "core", isCore, diff --git a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk similarity index 78% rename from Lib/Shared/Volantis.Module/Module/ModuleBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk index af874ef4..2712c2b5 100644 --- a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk @@ -2,11 +2,13 @@ class ModuleBase { moduleInfo := "" config := "" key := "" + _core := false - __New(key, moduleInfo, config) { + __New(key, moduleInfo, config, isCore) { this.key := key this.moduleInfo := moduleInfo this.config := config + this._core := isCore } IsEnabled() { @@ -14,7 +16,7 @@ class ModuleBase { } IsCore() { - return (this.config.Has("core") && this.config["core"]) + return this._core } GetConfigValue(key, defaultValue := "") { @@ -28,7 +30,20 @@ class ModuleBase { } GetVersion() { - return this.moduleInfo.Has("version") ? this.moduleInfo["version"] : "" + versionStr := this.moduleInfo.Has("version") ? this.moduleInfo["version"] : "" + + if (versionStr == "{{VERSION}}") { + + if (AppBase.Instance) { + versionStr := AppBase.Instance.Version + } + + if (versionStr == "{{VERSION}}") { + versionStr := "Core" + } + } + + return versionStr } GetServiceFiles() { diff --git a/Lib/Shared/Volantis.Module/Module/SimpleModule.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/Module/SimpleModule.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/Module/SimpleModule.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/Module/SimpleModule.ahk diff --git a/Lib/Shared/Volantis.Module/ModuleInfo/FileModuleInfo.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/ModuleInfo/FileModuleInfo.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/ModuleInfo/FileModuleInfo.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/ModuleInfo/FileModuleInfo.ahk diff --git a/Lib/Shared/Volantis.Module/ModuleInfo/ModuleInfoBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/ModuleInfo/ModuleInfoBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/ModuleInfo/ModuleInfoBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/ModuleInfo/ModuleInfoBase.ahk diff --git a/Lib/Shared/Volantis.Module/ModuleInfo/ParameterModuleInfo.ahk b/launchpad_ahk/Lib/Shared/Volantis.Module/ModuleInfo/ParameterModuleInfo.ahk similarity index 100% rename from Lib/Shared/Volantis.Module/ModuleInfo/ParameterModuleInfo.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Module/ModuleInfo/ParameterModuleInfo.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/ClassNameExistsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/ClassNameExistsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/ClassNameExistsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/ClassNameExistsCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/ConditionBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/ConditionBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/ConditionBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/ConditionBase.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/ContainsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/ContainsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/ContainsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/ContainsCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/DirConditionBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/DirConditionBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/DirConditionBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/DirConditionBase.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/DirExistsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/DirExistsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/DirExistsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/DirExistsCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/EndsWithCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/EndsWithCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/EndsWithCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/EndsWithCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/FileConditionBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileConditionBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/FileConditionBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileConditionBase.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/FileContainsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileContainsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/FileContainsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileContainsCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/FileExistsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileExistsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/FileExistsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileExistsCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/FileModifiedAfterCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileModifiedAfterCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/FileModifiedAfterCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/FileModifiedAfterCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/GreaterThanCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/GreaterThanCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/GreaterThanCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/GreaterThanCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsEmptyCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsEmptyCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsEmptyCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsEmptyCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsFloatCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsFloatCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsFloatCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsFloatCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsHotkeyCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsHotkeyCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsHotkeyCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsHotkeyCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsIntegerCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsIntegerCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsIntegerCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsIntegerCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsNumberCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsNumberCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsNumberCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsNumberCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsTrueCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsTrueCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsTrueCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsTrueCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/IsUrlCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsUrlCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/IsUrlCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/IsUrlCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/KeyExistsCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/KeyExistsCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/KeyExistsCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/KeyExistsCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/LessThanCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/LessThanCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/LessThanCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/LessThanCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/MatchesCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/MatchesCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/MatchesCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/MatchesCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/RegExCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/RegExCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/RegExCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/RegExCondition.ahk diff --git a/Lib/Shared/Volantis.Query/Condition/StartsWithCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Condition/StartsWithCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Condition/StartsWithCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Condition/StartsWithCondition.ahk diff --git a/Lib/Shared/Volantis.Query/ConditionGroup/AndGroup.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/ConditionGroup/AndGroup.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/ConditionGroup/AndGroup.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/ConditionGroup/AndGroup.ahk diff --git a/Lib/Shared/Volantis.Query/ConditionGroup/ConditionGroupBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/ConditionGroup/ConditionGroupBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/ConditionGroup/ConditionGroupBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/ConditionGroup/ConditionGroupBase.ahk diff --git a/Lib/Shared/Volantis.Query/ConditionGroup/OrGroup.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/ConditionGroup/OrGroup.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/ConditionGroup/OrGroup.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/ConditionGroup/OrGroup.ahk diff --git a/Lib/Shared/Volantis.Query/Exception/QueryException.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Exception/QueryException.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Exception/QueryException.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Exception/QueryException.ahk diff --git a/Lib/Shared/Volantis.Query/Query/MapQuery.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Query/MapQuery.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Query/MapQuery.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Query/MapQuery.ahk diff --git a/Lib/Shared/Volantis.Query/Query/QueryBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/Query/QueryBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/Query/QueryBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/Query/QueryBase.ahk diff --git a/Lib/Shared/Volantis.Query/QueryCondition/FieldCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/FieldCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/QueryCondition/FieldCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/FieldCondition.ahk diff --git a/Lib/Shared/Volantis.Query/QueryCondition/HasFieldCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/HasFieldCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/QueryCondition/HasFieldCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/HasFieldCondition.ahk diff --git a/Lib/Shared/Volantis.Query/QueryCondition/IdCondition.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/IdCondition.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/QueryCondition/IdCondition.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/IdCondition.ahk diff --git a/Lib/Shared/Volantis.Query/QueryCondition/QueryConditionBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/QueryConditionBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Query/QueryCondition/QueryConditionBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Query/QueryCondition/QueryConditionBase.ahk diff --git a/Lib/Shared/Volantis.Query/Volantis.Query.library.json b/launchpad_ahk/Lib/Shared/Volantis.Query/Volantis.Query.library.json similarity index 100% rename from Lib/Shared/Volantis.Query/Volantis.Query.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Query/Volantis.Query.library.json diff --git a/Lib/Shared/Volantis.Theme/AnimatedGif/AnimatedGif.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/AnimatedGif/AnimatedGif.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/AnimatedGif/AnimatedGif.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/AnimatedGif/AnimatedGif.ahk diff --git a/Lib/Shared/Volantis.Theme/Factory/GuiFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/Factory/GuiFactory.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/Factory/GuiFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/Factory/GuiFactory.ahk diff --git a/Lib/Shared/Volantis.Theme/Factory/ThemeFactory.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/Factory/ThemeFactory.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/Factory/ThemeFactory.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/Factory/ThemeFactory.ahk diff --git a/Lib/Shared/Volantis.Theme/GdiPlus/GdiPlusBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GdiPlus/GdiPlusBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GdiPlus/GdiPlusBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GdiPlus/GdiPlusBase.ahk diff --git a/Lib/Shared/Volantis.Theme/GdiPlus/Gdip.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GdiPlus/Gdip.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GdiPlus/Gdip.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GdiPlus/Gdip.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/Button/ButtonShape.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/ButtonShape.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/Button/ButtonShape.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/ButtonShape.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/Button/MainMenuButtonShape.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/MainMenuButtonShape.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/Button/MainMenuButtonShape.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/MainMenuButtonShape.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/Button/ManageButtonShape.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/ManageButtonShape.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/Button/ManageButtonShape.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/ManageButtonShape.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/Button/MenuSeparatorShape.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/MenuSeparatorShape.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/Button/MenuSeparatorShape.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/MenuSeparatorShape.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/Button/StatusIndicatorButtonShape.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/StatusIndicatorButtonShape.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/Button/StatusIndicatorButtonShape.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/StatusIndicatorButtonShape.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/Button/SymbolButtonShape.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/SymbolButtonShape.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/Button/SymbolButtonShape.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/Button/SymbolButtonShape.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiShape/GuiShapeBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/GuiShapeBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiShape/GuiShapeBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiShape/GuiShapeBase.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/AddSymbol.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/AddSymbol.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/AddSymbol.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/AddSymbol.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/ArrowDownSymbol.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/ArrowDownSymbol.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/ArrowDownSymbol.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/ArrowDownSymbol.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/GuiSymbolBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/GuiSymbolBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/GuiSymbolBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/GuiSymbolBase.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/CloseSymbol.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/CloseSymbol.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/CloseSymbol.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/CloseSymbol.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MaximizeSymbol.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MaximizeSymbol.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MaximizeSymbol.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MaximizeSymbol.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MinimizeSymbol.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MinimizeSymbol.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MinimizeSymbol.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/MinimizeSymbol.ahk diff --git a/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/UnmaximizeSymbol.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/UnmaximizeSymbol.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/UnmaximizeSymbol.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/GuiSymbol/Titlebar/UnmaximizeSymbol.ahk diff --git a/Lib/Shared/Volantis.Theme/ParameterBag/GuiControlParameters.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/ParameterBag/GuiControlParameters.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/ParameterBag/GuiControlParameters.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/ParameterBag/GuiControlParameters.ahk diff --git a/Lib/Shared/Volantis.Theme/Theme/BasicTheme.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/Theme/BasicTheme.ahk similarity index 100% rename from Lib/Shared/Volantis.Theme/Theme/BasicTheme.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/Theme/BasicTheme.ahk diff --git a/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk similarity index 99% rename from Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk index 61ead15e..a33e9428 100644 --- a/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk @@ -8,7 +8,7 @@ class ThemeBase { defaultTheme := "Lightpad" vars := Map() colors := Map("background", "FFFFFF", "text", "000000", "textInactive", "959595", "accent", "9466FC", "accentBright", "EEE6FF", "accentBg", "8A57F0", "transColor", "") - themeAssets := Map("logo", "Resources\Graphics\Logo.png", "icon", "Resources\Graphics\Launchpad.ico", "spinner", "Resources\Graphics\Spinner.gif") + themeAssets := Map("logo", "Resources\Graphics\logo.png", "icon", "Resources\Graphics\launchpad.ico", "spinner", "Resources\Graphics\spinner.gif") symbols := Map() buttons := Map("height", Map("s", 20, "m", 30, "l", 40, "xl", 80), "fixedWidth", Map("s", 80, "m", 100, "l", 120, "xl", 140)) labels := Map("height", "auto", "fixedWidth", 75, "font", "normal") diff --git a/Lib/Shared/Volantis.Theme/Volantis.Theme.library.json b/launchpad_ahk/Lib/Shared/Volantis.Theme/Volantis.Theme.library.json similarity index 100% rename from Lib/Shared/Volantis.Theme/Volantis.Theme.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Theme/Volantis.Theme.library.json diff --git a/Lib/Shared/Volantis.Utility/Cloner/ClonerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Cloner/ClonerBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Cloner/ClonerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Cloner/ClonerBase.ahk diff --git a/Lib/Shared/Volantis.Utility/Cloner/ListCloner.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Cloner/ListCloner.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Cloner/ListCloner.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Cloner/ListCloner.ahk diff --git a/Lib/Shared/Volantis.Utility/Cloner/SimpleCloner.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Cloner/SimpleCloner.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Cloner/SimpleCloner.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Cloner/SimpleCloner.ahk diff --git a/Lib/Shared/Volantis.Utility/Converter/ConverterBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Converter/ConverterBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Converter/ConverterBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Converter/ConverterBase.ahk diff --git a/Lib/Shared/Volantis.Utility/Converter/UnixTimestampConverter.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Converter/UnixTimestampConverter.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Converter/UnixTimestampConverter.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Converter/UnixTimestampConverter.ahk diff --git a/Lib/Shared/Volantis.Utility/Debugger/Debugger.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Debugger/Debugger.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Debugger/Debugger.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Debugger/Debugger.ahk diff --git a/Lib/Shared/Volantis.Utility/Debugger/Debugger.test.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Debugger/Debugger.test.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Debugger/Debugger.test.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Debugger/Debugger.test.ahk diff --git a/Lib/Shared/Volantis.Utility/Hasher/FileHasher.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Hasher/FileHasher.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Hasher/FileHasher.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Hasher/FileHasher.ahk diff --git a/Lib/Shared/Volantis.Utility/Hasher/FileHasher.test.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Hasher/FileHasher.test.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Hasher/FileHasher.test.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Hasher/FileHasher.test.ahk diff --git a/Lib/Shared/Volantis.Utility/Hasher/HasherBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Hasher/HasherBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Hasher/HasherBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Hasher/HasherBase.ahk diff --git a/Lib/Shared/Volantis.Utility/IdGenerator/IdGeneratorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IdGenerator/IdGeneratorBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/IdGenerator/IdGeneratorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IdGenerator/IdGeneratorBase.ahk diff --git a/Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.ahk diff --git a/Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.test.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.test.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.test.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IdGenerator/UuidGenerator.test.ahk diff --git a/Lib/Shared/Volantis.Utility/IncludeBuilder/AhkIncludeBuilder.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeBuilder/AhkIncludeBuilder.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/IncludeBuilder/AhkIncludeBuilder.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeBuilder/AhkIncludeBuilder.ahk diff --git a/Lib/Shared/Volantis.Utility/IncludeBuilder/IncludeBuilderBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeBuilder/IncludeBuilderBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/IncludeBuilder/IncludeBuilderBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeBuilder/IncludeBuilderBase.ahk diff --git a/Lib/Shared/Volantis.Utility/IncludeWriter/AhkIncludeWriter.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeWriter/AhkIncludeWriter.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/IncludeWriter/AhkIncludeWriter.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeWriter/AhkIncludeWriter.ahk diff --git a/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk similarity index 82% rename from Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk index cb9e37da..aab7342d 100644 --- a/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk @@ -25,7 +25,12 @@ class IncludeWriterBase { updated := this.FilesAreDifferent(this.tmpPath, this.outputPath) if (updated) { - FileDelete(this.outputPath) + try { + FileDelete(this.outputPath) + } catch Any { + throw AppException("Unable to delete file path " . this.outputPath) + } + } } diff --git a/Lib/Shared/Volantis.Utility/Locator/FileLocator.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Locator/FileLocator.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Locator/FileLocator.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Locator/FileLocator.ahk diff --git a/Lib/Shared/Volantis.Utility/Locator/LocatorBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Locator/LocatorBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Locator/LocatorBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Locator/LocatorBase.ahk diff --git a/Lib/Shared/Volantis.Utility/Logger/FileLogger.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Logger/FileLogger.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Logger/FileLogger.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Logger/FileLogger.ahk diff --git a/Lib/Shared/Volantis.Utility/Logger/LoggerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Logger/LoggerBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Logger/LoggerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Logger/LoggerBase.ahk diff --git a/Lib/Shared/Volantis.Utility/Merger/ListMerger.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Merger/ListMerger.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Merger/ListMerger.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Merger/ListMerger.ahk diff --git a/Lib/Shared/Volantis.Utility/Merger/MergerBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Merger/MergerBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Merger/MergerBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Merger/MergerBase.ahk diff --git a/Lib/Shared/Volantis.Utility/Notifier/NotifierBase.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Notifier/NotifierBase.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Notifier/NotifierBase.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Notifier/NotifierBase.ahk diff --git a/Lib/Shared/Volantis.Utility/Notifier/ToastNotifier.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/Notifier/ToastNotifier.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/Notifier/ToastNotifier.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Notifier/ToastNotifier.ahk diff --git a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk similarity index 99% rename from Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk index fdf689d8..fe00f734 100644 --- a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk +++ b/launchpad_ahk/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk @@ -77,7 +77,7 @@ class VersionChecker { } incrementIndex := versionArr.Length - 1 - } else if (versionArr[0] == "0") { + } else if (versionArr[1] == "0") { incrementIndex := versionArr.Length } diff --git a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.test.ahk b/launchpad_ahk/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.test.ahk similarity index 100% rename from Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.test.ahk rename to launchpad_ahk/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.test.ahk diff --git a/Lib/Shared/Volantis.Utility/Volantis.Utility.library.json b/launchpad_ahk/Lib/Shared/Volantis.Utility/Volantis.Utility.library.json similarity index 100% rename from Lib/Shared/Volantis.Utility/Volantis.Utility.library.json rename to launchpad_ahk/Lib/Shared/Volantis.Utility/Volantis.Utility.library.json diff --git a/Lib/TestLib/Assertable/AssertableBase.ahk b/launchpad_ahk/Lib/TestLib/Assertable/AssertableBase.ahk similarity index 100% rename from Lib/TestLib/Assertable/AssertableBase.ahk rename to launchpad_ahk/Lib/TestLib/Assertable/AssertableBase.ahk diff --git a/Lib/TestLib/Includes.ahk b/launchpad_ahk/Lib/TestLib/Includes.ahk similarity index 100% rename from Lib/TestLib/Includes.ahk rename to launchpad_ahk/Lib/TestLib/Includes.ahk diff --git a/Lib/TestLib/Includes.test.ahk b/launchpad_ahk/Lib/TestLib/Includes.test.ahk similarity index 100% rename from Lib/TestLib/Includes.test.ahk rename to launchpad_ahk/Lib/TestLib/Includes.test.ahk diff --git a/Lib/TestLib/ResultViewer/FileResultViewerBase.ahk b/launchpad_ahk/Lib/TestLib/ResultViewer/FileResultViewerBase.ahk similarity index 100% rename from Lib/TestLib/ResultViewer/FileResultViewerBase.ahk rename to launchpad_ahk/Lib/TestLib/ResultViewer/FileResultViewerBase.ahk diff --git a/Lib/TestLib/ResultViewer/HtmlResultViewer.ahk b/launchpad_ahk/Lib/TestLib/ResultViewer/HtmlResultViewer.ahk similarity index 100% rename from Lib/TestLib/ResultViewer/HtmlResultViewer.ahk rename to launchpad_ahk/Lib/TestLib/ResultViewer/HtmlResultViewer.ahk diff --git a/Lib/TestLib/ResultViewer/ResultViewerBase.ahk b/launchpad_ahk/Lib/TestLib/ResultViewer/ResultViewerBase.ahk similarity index 100% rename from Lib/TestLib/ResultViewer/ResultViewerBase.ahk rename to launchpad_ahk/Lib/TestLib/ResultViewer/ResultViewerBase.ahk diff --git a/Lib/TestLib/ResultViewer/TemplateFileResultViewerBase.ahk b/launchpad_ahk/Lib/TestLib/ResultViewer/TemplateFileResultViewerBase.ahk similarity index 100% rename from Lib/TestLib/ResultViewer/TemplateFileResultViewerBase.ahk rename to launchpad_ahk/Lib/TestLib/ResultViewer/TemplateFileResultViewerBase.ahk diff --git a/Lib/TestLib/Test/AppTestBase.ahk b/launchpad_ahk/Lib/TestLib/Test/AppTestBase.ahk similarity index 96% rename from Lib/TestLib/Test/AppTestBase.ahk rename to launchpad_ahk/Lib/TestLib/Test/AppTestBase.ahk index 028d322c..4414cacf 100644 --- a/Lib/TestLib/Test/AppTestBase.ahk +++ b/launchpad_ahk/Lib/TestLib/Test/AppTestBase.ahk @@ -6,12 +6,12 @@ class AppTestBase extends TestBase { GetTestAppConfig() { config := Map( "appName", "Test App", - "developer", "Test Developer", "appDir", A_ScriptDir, "tmpDir", this.testDir . "\Temp", "dataDir", this.testDir . "\Data", "version", this.testAppVersion, "parameters", Map( + "app.developer", "Test Developer", "config.flush_cache_on_exit", false, "config.logging_level", "none", "config.module_dirs", [], diff --git a/Lib/TestLib/Test/TestBase.ahk b/launchpad_ahk/Lib/TestLib/Test/TestBase.ahk similarity index 100% rename from Lib/TestLib/Test/TestBase.ahk rename to launchpad_ahk/Lib/TestLib/Test/TestBase.ahk diff --git a/Lib/TestLib/TestException/FileSystemTestException.ahk b/launchpad_ahk/Lib/TestLib/TestException/FileSystemTestException.ahk similarity index 100% rename from Lib/TestLib/TestException/FileSystemTestException.ahk rename to launchpad_ahk/Lib/TestLib/TestException/FileSystemTestException.ahk diff --git a/Lib/TestLib/TestException/NotImplementedTestException.ahk b/launchpad_ahk/Lib/TestLib/TestException/NotImplementedTestException.ahk similarity index 100% rename from Lib/TestLib/TestException/NotImplementedTestException.ahk rename to launchpad_ahk/Lib/TestLib/TestException/NotImplementedTestException.ahk diff --git a/Lib/TestLib/TestException/TestException.ahk b/launchpad_ahk/Lib/TestLib/TestException/TestException.ahk similarity index 100% rename from Lib/TestLib/TestException/TestException.ahk rename to launchpad_ahk/Lib/TestLib/TestException/TestException.ahk diff --git a/Lib/TestLib/TestGenerator/TestGeneratorBase.ahk b/launchpad_ahk/Lib/TestLib/TestGenerator/TestGeneratorBase.ahk similarity index 100% rename from Lib/TestLib/TestGenerator/TestGeneratorBase.ahk rename to launchpad_ahk/Lib/TestLib/TestGenerator/TestGeneratorBase.ahk diff --git a/Lib/TestLib/TestLoader/FilesystemTestLoader.ahk b/launchpad_ahk/Lib/TestLib/TestLoader/FilesystemTestLoader.ahk similarity index 100% rename from Lib/TestLib/TestLoader/FilesystemTestLoader.ahk rename to launchpad_ahk/Lib/TestLib/TestLoader/FilesystemTestLoader.ahk diff --git a/Lib/TestLib/TestLoader/TestLoaderBase.ahk b/launchpad_ahk/Lib/TestLib/TestLoader/TestLoaderBase.ahk similarity index 100% rename from Lib/TestLib/TestLoader/TestLoaderBase.ahk rename to launchpad_ahk/Lib/TestLib/TestLoader/TestLoaderBase.ahk diff --git a/Lib/TestLib/TestRunner/SimpleTestRunner.ahk b/launchpad_ahk/Lib/TestLib/TestRunner/SimpleTestRunner.ahk similarity index 100% rename from Lib/TestLib/TestRunner/SimpleTestRunner.ahk rename to launchpad_ahk/Lib/TestLib/TestRunner/SimpleTestRunner.ahk diff --git a/Lib/TestLib/TestRunner/TestRunnerBase.ahk b/launchpad_ahk/Lib/TestLib/TestRunner/TestRunnerBase.ahk similarity index 100% rename from Lib/TestLib/TestRunner/TestRunnerBase.ahk rename to launchpad_ahk/Lib/TestLib/TestRunner/TestRunnerBase.ahk diff --git a/Resources/Dependencies/AhkBins.zip b/launchpad_ahk/Resources/Dependencies/AhkBins.zip similarity index 100% rename from Resources/Dependencies/AhkBins.zip rename to launchpad_ahk/Resources/Dependencies/AhkBins.zip diff --git a/Resources/Dependencies/AniGIF.dll b/launchpad_ahk/Resources/Dependencies/AniGIF.dll similarity index 100% rename from Resources/Dependencies/AniGIF.dll rename to launchpad_ahk/Resources/Dependencies/AniGIF.dll diff --git a/Resources/Dependencies/BlizzardProductDb.proto b/launchpad_ahk/Resources/Dependencies/BlizzardProductDb.proto similarity index 100% rename from Resources/Dependencies/BlizzardProductDb.proto rename to launchpad_ahk/Resources/Dependencies/BlizzardProductDb.proto diff --git a/Resources/Graphics/Icons/Dark/Backup.ico b/launchpad_ahk/Resources/Graphics/Icons/Dark/backup.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Backup.ico rename to launchpad_ahk/Resources/Graphics/Icons/Dark/backup.ico diff --git a/Resources/Graphics/Icons/Dark/cloud.png b/launchpad_ahk/Resources/Graphics/Icons/Dark/cloud.png similarity index 100% rename from Resources/Graphics/Icons/Dark/cloud.png rename to launchpad_ahk/Resources/Graphics/Icons/Dark/cloud.png diff --git a/Resources/Graphics/Icons/Dark/Config.ico b/launchpad_ahk/Resources/Graphics/Icons/Dark/config.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Config.ico rename to launchpad_ahk/Resources/Graphics/Icons/Dark/config.ico diff --git a/Resources/Graphics/Icons/Dark/cube-outline.png b/launchpad_ahk/Resources/Graphics/Icons/Dark/cube-outline.png similarity index 100% rename from Resources/Graphics/Icons/Dark/cube-outline.png rename to launchpad_ahk/Resources/Graphics/Icons/Dark/cube-outline.png diff --git a/Resources/Graphics/Icons/Dark/Game.ico b/launchpad_ahk/Resources/Graphics/Icons/Dark/game.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Game.ico rename to launchpad_ahk/Resources/Graphics/Icons/Dark/game.ico diff --git a/Resources/Graphics/Icons/Dark/github.png b/launchpad_ahk/Resources/Graphics/Icons/Dark/github.png similarity index 100% rename from Resources/Graphics/Icons/Dark/github.png rename to launchpad_ahk/Resources/Graphics/Icons/Dark/github.png diff --git a/Resources/Graphics/Icons/Dark/Logo.ico b/launchpad_ahk/Resources/Graphics/Icons/Dark/logo.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Logo.ico rename to launchpad_ahk/Resources/Graphics/Icons/Dark/logo.ico diff --git a/Resources/Graphics/Icons/Dark/Module.png b/launchpad_ahk/Resources/Graphics/Icons/Dark/module.png similarity index 100% rename from Resources/Graphics/Icons/Dark/Module.png rename to launchpad_ahk/Resources/Graphics/Icons/Dark/module.png diff --git a/Resources/Graphics/Icons/Dark/Platform.ico b/launchpad_ahk/Resources/Graphics/Icons/Dark/platform.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Platform.ico rename to launchpad_ahk/Resources/Graphics/Icons/Dark/platform.ico diff --git a/Resources/Graphics/Icons/Dark/webhook.png b/launchpad_ahk/Resources/Graphics/Icons/Dark/webhook.png similarity index 100% rename from Resources/Graphics/Icons/Dark/webhook.png rename to launchpad_ahk/Resources/Graphics/Icons/Dark/webhook.png diff --git a/Resources/Graphics/Icons/Gradient/Backup.ico b/launchpad_ahk/Resources/Graphics/Icons/Gradient/backup.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Backup.ico rename to launchpad_ahk/Resources/Graphics/Icons/Gradient/backup.ico diff --git a/Resources/Graphics/Icons/Gradient/Config.ico b/launchpad_ahk/Resources/Graphics/Icons/Gradient/config.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Config.ico rename to launchpad_ahk/Resources/Graphics/Icons/Gradient/config.ico diff --git a/Resources/Graphics/Icons/Gradient/Game.ico b/launchpad_ahk/Resources/Graphics/Icons/Gradient/game.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Game.ico rename to launchpad_ahk/Resources/Graphics/Icons/Gradient/game.ico diff --git a/Resources/Graphics/Icons/Gradient/Logo.ico b/launchpad_ahk/Resources/Graphics/Icons/Gradient/logo.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Logo.ico rename to launchpad_ahk/Resources/Graphics/Icons/Gradient/logo.ico diff --git a/Resources/Graphics/Icons/Gradient/Platform.ico b/launchpad_ahk/Resources/Graphics/Icons/Gradient/platform.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Platform.ico rename to launchpad_ahk/Resources/Graphics/Icons/Gradient/platform.ico diff --git a/Resources/Graphics/Icons/Light/Backup.ico b/launchpad_ahk/Resources/Graphics/Icons/Light/backup.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Backup.ico rename to launchpad_ahk/Resources/Graphics/Icons/Light/backup.ico diff --git a/Resources/Graphics/Icons/Light/cloud.png b/launchpad_ahk/Resources/Graphics/Icons/Light/cloud.png similarity index 100% rename from Resources/Graphics/Icons/Light/cloud.png rename to launchpad_ahk/Resources/Graphics/Icons/Light/cloud.png diff --git a/Resources/Graphics/Icons/Light/Config.ico b/launchpad_ahk/Resources/Graphics/Icons/Light/config.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Config.ico rename to launchpad_ahk/Resources/Graphics/Icons/Light/config.ico diff --git a/Resources/Graphics/Icons/Light/cube-outline.png b/launchpad_ahk/Resources/Graphics/Icons/Light/cube-outline.png similarity index 100% rename from Resources/Graphics/Icons/Light/cube-outline.png rename to launchpad_ahk/Resources/Graphics/Icons/Light/cube-outline.png diff --git a/Resources/Graphics/Icons/Light/Game.ico b/launchpad_ahk/Resources/Graphics/Icons/Light/game.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Game.ico rename to launchpad_ahk/Resources/Graphics/Icons/Light/game.ico diff --git a/Resources/Graphics/Icons/Light/github.png b/launchpad_ahk/Resources/Graphics/Icons/Light/github.png similarity index 100% rename from Resources/Graphics/Icons/Light/github.png rename to launchpad_ahk/Resources/Graphics/Icons/Light/github.png diff --git a/Resources/Graphics/Icons/Light/Logo.ico b/launchpad_ahk/Resources/Graphics/Icons/Light/logo.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Logo.ico rename to launchpad_ahk/Resources/Graphics/Icons/Light/logo.ico diff --git a/Resources/Graphics/Icons/Light/Module.png b/launchpad_ahk/Resources/Graphics/Icons/Light/module.png similarity index 100% rename from Resources/Graphics/Icons/Light/Module.png rename to launchpad_ahk/Resources/Graphics/Icons/Light/module.png diff --git a/Resources/Graphics/Icons/Light/Platform.ico b/launchpad_ahk/Resources/Graphics/Icons/Light/platform.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Platform.ico rename to launchpad_ahk/Resources/Graphics/Icons/Light/platform.ico diff --git a/Resources/Graphics/Icons/Light/webhook.png b/launchpad_ahk/Resources/Graphics/Icons/Light/webhook.png similarity index 100% rename from Resources/Graphics/Icons/Light/webhook.png rename to launchpad_ahk/Resources/Graphics/Icons/Light/webhook.png diff --git a/Resources/Graphics/Launchpad-256.png b/launchpad_ahk/Resources/Graphics/launchpad-256.png similarity index 100% rename from Resources/Graphics/Launchpad-256.png rename to launchpad_ahk/Resources/Graphics/launchpad-256.png diff --git a/Resources/Graphics/Launchpad-64.png b/launchpad_ahk/Resources/Graphics/launchpad-64.png similarity index 100% rename from Resources/Graphics/Launchpad-64.png rename to launchpad_ahk/Resources/Graphics/launchpad-64.png diff --git a/Resources/Graphics/Launchpad.ico b/launchpad_ahk/Resources/Graphics/launchpad.ico similarity index 100% rename from Resources/Graphics/Launchpad.ico rename to launchpad_ahk/Resources/Graphics/launchpad.ico diff --git a/Resources/Graphics/Logo.png b/launchpad_ahk/Resources/Graphics/logo.png similarity index 100% rename from Resources/Graphics/Logo.png rename to launchpad_ahk/Resources/Graphics/logo.png diff --git a/Resources/Graphics/Spinner-Steam.gif b/launchpad_ahk/Resources/Graphics/spinner-steam.gif similarity index 100% rename from Resources/Graphics/Spinner-Steam.gif rename to launchpad_ahk/Resources/Graphics/spinner-steam.gif diff --git a/Resources/Graphics/Spinner.gif b/launchpad_ahk/Resources/Graphics/spinner.gif similarity index 100% rename from Resources/Graphics/Spinner.gif rename to launchpad_ahk/Resources/Graphics/spinner.gif diff --git a/Resources/LaunchpadOverlay/LaunchpadOverlay.exe b/launchpad_ahk/Resources/LaunchpadOverlay/LaunchpadOverlay.exe similarity index 100% rename from Resources/LaunchpadOverlay/LaunchpadOverlay.exe rename to launchpad_ahk/Resources/LaunchpadOverlay/LaunchpadOverlay.exe diff --git a/Resources/Templates/Launcher.template.ahk b/launchpad_ahk/Resources/Templates/Launcher.template.ahk similarity index 100% rename from Resources/Templates/Launcher.template.ahk rename to launchpad_ahk/Resources/Templates/Launcher.template.ahk diff --git a/Resources/Themes/Blizzpad.json b/launchpad_ahk/Resources/Themes/Blizzpad.json similarity index 100% rename from Resources/Themes/Blizzpad.json rename to launchpad_ahk/Resources/Themes/Blizzpad.json diff --git a/Resources/Themes/Darkpad.json b/launchpad_ahk/Resources/Themes/Darkpad.json similarity index 100% rename from Resources/Themes/Darkpad.json rename to launchpad_ahk/Resources/Themes/Darkpad.json diff --git a/Resources/Themes/Lightpad.json b/launchpad_ahk/Resources/Themes/Lightpad.json similarity index 99% rename from Resources/Themes/Lightpad.json rename to launchpad_ahk/Resources/Themes/Lightpad.json index 2049c352..bcabf3a0 100644 --- a/Resources/Themes/Lightpad.json +++ b/launchpad_ahk/Resources/Themes/Lightpad.json @@ -108,9 +108,9 @@ "transColor": "" }, "themeAssets": { - "icon": "Graphics\\Launchpad.ico", - "logo": "Graphics\\Logo.png", - "spinner": "Graphics\\Spinner.gif" + "icon": "Graphics\\launchpad.ico", + "logo": "Graphics\\logo.png", + "spinner": "Graphics\\spinner.gif" }, "symbols": { "arrowDown": "ArrowDownSymbol", diff --git a/Resources/Themes/Originpad.json b/launchpad_ahk/Resources/Themes/Originpad.json similarity index 100% rename from Resources/Themes/Originpad.json rename to launchpad_ahk/Resources/Themes/Originpad.json diff --git a/Resources/Themes/Overpad.json b/launchpad_ahk/Resources/Themes/Overpad.json similarity index 100% rename from Resources/Themes/Overpad.json rename to launchpad_ahk/Resources/Themes/Overpad.json diff --git a/Resources/Themes/Steampad.json b/launchpad_ahk/Resources/Themes/Steampad.json similarity index 99% rename from Resources/Themes/Steampad.json rename to launchpad_ahk/Resources/Themes/Steampad.json index 9b6eae9f..c4e36465 100644 --- a/Resources/Themes/Steampad.json +++ b/launchpad_ahk/Resources/Themes/Steampad.json @@ -107,7 +107,7 @@ }, "themeAssets": { "logo": "", - "spinner": "Graphics\\Spinner-Steam.gif" + "spinner": "Graphics\\spinner-steam.gif" }, "buttons": { "styles": { diff --git a/Resources/Themes/Xboxpad.json b/launchpad_ahk/Resources/Themes/Xboxpad.json similarity index 100% rename from Resources/Themes/Xboxpad.json rename to launchpad_ahk/Resources/Themes/Xboxpad.json diff --git a/Scripts/Build.ahk b/launchpad_ahk/Scripts/Build.ahk similarity index 76% rename from Scripts/Build.ahk rename to launchpad_ahk/Scripts/Build.ahk index 9d166304..09656b22 100644 --- a/Scripts/Build.ahk +++ b/launchpad_ahk/Scripts/Build.ahk @@ -9,8 +9,7 @@ appVersion := "{{VERSION}}" LaunchpadBuilder(Map( "appDir", appDir, "appName", "Launchpad", - "developer", "Volantis Development", "version", appVersion, - "trayIcon", appDir . "\Resources\Graphics\Launchpad.ico", + "trayIcon", appDir . "\Resources\Graphics\launchpad.ico", "console", true, )) diff --git a/Scripts/UpdateIncludes.ahk b/launchpad_ahk/Scripts/UpdateIncludes.ahk similarity index 100% rename from Scripts/UpdateIncludes.ahk rename to launchpad_ahk/Scripts/UpdateIncludes.ahk diff --git a/Vendor/7zip/32bit/7za.exe b/launchpad_ahk/Vendor/7zip/32bit/7za.exe similarity index 100% rename from Vendor/7zip/32bit/7za.exe rename to launchpad_ahk/Vendor/7zip/32bit/7za.exe diff --git a/Vendor/7zip/64bit/7za.exe b/launchpad_ahk/Vendor/7zip/64bit/7za.exe similarity index 100% rename from Vendor/7zip/64bit/7za.exe rename to launchpad_ahk/Vendor/7zip/64bit/7za.exe diff --git a/Vendor/7zip/License.txt b/launchpad_ahk/Vendor/7zip/License.txt similarity index 100% rename from Vendor/7zip/License.txt rename to launchpad_ahk/Vendor/7zip/License.txt diff --git a/Vendor/Verpatch/LICENSE.txt b/launchpad_ahk/Vendor/Verpatch/LICENSE.txt similarity index 100% rename from Vendor/Verpatch/LICENSE.txt rename to launchpad_ahk/Vendor/Verpatch/LICENSE.txt diff --git a/Vendor/Verpatch/ver-self.cmd b/launchpad_ahk/Vendor/Verpatch/ver-self.cmd similarity index 100% rename from Vendor/Verpatch/ver-self.cmd rename to launchpad_ahk/Vendor/Verpatch/ver-self.cmd diff --git a/Vendor/Verpatch/verpatch-ReadMe.txt b/launchpad_ahk/Vendor/Verpatch/verpatch-ReadMe.txt similarity index 100% rename from Vendor/Verpatch/verpatch-ReadMe.txt rename to launchpad_ahk/Vendor/Verpatch/verpatch-ReadMe.txt diff --git a/Vendor/Verpatch/verpatch.exe b/launchpad_ahk/Vendor/Verpatch/verpatch.exe similarity index 100% rename from Vendor/Verpatch/verpatch.exe rename to launchpad_ahk/Vendor/Verpatch/verpatch.exe diff --git a/launchpad_app/.env b/launchpad_app/.env new file mode 100644 index 00000000..702a7e9a --- /dev/null +++ b/launchpad_app/.env @@ -0,0 +1,4 @@ +# Leave all secret values blank here. +# Create .env.local or .env.[environment].local and put them there instead. +GITHUB_TOKEN= +FIREBASE_TOKEN= diff --git a/launchpad_app/.gitignore b/launchpad_app/.gitignore new file mode 100644 index 00000000..f25201da --- /dev/null +++ b/launchpad_app/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +.env.local +.env.*.local diff --git a/launchpad_app/.metadata b/launchpad_app/.metadata new file mode 100644 index 00000000..262ceed0 --- /dev/null +++ b/launchpad_app/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 135454af32477f815a7525073027a3ff9eff1bfd + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: android + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: ios + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: linux + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: macos + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: web + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: windows + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/launchpad_app/README.md b/launchpad_app/README.md new file mode 100644 index 00000000..e21ae20e --- /dev/null +++ b/launchpad_app/README.md @@ -0,0 +1,3 @@ +# Launchpad (Flutter) + +This is the codebase for the new Flutter version of Launchpad! diff --git a/launchpad_app/analysis_options.yaml b/launchpad_app/analysis_options.yaml new file mode 100644 index 00000000..e07e9e94 --- /dev/null +++ b/launchpad_app/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + errors: + invalid_annotation_target: ignore diff --git a/launchpad_app/android/.gitignore b/launchpad_app/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/launchpad_app/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/launchpad_app/android/app/build.gradle b/launchpad_app/android/app/build.gradle new file mode 100644 index 00000000..ae55ed98 --- /dev/null +++ b/launchpad_app/android/app/build.gradle @@ -0,0 +1,71 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.volantisdev.launchpad_app" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/launchpad_app/android/app/src/debug/AndroidManifest.xml b/launchpad_app/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..916df4fb --- /dev/null +++ b/launchpad_app/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/launchpad_app/android/app/src/main/AndroidManifest.xml b/launchpad_app/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a40dcca4 --- /dev/null +++ b/launchpad_app/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/android/app/src/main/kotlin/com/example/launchpad_app/MainActivity.kt b/launchpad_app/android/app/src/main/kotlin/com/example/launchpad_app/MainActivity.kt new file mode 100644 index 00000000..6e59656a --- /dev/null +++ b/launchpad_app/android/app/src/main/kotlin/com/example/launchpad_app/MainActivity.kt @@ -0,0 +1,6 @@ +package com.volantisdev.launchpad_app + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/launchpad_app/android/app/src/main/res/drawable-hdpi/splash.png b/launchpad_app/android/app/src/main/res/drawable-hdpi/splash.png new file mode 100644 index 00000000..934a3d3a Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable-hdpi/splash.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable-mdpi/splash.png b/launchpad_app/android/app/src/main/res/drawable-mdpi/splash.png new file mode 100644 index 00000000..16b9e0b0 Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable-mdpi/splash.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable-v21/background.png b/launchpad_app/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 00000000..e253ffe1 Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable-v21/background.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable-v21/launch_background.xml b/launchpad_app/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..3cc4948a --- /dev/null +++ b/launchpad_app/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/launchpad_app/android/app/src/main/res/drawable-xhdpi/splash.png b/launchpad_app/android/app/src/main/res/drawable-xhdpi/splash.png new file mode 100644 index 00000000..02a3a563 Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable-xhdpi/splash.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable-xxhdpi/splash.png b/launchpad_app/android/app/src/main/res/drawable-xxhdpi/splash.png new file mode 100644 index 00000000..417a818f Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable-xxhdpi/splash.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable-xxxhdpi/splash.png b/launchpad_app/android/app/src/main/res/drawable-xxxhdpi/splash.png new file mode 100644 index 00000000..9983337e Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable-xxxhdpi/splash.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable/background.png b/launchpad_app/android/app/src/main/res/drawable/background.png new file mode 100644 index 00000000..e253ffe1 Binary files /dev/null and b/launchpad_app/android/app/src/main/res/drawable/background.png differ diff --git a/launchpad_app/android/app/src/main/res/drawable/launch_background.xml b/launchpad_app/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..3cc4948a --- /dev/null +++ b/launchpad_app/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/launchpad_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/launchpad_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..8b2d3ebc Binary files /dev/null and b/launchpad_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/launchpad_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/launchpad_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..41474a3b Binary files /dev/null and b/launchpad_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/launchpad_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/launchpad_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..cd173698 Binary files /dev/null and b/launchpad_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/launchpad_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/launchpad_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..acf5fb5b Binary files /dev/null and b/launchpad_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/launchpad_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/launchpad_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..8e4277fe Binary files /dev/null and b/launchpad_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/launchpad_app/android/app/src/main/res/values-night/styles.xml b/launchpad_app/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/launchpad_app/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/launchpad_app/android/app/src/main/res/values/styles.xml b/launchpad_app/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..82c1a057 --- /dev/null +++ b/launchpad_app/android/app/src/main/res/values/styles.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/launchpad_app/android/app/src/profile/AndroidManifest.xml b/launchpad_app/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..916df4fb --- /dev/null +++ b/launchpad_app/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/launchpad_app/android/build.gradle b/launchpad_app/android/build.gradle new file mode 100644 index 00000000..83ae2200 --- /dev/null +++ b/launchpad_app/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/launchpad_app/android/gradle.properties b/launchpad_app/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/launchpad_app/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/launchpad_app/android/gradle/wrapper/gradle-wrapper.properties b/launchpad_app/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..cb24abda --- /dev/null +++ b/launchpad_app/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/launchpad_app/android/settings.gradle b/launchpad_app/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/launchpad_app/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/launchpad_app/assets/graphics/icon.png b/launchpad_app/assets/graphics/icon.png new file mode 100644 index 00000000..99f674ff Binary files /dev/null and b/launchpad_app/assets/graphics/icon.png differ diff --git a/launchpad_app/assets/graphics/icon.svg b/launchpad_app/assets/graphics/icon.svg new file mode 100644 index 00000000..76d36c8a --- /dev/null +++ b/launchpad_app/assets/graphics/icon.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + diff --git a/launchpad_app/assets/graphics/light/logo_square.svg b/launchpad_app/assets/graphics/light/logo_square.svg new file mode 100644 index 00000000..7a77eb9c --- /dev/null +++ b/launchpad_app/assets/graphics/light/logo_square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launchpad_app/assets/graphics/light/logo_wide.svg b/launchpad_app/assets/graphics/light/logo_wide.svg new file mode 100644 index 00000000..0f8d4a55 --- /dev/null +++ b/launchpad_app/assets/graphics/light/logo_wide.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/assets/graphics/light/platform_icon_bethesda.png b/launchpad_app/assets/graphics/light/platform_icon_bethesda.png new file mode 100644 index 00000000..5fc77e3f Binary files /dev/null and b/launchpad_app/assets/graphics/light/platform_icon_bethesda.png differ diff --git a/launchpad_app/assets/graphics/light/splash.png b/launchpad_app/assets/graphics/light/splash.png new file mode 100644 index 00000000..ac413759 Binary files /dev/null and b/launchpad_app/assets/graphics/light/splash.png differ diff --git a/launchpad_app/assets/graphics/light/splash.svg b/launchpad_app/assets/graphics/light/splash.svg new file mode 100644 index 00000000..fccb3ed2 --- /dev/null +++ b/launchpad_app/assets/graphics/light/splash.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/assets/graphics/logo_square.svg b/launchpad_app/assets/graphics/logo_square.svg new file mode 100644 index 00000000..34697f48 --- /dev/null +++ b/launchpad_app/assets/graphics/logo_square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launchpad_app/assets/graphics/logo_wide.svg b/launchpad_app/assets/graphics/logo_wide.svg new file mode 100644 index 00000000..df103d41 --- /dev/null +++ b/launchpad_app/assets/graphics/logo_wide.svg @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/assets/graphics/platform_icon_bethesda.png b/launchpad_app/assets/graphics/platform_icon_bethesda.png new file mode 100644 index 00000000..cdc09d54 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_bethesda.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_blizzard.png b/launchpad_app/assets/graphics/platform_icon_blizzard.png new file mode 100644 index 00000000..355f52b4 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_blizzard.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_ea.png b/launchpad_app/assets/graphics/platform_icon_ea.png new file mode 100644 index 00000000..33356644 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_ea.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_epic.png b/launchpad_app/assets/graphics/platform_icon_epic.png new file mode 100644 index 00000000..e1669158 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_epic.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_epic.svg b/launchpad_app/assets/graphics/platform_icon_epic.svg new file mode 100644 index 00000000..065ec953 --- /dev/null +++ b/launchpad_app/assets/graphics/platform_icon_epic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launchpad_app/assets/graphics/platform_icon_origin.png b/launchpad_app/assets/graphics/platform_icon_origin.png new file mode 100644 index 00000000..b8c36d29 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_origin.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_riot.png b/launchpad_app/assets/graphics/platform_icon_riot.png new file mode 100644 index 00000000..d36f05c4 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_riot.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_riot.svg b/launchpad_app/assets/graphics/platform_icon_riot.svg new file mode 100644 index 00000000..45b0bcea --- /dev/null +++ b/launchpad_app/assets/graphics/platform_icon_riot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launchpad_app/assets/graphics/platform_icon_steam.png b/launchpad_app/assets/graphics/platform_icon_steam.png new file mode 100644 index 00000000..766c084b Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_steam.png differ diff --git a/launchpad_app/assets/graphics/platform_icon_steam.svg b/launchpad_app/assets/graphics/platform_icon_steam.svg new file mode 100644 index 00000000..ef72681c --- /dev/null +++ b/launchpad_app/assets/graphics/platform_icon_steam.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/assets/graphics/platform_icon_uplay.png b/launchpad_app/assets/graphics/platform_icon_uplay.png new file mode 100644 index 00000000..d637d4a6 Binary files /dev/null and b/launchpad_app/assets/graphics/platform_icon_uplay.png differ diff --git a/launchpad_app/assets/graphics/splash.png b/launchpad_app/assets/graphics/splash.png new file mode 100644 index 00000000..cc0906c8 Binary files /dev/null and b/launchpad_app/assets/graphics/splash.png differ diff --git a/launchpad_app/assets/graphics/splash.svg b/launchpad_app/assets/graphics/splash.svg new file mode 100644 index 00000000..ad023fdc --- /dev/null +++ b/launchpad_app/assets/graphics/splash.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/build.yaml b/launchpad_app/build.yaml new file mode 100644 index 00000000..362e2aa8 --- /dev/null +++ b/launchpad_app/build.yaml @@ -0,0 +1,11 @@ +targets: + $default: + builders: + swagger_dart_code_generator: + enabled: true + options: + input_folder: lib/swaggers/ + output_folder: lib/gen + # @todo Uncomment this after the URL is available + # input_urls: + # - https://api.launchpad.games/v1/openapi.yaml diff --git a/launchpad_app/distributor_options.yaml b/launchpad_app/distributor_options.yaml new file mode 100644 index 00000000..e2b2164f --- /dev/null +++ b/launchpad_app/distributor_options.yaml @@ -0,0 +1,40 @@ +output: dist/ +releases: + - name: dev + jobs: + - name: release-dev-windows + package: + platform: windows + target: exe + build_arts: + flavor: dev + dart-define: + APP_ENV: dev + # - name: prod + # jobs: + # - name: release-prod-windows + # package: + # platform: windows + # target: exe + # build_arts: + # flavor: prod + # dart-define: + # APP_ENV: prod + # publish: + # targets: github + # args: + # repo-owner: VolantisDev + # repo-name: Launchpad + # - name: release-prod-web + # package: + # platform: web + # build_arts: + # flavor: prod + # dart-define: + # APP_ENV: prod + # publish: + # targets: firebase + # args: + +# @todo Add release version and publish_to when I'm ready to publish +# to GitHub and/or Firebase diff --git a/launchpad_app/ios/.gitignore b/launchpad_app/ios/.gitignore new file mode 100644 index 00000000..7a7f9873 --- /dev/null +++ b/launchpad_app/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/launchpad_app/ios/Flutter/AppFrameworkInfo.plist b/launchpad_app/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..9625e105 --- /dev/null +++ b/launchpad_app/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/launchpad_app/ios/Flutter/Debug.xcconfig b/launchpad_app/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/launchpad_app/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/launchpad_app/ios/Flutter/Release.xcconfig b/launchpad_app/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/launchpad_app/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/launchpad_app/ios/Runner.xcodeproj/project.pbxproj b/launchpad_app/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..891a84eb --- /dev/null +++ b/launchpad_app/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,481 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.volantisdev.launchpadApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.volantisdev.launchpadApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.volantisdev.launchpadApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/launchpad_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/launchpad_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/launchpad_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..c87d15a3 --- /dev/null +++ b/launchpad_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/ios/Runner.xcworkspace/contents.xcworkspacedata b/launchpad_app/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/launchpad_app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/launchpad_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/launchpad_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/launchpad_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/launchpad_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/launchpad_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/launchpad_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/launchpad_app/ios/Runner/AppDelegate.swift b/launchpad_app/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/launchpad_app/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..887c4539 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..26da06fe Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..8895f096 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..a95c9dd4 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..8e816c8e Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..0f5765fd Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..d196e5dd Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..8895f096 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..c347b09e Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..1d26da0a Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 00000000..05182215 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 00000000..ce95447c Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 00000000..7459d7a4 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 00000000..5ffd0092 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..1d26da0a Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..834a6616 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 00000000..8b2d3ebc Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 00000000..acf5fb5b Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..e5a78ef6 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..c250a75d Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..fc3685e5 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/launchpad_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json new file mode 100644 index 00000000..9f447e1b --- /dev/null +++ b/launchpad_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/launchpad_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png new file mode 100644 index 00000000..e253ffe1 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..00cabce8 --- /dev/null +++ b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "LaunchImage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "LaunchImage@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "LaunchImage@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..16b9e0b0 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..02a3a563 Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..417a818f Binary files /dev/null and b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/launchpad_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/launchpad_app/ios/Runner/Base.lproj/LaunchScreen.storyboard b/launchpad_app/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..17250252 --- /dev/null +++ b/launchpad_app/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/ios/Runner/Base.lproj/Main.storyboard b/launchpad_app/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/launchpad_app/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/ios/Runner/Info.plist b/launchpad_app/ios/Runner/Info.plist new file mode 100644 index 00000000..8c916be5 --- /dev/null +++ b/launchpad_app/ios/Runner/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Launchpad App + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + launchpad_app + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + + CFBundleURLSchemes + + launchpad + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + UIStatusBarHidden + + + diff --git a/launchpad_app/ios/Runner/Runner-Bridging-Header.h b/launchpad_app/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/launchpad_app/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/launchpad_app/lib/gen/assets.gen.dart b/launchpad_app/lib/gen/assets.gen.dart new file mode 100644 index 00000000..57c1b3f1 --- /dev/null +++ b/launchpad_app/lib/gen/assets.gen.dart @@ -0,0 +1,216 @@ +/// GENERATED CODE - DO NOT MODIFY BY HAND +/// ***************************************************** +/// FlutterGen +/// ***************************************************** + +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal + +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter/services.dart'; + +class $AssetsGraphicsGen { + const $AssetsGraphicsGen(); + + /// File path: assets/graphics/icon.png + AssetGenImage get icon => const AssetGenImage('assets/graphics/icon.png'); + + $AssetsGraphicsLightGen get light => const $AssetsGraphicsLightGen(); + + /// File path: assets/graphics/logo_wide.svg + SvgGenImage get logoWide => + const SvgGenImage('assets/graphics/logo_wide.svg'); + + /// File path: assets/graphics/platform_icon_bethesda.png + AssetGenImage get platformIconBethesda => + const AssetGenImage('assets/graphics/platform_icon_bethesda.png'); + + /// File path: assets/graphics/platform_icon_blizzard.png + AssetGenImage get platformIconBlizzard => + const AssetGenImage('assets/graphics/platform_icon_blizzard.png'); + + /// File path: assets/graphics/platform_icon_ea.png + AssetGenImage get platformIconEa => + const AssetGenImage('assets/graphics/platform_icon_ea.png'); + + /// File path: assets/graphics/platform_icon_epic.png + AssetGenImage get platformIconEpic => + const AssetGenImage('assets/graphics/platform_icon_epic.png'); + + /// File path: assets/graphics/platform_icon_origin.png + AssetGenImage get platformIconOrigin => + const AssetGenImage('assets/graphics/platform_icon_origin.png'); + + /// File path: assets/graphics/platform_icon_riot.png + AssetGenImage get platformIconRiot => + const AssetGenImage('assets/graphics/platform_icon_riot.png'); + + /// File path: assets/graphics/platform_icon_steam.png + AssetGenImage get platformIconSteam => + const AssetGenImage('assets/graphics/platform_icon_steam.png'); + + /// File path: assets/graphics/platform_icon_uplay.png + AssetGenImage get platformIconUplay => + const AssetGenImage('assets/graphics/platform_icon_uplay.png'); + + /// File path: assets/graphics/splash.png + AssetGenImage get splash => const AssetGenImage('assets/graphics/splash.png'); + + /// List of all assets + List get values => [ + icon, + logoWide, + platformIconBethesda, + platformIconBlizzard, + platformIconEa, + platformIconEpic, + platformIconOrigin, + platformIconRiot, + platformIconSteam, + platformIconUplay, + splash + ]; +} + +class $AssetsGraphicsLightGen { + const $AssetsGraphicsLightGen(); + + /// File path: assets/graphics/light/logo_wide.svg + SvgGenImage get logoWide => + const SvgGenImage('assets/graphics/light/logo_wide.svg'); + + /// File path: assets/graphics/light/platform_icon_bethesda.png + AssetGenImage get platformIconBethesda => + const AssetGenImage('assets/graphics/light/platform_icon_bethesda.png'); + + /// File path: assets/graphics/light/splash.png + AssetGenImage get splash => + const AssetGenImage('assets/graphics/light/splash.png'); + + /// List of all assets + List get values => [logoWide, platformIconBethesda, splash]; +} + +class Assets { + Assets._(); + + static const $AssetsGraphicsGen graphics = $AssetsGraphicsGen(); +} + +class AssetGenImage { + const AssetGenImage(this._assetName); + + final String _assetName; + + Image image({ + Key? key, + AssetBundle? bundle, + ImageFrameBuilder? frameBuilder, + ImageErrorWidgetBuilder? errorBuilder, + String? semanticLabel, + bool excludeFromSemantics = false, + double? scale, + double? width, + double? height, + Color? color, + Animation? opacity, + BlendMode? colorBlendMode, + BoxFit? fit, + AlignmentGeometry alignment = Alignment.center, + ImageRepeat repeat = ImageRepeat.noRepeat, + Rect? centerSlice, + bool matchTextDirection = false, + bool gaplessPlayback = false, + bool isAntiAlias = false, + String? package, + FilterQuality filterQuality = FilterQuality.low, + int? cacheWidth, + int? cacheHeight, + }) { + return Image.asset( + _assetName, + key: key, + bundle: bundle, + frameBuilder: frameBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + scale: scale, + width: width, + height: height, + color: color, + opacity: opacity, + colorBlendMode: colorBlendMode, + fit: fit, + alignment: alignment, + repeat: repeat, + centerSlice: centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + package: package, + filterQuality: filterQuality, + cacheWidth: cacheWidth, + cacheHeight: cacheHeight, + ); + } + + ImageProvider provider() => AssetImage(_assetName); + + String get path => _assetName; + + String get keyName => _assetName; +} + +class SvgGenImage { + const SvgGenImage(this._assetName); + + final String _assetName; + + SvgPicture svg({ + Key? key, + bool matchTextDirection = false, + AssetBundle? bundle, + String? package, + double? width, + double? height, + BoxFit fit = BoxFit.contain, + AlignmentGeometry alignment = Alignment.center, + bool allowDrawingOutsideViewBox = false, + WidgetBuilder? placeholderBuilder, + Color? color, + BlendMode colorBlendMode = BlendMode.srcIn, + String? semanticsLabel, + bool excludeFromSemantics = false, + Clip clipBehavior = Clip.hardEdge, + bool cacheColorFilter = false, + SvgTheme? theme, + }) { + return SvgPicture.asset( + _assetName, + key: key, + matchTextDirection: matchTextDirection, + bundle: bundle, + package: package, + width: width, + height: height, + fit: fit, + alignment: alignment, + allowDrawingOutsideViewBox: allowDrawingOutsideViewBox, + placeholderBuilder: placeholderBuilder, + color: color, + colorBlendMode: colorBlendMode, + semanticsLabel: semanticsLabel, + excludeFromSemantics: excludeFromSemantics, + clipBehavior: clipBehavior, + cacheColorFilter: cacheColorFilter, + theme: theme, + ); + } + + String get path => _assetName; + + String get keyName => _assetName; +} diff --git a/launchpad_app/lib/gen/client_index.dart b/launchpad_app/lib/gen/client_index.dart new file mode 100644 index 00000000..6b282f5d --- /dev/null +++ b/launchpad_app/lib/gen/client_index.dart @@ -0,0 +1 @@ +export 'launchpad_api.swagger.dart' show LaunchpadApi; diff --git a/launchpad_app/lib/gen/client_mapping.dart b/launchpad_app/lib/gen/client_mapping.dart new file mode 100644 index 00000000..1b4bf0d8 --- /dev/null +++ b/launchpad_app/lib/gen/client_mapping.dart @@ -0,0 +1 @@ +final Map)> generatedMapping = {}; diff --git a/launchpad_app/lib/gen/launchpad_api.swagger.chopper.dart b/launchpad_app/lib/gen/launchpad_api.swagger.chopper.dart new file mode 100644 index 00000000..ff250042 --- /dev/null +++ b/launchpad_app/lib/gen/launchpad_api.swagger.chopper.dart @@ -0,0 +1,221 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'launchpad_api.swagger.dart'; + +// ************************************************************************** +// ChopperGenerator +// ************************************************************************** + +// ignore_for_file: always_put_control_body_on_new_line, always_specify_types, prefer_const_declarations, unnecessary_brace_in_string_interps +class _$LaunchpadApi extends LaunchpadApi { + _$LaunchpadApi([ChopperClient? client]) { + if (client == null) return; + this.client = client; + } + + @override + final definitionType = LaunchpadApi; + + @override + Future> _helloGet() { + final Uri $url = Uri.parse('/hello'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future> _statusGet() { + final Uri $url = Uri.parse('/status'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future> _releaseInfoGet() { + final Uri $url = Uri.parse('/release-info'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future>> _platformsGet() { + final Uri $url = Uri.parse('/platforms'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send, Object>($request); + } + + @override + Future> _platformsPlatformIdGet( + {required String? platformId}) { + final Uri $url = Uri.parse('/platforms/${platformId}'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future>> _launcherTypesGet() { + final Uri $url = Uri.parse('/launcher-types'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send, Object>($request); + } + + @override + Future> _launcherTypesLauncherTypeIdGet( + {required String? launcherTypeId}) { + final Uri $url = Uri.parse('/launcher-types/${launcherTypeId}'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future>> _gameTypesGet() { + final Uri $url = Uri.parse('/game-types'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send, Object>($request); + } + + @override + Future> _gameTypesGameTypeIdGet( + {required String? gameTypeId}) { + final Uri $url = Uri.parse('/game-types/${gameTypeId}'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future>> _gamesGet() { + final Uri $url = Uri.parse('/games'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send, Object>($request); + } + + @override + Future>> _gameKeysGet() { + final Uri $url = Uri.parse('/game-keys'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send, String>($request); + } + + @override + Future> _gamesGameIdGet({required String? gameId}) { + final Uri $url = Uri.parse('/games/${gameId}'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future> _lookupGameKeyPlatformIdGet({ + required String? gameKey, + required String? platformId, + }) { + final Uri $url = Uri.parse('/lookup/${gameKey}/${platformId}'); + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + ); + return client.send($request); + } + + @override + Future> _submitErrorPost({ + required String? message, + required String? what, + required String? file, + required int? line, + String? extra, + String? stack, + String? email, + String? details, + String? version, + }) { + final Uri $url = Uri.parse('/submit-error'); + final Map $params = { + 'message': message, + 'what': what, + 'file': file, + 'line': line, + 'extra': extra, + 'stack': stack, + 'email': email, + 'details': details, + 'version': version, + }; + final Request $request = Request( + 'POST', + $url, + client.baseUrl, + parameters: $params, + ); + return client.send($request); + } + + @override + Future> _submitFeedbackPost({ + required String? feedback, + String? email, + String? version, + }) { + final Uri $url = Uri.parse('/submit-feedback'); + final Map $params = { + 'feedback': feedback, + 'email': email, + 'version': version, + }; + final Request $request = Request( + 'POST', + $url, + client.baseUrl, + parameters: $params, + ); + return client.send($request); + } +} diff --git a/launchpad_app/lib/gen/launchpad_api.swagger.dart b/launchpad_app/lib/gen/launchpad_api.swagger.dart new file mode 100644 index 00000000..f6b55f2b --- /dev/null +++ b/launchpad_app/lib/gen/launchpad_api.swagger.dart @@ -0,0 +1,1234 @@ +// ignore_for_file: type=lint + +import 'package:json_annotation/json_annotation.dart'; +import 'package:collection/collection.dart'; +import 'dart:convert'; + +import 'package:chopper/chopper.dart'; + +import 'client_mapping.dart'; +import 'dart:async'; +import 'package:chopper/chopper.dart' as chopper; + +part 'launchpad_api.swagger.chopper.dart'; +part 'launchpad_api.swagger.g.dart'; + +// ************************************************************************** +// SwaggerChopperGenerator +// ************************************************************************** + +@ChopperApi() +abstract class LaunchpadApi extends ChopperService { + static LaunchpadApi create({ + ChopperClient? client, + Authenticator? authenticator, + Uri? baseUrl, + Iterable? interceptors, + }) { + if (client != null) { + return _$LaunchpadApi(client); + } + + final newClient = ChopperClient( + services: [_$LaunchpadApi()], + converter: $JsonSerializableConverter(), + interceptors: interceptors ?? [], + authenticator: authenticator, + baseUrl: baseUrl ?? Uri.parse('http://')); + return _$LaunchpadApi(newClient); + } + + /// + Future> helloGet() { + return _helloGet(); + } + + /// + @Get(path: '/hello') + Future> _helloGet(); + + /// + Future> statusGet() { + generatedMapping.putIfAbsent(Status, () => Status.fromJsonFactory); + + return _statusGet(); + } + + /// + @Get(path: '/status') + Future> _statusGet(); + + /// + Future> releaseInfoGet() { + generatedMapping.putIfAbsent( + ReleaseInfo, () => ReleaseInfo.fromJsonFactory); + + return _releaseInfoGet(); + } + + /// + @Get(path: '/release-info') + Future> _releaseInfoGet(); + + /// + Future> platformsGet() { + return _platformsGet(); + } + + /// + @Get(path: '/platforms') + Future> _platformsGet(); + + /// + ///@param platformId The id of the platform + Future> platformsPlatformIdGet( + {required String? platformId}) { + generatedMapping.putIfAbsent( + PlatformDocument, () => PlatformDocument.fromJsonFactory); + + return _platformsPlatformIdGet(platformId: platformId); + } + + /// + ///@param platformId The id of the platform + @Get(path: '/platforms/{platformId}') + Future> _platformsPlatformIdGet( + {@Path('platformId') required String? platformId}); + + /// + Future> launcherTypesGet() { + return _launcherTypesGet(); + } + + /// + @Get(path: '/launcher-types') + Future> _launcherTypesGet(); + + /// + ///@param launcherTypeId The id of the launcher type + Future> launcherTypesLauncherTypeIdGet( + {required String? launcherTypeId}) { + generatedMapping.putIfAbsent( + LauncherTypeDocument, () => LauncherTypeDocument.fromJsonFactory); + + return _launcherTypesLauncherTypeIdGet(launcherTypeId: launcherTypeId); + } + + /// + ///@param launcherTypeId The id of the launcher type + @Get(path: '/launcher-types/{launcherTypeId}') + Future> + _launcherTypesLauncherTypeIdGet( + {@Path('launcherTypeId') required String? launcherTypeId}); + + /// + Future> gameTypesGet() { + return _gameTypesGet(); + } + + /// + @Get(path: '/game-types') + Future> _gameTypesGet(); + + /// + ///@param gameTypeId The id of the game type + Future> gameTypesGameTypeIdGet( + {required String? gameTypeId}) { + generatedMapping.putIfAbsent( + GameTypeDocument, () => GameTypeDocument.fromJsonFactory); + + return _gameTypesGameTypeIdGet(gameTypeId: gameTypeId); + } + + /// + ///@param gameTypeId The id of the game type + @Get(path: '/game-types/{gameTypeId}') + Future> _gameTypesGameTypeIdGet( + {@Path('gameTypeId') required String? gameTypeId}); + + /// + Future>> gamesGet() { + return _gamesGet(); + } + + /// + @Get(path: '/games') + Future>> _gamesGet(); + + /// + Future>> gameKeysGet() { + return _gameKeysGet(); + } + + /// + @Get(path: '/game-keys') + Future>> _gameKeysGet(); + + /// + ///@param gameId The id of the game + Future> gamesGameIdGet( + {required String? gameId}) { + generatedMapping.putIfAbsent( + GameDocument, () => GameDocument.fromJsonFactory); + + return _gamesGameIdGet(gameId: gameId); + } + + /// + ///@param gameId The id of the game + @Get(path: '/games/{gameId}') + Future> _gamesGameIdGet( + {@Path('gameId') required String? gameId}); + + /// + ///@param gameKey The key of the game + ///@param platformId The id of the platform + Future> lookupGameKeyPlatformIdGet({ + required String? gameKey, + required String? platformId, + }) { + return _lookupGameKeyPlatformIdGet( + gameKey: gameKey, platformId: platformId); + } + + /// + ///@param gameKey The key of the game + ///@param platformId The id of the platform + @Get(path: '/lookup/{gameKey}/{platformId}') + Future> _lookupGameKeyPlatformIdGet({ + @Path('gameKey') required String? gameKey, + @Path('platformId') required String? platformId, + }); + + /// + ///@param message The error message + ///@param what What caused the error + ///@param file What file the error originated in + ///@param line The line within the file + ///@param extra Extra information about the error + ///@param stack The stack trace if available + ///@param email The email address of the submitter if provided + ///@param details Additional details by the submitter if provided + ///@param version The version of Launchpad that generated the error + Future submitErrorPost({ + required String? message, + required String? what, + required String? file, + required int? line, + String? extra, + String? stack, + String? email, + String? details, + String? version, + }) { + return _submitErrorPost( + message: message, + what: what, + file: file, + line: line, + extra: extra, + stack: stack, + email: email, + details: details, + version: version); + } + + /// + ///@param message The error message + ///@param what What caused the error + ///@param file What file the error originated in + ///@param line The line within the file + ///@param extra Extra information about the error + ///@param stack The stack trace if available + ///@param email The email address of the submitter if provided + ///@param details Additional details by the submitter if provided + ///@param version The version of Launchpad that generated the error + @Post( + path: '/submit-error', + optionalBody: true, + ) + Future _submitErrorPost({ + @Query('message') required String? message, + @Query('what') required String? what, + @Query('file') required String? file, + @Query('line') required int? line, + @Query('extra') String? extra, + @Query('stack') String? stack, + @Query('email') String? email, + @Query('details') String? details, + @Query('version') String? version, + }); + + /// + ///@param feedback The feedback that was submitted + ///@param email The email address of the submitter if provided + ///@param version The version of Launchpad + Future submitFeedbackPost({ + required String? feedback, + String? email, + String? version, + }) { + return _submitFeedbackPost( + feedback: feedback, email: email, version: version); + } + + /// + ///@param feedback The feedback that was submitted + ///@param email The email address of the submitter if provided + ///@param version The version of Launchpad + @Post( + path: '/submit-feedback', + optionalBody: true, + ) + Future _submitFeedbackPost({ + @Query('feedback') required String? feedback, + @Query('email') String? email, + @Query('version') String? version, + }); +} + +@JsonSerializable(explicitToJson: true) +class Status { + Status({ + required this.authenticated, + required this.email, + }); + + factory Status.fromJson(Map json) => _$StatusFromJson(json); + + @JsonKey(name: 'authenticated') + final bool authenticated; + @JsonKey(name: 'email') + final String email; + static const fromJsonFactory = _$StatusFromJson; + static const toJsonFactory = _$StatusToJson; + Map toJson() => _$StatusToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is Status && + (identical(other.authenticated, authenticated) || + const DeepCollectionEquality() + .equals(other.authenticated, authenticated)) && + (identical(other.email, email) || + const DeepCollectionEquality().equals(other.email, email))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(authenticated) ^ + const DeepCollectionEquality().hash(email) ^ + runtimeType.hashCode; +} + +extension $StatusExtension on Status { + Status copyWith({bool? authenticated, String? email}) { + return Status( + authenticated: authenticated ?? this.authenticated, + email: email ?? this.email); + } + + Status copyWithWrapped( + {Wrapped? authenticated, Wrapped? email}) { + return Status( + authenticated: + (authenticated != null ? authenticated.value : this.authenticated), + email: (email != null ? email.value : this.email)); + } +} + +@JsonSerializable(explicitToJson: true) +class ReleaseInfo { + ReleaseInfo({ + this.tag, + required this.version, + required this.installer, + required this.releasePage, + this.timestamp, + }); + + factory ReleaseInfo.fromJson(Map json) => + _$ReleaseInfoFromJson(json); + + @JsonKey(name: 'tag') + final String? tag; + @JsonKey(name: 'version') + final String version; + @JsonKey(name: 'installer') + final String installer; + @JsonKey(name: 'releasePage') + final String releasePage; + @JsonKey(name: 'timestamp') + final int? timestamp; + static const fromJsonFactory = _$ReleaseInfoFromJson; + static const toJsonFactory = _$ReleaseInfoToJson; + Map toJson() => _$ReleaseInfoToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is ReleaseInfo && + (identical(other.tag, tag) || + const DeepCollectionEquality().equals(other.tag, tag)) && + (identical(other.version, version) || + const DeepCollectionEquality() + .equals(other.version, version)) && + (identical(other.installer, installer) || + const DeepCollectionEquality() + .equals(other.installer, installer)) && + (identical(other.releasePage, releasePage) || + const DeepCollectionEquality() + .equals(other.releasePage, releasePage)) && + (identical(other.timestamp, timestamp) || + const DeepCollectionEquality() + .equals(other.timestamp, timestamp))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(tag) ^ + const DeepCollectionEquality().hash(version) ^ + const DeepCollectionEquality().hash(installer) ^ + const DeepCollectionEquality().hash(releasePage) ^ + const DeepCollectionEquality().hash(timestamp) ^ + runtimeType.hashCode; +} + +extension $ReleaseInfoExtension on ReleaseInfo { + ReleaseInfo copyWith( + {String? tag, + String? version, + String? installer, + String? releasePage, + int? timestamp}) { + return ReleaseInfo( + tag: tag ?? this.tag, + version: version ?? this.version, + installer: installer ?? this.installer, + releasePage: releasePage ?? this.releasePage, + timestamp: timestamp ?? this.timestamp); + } + + ReleaseInfo copyWithWrapped( + {Wrapped? tag, + Wrapped? version, + Wrapped? installer, + Wrapped? releasePage, + Wrapped? timestamp}) { + return ReleaseInfo( + tag: (tag != null ? tag.value : this.tag), + version: (version != null ? version.value : this.version), + installer: (installer != null ? installer.value : this.installer), + releasePage: + (releasePage != null ? releasePage.value : this.releasePage), + timestamp: (timestamp != null ? timestamp.value : this.timestamp)); + } +} + +typedef Listing = List; + +@JsonSerializable(explicitToJson: true) +class PlatformDocument { + PlatformDocument({ + required this.id, + required this.data, + }); + + factory PlatformDocument.fromJson(Map json) => + _$PlatformDocumentFromJson(json); + + @JsonKey(name: 'id') + final String id; + @JsonKey(name: 'data') + final Platform data; + static const fromJsonFactory = _$PlatformDocumentFromJson; + static const toJsonFactory = _$PlatformDocumentToJson; + Map toJson() => _$PlatformDocumentToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is PlatformDocument && + (identical(other.id, id) || + const DeepCollectionEquality().equals(other.id, id)) && + (identical(other.data, data) || + const DeepCollectionEquality().equals(other.data, data))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(id) ^ + const DeepCollectionEquality().hash(data) ^ + runtimeType.hashCode; +} + +extension $PlatformDocumentExtension on PlatformDocument { + PlatformDocument copyWith({String? id, Platform? data}) { + return PlatformDocument(id: id ?? this.id, data: data ?? this.data); + } + + PlatformDocument copyWithWrapped( + {Wrapped? id, Wrapped? data}) { + return PlatformDocument( + id: (id != null ? id.value : this.id), + data: (data != null ? data.value : this.data)); + } +} + +@JsonSerializable(explicitToJson: true) +class Platform { + Platform({ + required this.key, + required this.name, + required this.launcherType, + required this.gameType, + required this.$class, + required this.enabled, + this.registry, + this.links, + }); + + factory Platform.fromJson(Map json) => + _$PlatformFromJson(json); + + @JsonKey(name: 'key') + final String key; + @JsonKey(name: 'name') + final String name; + @JsonKey(name: 'launcherType') + final String launcherType; + @JsonKey(name: 'gameType') + final String gameType; + @JsonKey(name: 'class') + final String $class; + @JsonKey(name: 'enabled') + final bool enabled; + @JsonKey(name: 'registry') + final Platform$Registry? registry; + @JsonKey(name: 'links') + final Platform$Links? links; + static const fromJsonFactory = _$PlatformFromJson; + static const toJsonFactory = _$PlatformToJson; + Map toJson() => _$PlatformToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is Platform && + (identical(other.key, key) || + const DeepCollectionEquality().equals(other.key, key)) && + (identical(other.name, name) || + const DeepCollectionEquality().equals(other.name, name)) && + (identical(other.launcherType, launcherType) || + const DeepCollectionEquality() + .equals(other.launcherType, launcherType)) && + (identical(other.gameType, gameType) || + const DeepCollectionEquality() + .equals(other.gameType, gameType)) && + (identical(other.$class, $class) || + const DeepCollectionEquality().equals(other.$class, $class)) && + (identical(other.enabled, enabled) || + const DeepCollectionEquality() + .equals(other.enabled, enabled)) && + (identical(other.registry, registry) || + const DeepCollectionEquality() + .equals(other.registry, registry)) && + (identical(other.links, links) || + const DeepCollectionEquality().equals(other.links, links))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(key) ^ + const DeepCollectionEquality().hash(name) ^ + const DeepCollectionEquality().hash(launcherType) ^ + const DeepCollectionEquality().hash(gameType) ^ + const DeepCollectionEquality().hash($class) ^ + const DeepCollectionEquality().hash(enabled) ^ + const DeepCollectionEquality().hash(registry) ^ + const DeepCollectionEquality().hash(links) ^ + runtimeType.hashCode; +} + +extension $PlatformExtension on Platform { + Platform copyWith( + {String? key, + String? name, + String? launcherType, + String? gameType, + String? $class, + bool? enabled, + Platform$Registry? registry, + Platform$Links? links}) { + return Platform( + key: key ?? this.key, + name: name ?? this.name, + launcherType: launcherType ?? this.launcherType, + gameType: gameType ?? this.gameType, + $class: $class ?? this.$class, + enabled: enabled ?? this.enabled, + registry: registry ?? this.registry, + links: links ?? this.links); + } + + Platform copyWithWrapped( + {Wrapped? key, + Wrapped? name, + Wrapped? launcherType, + Wrapped? gameType, + Wrapped? $class, + Wrapped? enabled, + Wrapped? registry, + Wrapped? links}) { + return Platform( + key: (key != null ? key.value : this.key), + name: (name != null ? name.value : this.name), + launcherType: + (launcherType != null ? launcherType.value : this.launcherType), + gameType: (gameType != null ? gameType.value : this.gameType), + $class: ($class != null ? $class.value : this.$class), + enabled: (enabled != null ? enabled.value : this.enabled), + registry: (registry != null ? registry.value : this.registry), + links: (links != null ? links.value : this.links)); + } +} + +@JsonSerializable(explicitToJson: true) +class LauncherTypeDocument { + LauncherTypeDocument({ + required this.id, + required this.data, + }); + + factory LauncherTypeDocument.fromJson(Map json) => + _$LauncherTypeDocumentFromJson(json); + + @JsonKey(name: 'id') + final String id; + @JsonKey(name: 'data') + final LauncherType data; + static const fromJsonFactory = _$LauncherTypeDocumentFromJson; + static const toJsonFactory = _$LauncherTypeDocumentToJson; + Map toJson() => _$LauncherTypeDocumentToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is LauncherTypeDocument && + (identical(other.id, id) || + const DeepCollectionEquality().equals(other.id, id)) && + (identical(other.data, data) || + const DeepCollectionEquality().equals(other.data, data))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(id) ^ + const DeepCollectionEquality().hash(data) ^ + runtimeType.hashCode; +} + +extension $LauncherTypeDocumentExtension on LauncherTypeDocument { + LauncherTypeDocument copyWith({String? id, LauncherType? data}) { + return LauncherTypeDocument(id: id ?? this.id, data: data ?? this.data); + } + + LauncherTypeDocument copyWithWrapped( + {Wrapped? id, Wrapped? data}) { + return LauncherTypeDocument( + id: (id != null ? id.value : this.id), + data: (data != null ? data.value : this.data)); + } +} + +@JsonSerializable(explicitToJson: true) +class LauncherType { + LauncherType({ + required this.key, + required this.name, + required this.defaults, + required this.enabled, + }); + + factory LauncherType.fromJson(Map json) => + _$LauncherTypeFromJson(json); + + @JsonKey(name: 'key') + final String key; + @JsonKey(name: 'name') + final String name; + @JsonKey(name: 'defaults') + final Object defaults; + @JsonKey(name: 'enabled') + final bool enabled; + static const fromJsonFactory = _$LauncherTypeFromJson; + static const toJsonFactory = _$LauncherTypeToJson; + Map toJson() => _$LauncherTypeToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is LauncherType && + (identical(other.key, key) || + const DeepCollectionEquality().equals(other.key, key)) && + (identical(other.name, name) || + const DeepCollectionEquality().equals(other.name, name)) && + (identical(other.defaults, defaults) || + const DeepCollectionEquality() + .equals(other.defaults, defaults)) && + (identical(other.enabled, enabled) || + const DeepCollectionEquality().equals(other.enabled, enabled))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(key) ^ + const DeepCollectionEquality().hash(name) ^ + const DeepCollectionEquality().hash(defaults) ^ + const DeepCollectionEquality().hash(enabled) ^ + runtimeType.hashCode; +} + +extension $LauncherTypeExtension on LauncherType { + LauncherType copyWith( + {String? key, String? name, Object? defaults, bool? enabled}) { + return LauncherType( + key: key ?? this.key, + name: name ?? this.name, + defaults: defaults ?? this.defaults, + enabled: enabled ?? this.enabled); + } + + LauncherType copyWithWrapped( + {Wrapped? key, + Wrapped? name, + Wrapped? defaults, + Wrapped? enabled}) { + return LauncherType( + key: (key != null ? key.value : this.key), + name: (name != null ? name.value : this.name), + defaults: (defaults != null ? defaults.value : this.defaults), + enabled: (enabled != null ? enabled.value : this.enabled)); + } +} + +@JsonSerializable(explicitToJson: true) +class GameTypeDocument { + GameTypeDocument({ + required this.id, + required this.data, + }); + + factory GameTypeDocument.fromJson(Map json) => + _$GameTypeDocumentFromJson(json); + + @JsonKey(name: 'id') + final String id; + @JsonKey(name: 'data') + final GameType data; + static const fromJsonFactory = _$GameTypeDocumentFromJson; + static const toJsonFactory = _$GameTypeDocumentToJson; + Map toJson() => _$GameTypeDocumentToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is GameTypeDocument && + (identical(other.id, id) || + const DeepCollectionEquality().equals(other.id, id)) && + (identical(other.data, data) || + const DeepCollectionEquality().equals(other.data, data))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(id) ^ + const DeepCollectionEquality().hash(data) ^ + runtimeType.hashCode; +} + +extension $GameTypeDocumentExtension on GameTypeDocument { + GameTypeDocument copyWith({String? id, GameType? data}) { + return GameTypeDocument(id: id ?? this.id, data: data ?? this.data); + } + + GameTypeDocument copyWithWrapped( + {Wrapped? id, Wrapped? data}) { + return GameTypeDocument( + id: (id != null ? id.value : this.id), + data: (data != null ? data.value : this.data)); + } +} + +@JsonSerializable(explicitToJson: true) +class GameType { + GameType({ + required this.key, + required this.name, + required this.defaults, + required this.enabled, + }); + + factory GameType.fromJson(Map json) => + _$GameTypeFromJson(json); + + @JsonKey(name: 'key') + final String key; + @JsonKey(name: 'name') + final String name; + @JsonKey(name: 'defaults') + final Object defaults; + @JsonKey(name: 'enabled') + final bool enabled; + static const fromJsonFactory = _$GameTypeFromJson; + static const toJsonFactory = _$GameTypeToJson; + Map toJson() => _$GameTypeToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is GameType && + (identical(other.key, key) || + const DeepCollectionEquality().equals(other.key, key)) && + (identical(other.name, name) || + const DeepCollectionEquality().equals(other.name, name)) && + (identical(other.defaults, defaults) || + const DeepCollectionEquality() + .equals(other.defaults, defaults)) && + (identical(other.enabled, enabled) || + const DeepCollectionEquality().equals(other.enabled, enabled))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(key) ^ + const DeepCollectionEquality().hash(name) ^ + const DeepCollectionEquality().hash(defaults) ^ + const DeepCollectionEquality().hash(enabled) ^ + runtimeType.hashCode; +} + +extension $GameTypeExtension on GameType { + GameType copyWith( + {String? key, String? name, Object? defaults, bool? enabled}) { + return GameType( + key: key ?? this.key, + name: name ?? this.name, + defaults: defaults ?? this.defaults, + enabled: enabled ?? this.enabled); + } + + GameType copyWithWrapped( + {Wrapped? key, + Wrapped? name, + Wrapped? defaults, + Wrapped? enabled}) { + return GameType( + key: (key != null ? key.value : this.key), + name: (name != null ? name.value : this.name), + defaults: (defaults != null ? defaults.value : this.defaults), + enabled: (enabled != null ? enabled.value : this.enabled)); + } +} + +@JsonSerializable(explicitToJson: true) +class RegistryValue { + RegistryValue({ + required this.view, + required this.key, + required this.value, + }); + + factory RegistryValue.fromJson(Map json) => + _$RegistryValueFromJson(json); + + @JsonKey(name: 'view') + final int view; + @JsonKey(name: 'key') + final String key; + @JsonKey(name: 'value') + final String value; + static const fromJsonFactory = _$RegistryValueFromJson; + static const toJsonFactory = _$RegistryValueToJson; + Map toJson() => _$RegistryValueToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is RegistryValue && + (identical(other.view, view) || + const DeepCollectionEquality().equals(other.view, view)) && + (identical(other.key, key) || + const DeepCollectionEquality().equals(other.key, key)) && + (identical(other.value, value) || + const DeepCollectionEquality().equals(other.value, value))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(view) ^ + const DeepCollectionEquality().hash(key) ^ + const DeepCollectionEquality().hash(value) ^ + runtimeType.hashCode; +} + +extension $RegistryValueExtension on RegistryValue { + RegistryValue copyWith({int? view, String? key, String? value}) { + return RegistryValue( + view: view ?? this.view, + key: key ?? this.key, + value: value ?? this.value); + } + + RegistryValue copyWithWrapped( + {Wrapped? view, Wrapped? key, Wrapped? value}) { + return RegistryValue( + view: (view != null ? view.value : this.view), + key: (key != null ? key.value : this.key), + value: (value != null ? value.value : this.value)); + } +} + +@JsonSerializable(explicitToJson: true) +class GameDocument { + GameDocument({ + required this.id, + required this.data, + }); + + factory GameDocument.fromJson(Map json) => + _$GameDocumentFromJson(json); + + @JsonKey(name: 'id') + final String id; + @JsonKey(name: 'data') + final Game data; + static const fromJsonFactory = _$GameDocumentFromJson; + static const toJsonFactory = _$GameDocumentToJson; + Map toJson() => _$GameDocumentToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is GameDocument && + (identical(other.id, id) || + const DeepCollectionEquality().equals(other.id, id)) && + (identical(other.data, data) || + const DeepCollectionEquality().equals(other.data, data))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(id) ^ + const DeepCollectionEquality().hash(data) ^ + runtimeType.hashCode; +} + +extension $GameDocumentExtension on GameDocument { + GameDocument copyWith({String? id, Game? data}) { + return GameDocument(id: id ?? this.id, data: data ?? this.data); + } + + GameDocument copyWithWrapped({Wrapped? id, Wrapped? data}) { + return GameDocument( + id: (id != null ? id.value : this.id), + data: (data != null ? data.value : this.data)); + } +} + +@JsonSerializable(explicitToJson: true) +class Game { + Game({ + required this.key, + required this.platform, + required this.defaults, + }); + + factory Game.fromJson(Map json) => _$GameFromJson(json); + + @JsonKey(name: 'key') + final String key; + @JsonKey(name: 'platform') + final String platform; + @JsonKey(name: 'defaults') + final Object defaults; + static const fromJsonFactory = _$GameFromJson; + static const toJsonFactory = _$GameToJson; + Map toJson() => _$GameToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is Game && + (identical(other.key, key) || + const DeepCollectionEquality().equals(other.key, key)) && + (identical(other.platform, platform) || + const DeepCollectionEquality() + .equals(other.platform, platform)) && + (identical(other.defaults, defaults) || + const DeepCollectionEquality() + .equals(other.defaults, defaults))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(key) ^ + const DeepCollectionEquality().hash(platform) ^ + const DeepCollectionEquality().hash(defaults) ^ + runtimeType.hashCode; +} + +extension $GameExtension on Game { + Game copyWith({String? key, String? platform, Object? defaults}) { + return Game( + key: key ?? this.key, + platform: platform ?? this.platform, + defaults: defaults ?? this.defaults); + } + + Game copyWithWrapped( + {Wrapped? key, + Wrapped? platform, + Wrapped? defaults}) { + return Game( + key: (key != null ? key.value : this.key), + platform: (platform != null ? platform.value : this.platform), + defaults: (defaults != null ? defaults.value : this.defaults)); + } +} + +@JsonSerializable(explicitToJson: true) +class Platform$Registry { + Platform$Registry({ + this.uninstallCmd, + this.installDir, + this.version, + }); + + factory Platform$Registry.fromJson(Map json) => + _$Platform$RegistryFromJson(json); + + @JsonKey(name: 'uninstallCmd') + final RegistryValue? uninstallCmd; + @JsonKey(name: 'installDir') + final RegistryValue? installDir; + @JsonKey(name: 'version') + final RegistryValue? version; + static const fromJsonFactory = _$Platform$RegistryFromJson; + static const toJsonFactory = _$Platform$RegistryToJson; + Map toJson() => _$Platform$RegistryToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is Platform$Registry && + (identical(other.uninstallCmd, uninstallCmd) || + const DeepCollectionEquality() + .equals(other.uninstallCmd, uninstallCmd)) && + (identical(other.installDir, installDir) || + const DeepCollectionEquality() + .equals(other.installDir, installDir)) && + (identical(other.version, version) || + const DeepCollectionEquality().equals(other.version, version))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(uninstallCmd) ^ + const DeepCollectionEquality().hash(installDir) ^ + const DeepCollectionEquality().hash(version) ^ + runtimeType.hashCode; +} + +extension $Platform$RegistryExtension on Platform$Registry { + Platform$Registry copyWith( + {RegistryValue? uninstallCmd, + RegistryValue? installDir, + RegistryValue? version}) { + return Platform$Registry( + uninstallCmd: uninstallCmd ?? this.uninstallCmd, + installDir: installDir ?? this.installDir, + version: version ?? this.version); + } + + Platform$Registry copyWithWrapped( + {Wrapped? uninstallCmd, + Wrapped? installDir, + Wrapped? version}) { + return Platform$Registry( + uninstallCmd: + (uninstallCmd != null ? uninstallCmd.value : this.uninstallCmd), + installDir: (installDir != null ? installDir.value : this.installDir), + version: (version != null ? version.value : this.version)); + } +} + +@JsonSerializable(explicitToJson: true) +class Platform$Links { + Platform$Links({ + this.install, + this.website, + }); + + factory Platform$Links.fromJson(Map json) => + _$Platform$LinksFromJson(json); + + @JsonKey(name: 'install') + final String? install; + @JsonKey(name: 'website') + final String? website; + static const fromJsonFactory = _$Platform$LinksFromJson; + static const toJsonFactory = _$Platform$LinksToJson; + Map toJson() => _$Platform$LinksToJson(this); + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is Platform$Links && + (identical(other.install, install) || + const DeepCollectionEquality() + .equals(other.install, install)) && + (identical(other.website, website) || + const DeepCollectionEquality().equals(other.website, website))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(install) ^ + const DeepCollectionEquality().hash(website) ^ + runtimeType.hashCode; +} + +extension $Platform$LinksExtension on Platform$Links { + Platform$Links copyWith({String? install, String? website}) { + return Platform$Links( + install: install ?? this.install, website: website ?? this.website); + } + + Platform$Links copyWithWrapped( + {Wrapped? install, Wrapped? website}) { + return Platform$Links( + install: (install != null ? install.value : this.install), + website: (website != null ? website.value : this.website)); + } +} + +typedef $JsonFactory = T Function(Map json); + +class $CustomJsonDecoder { + $CustomJsonDecoder(this.factories); + + final Map factories; + + dynamic decode(dynamic entity) { + if (entity is Iterable) { + return _decodeList(entity); + } + + if (entity is T) { + return entity; + } + + if (isTypeOf()) { + return entity; + } + + if (isTypeOf()) { + return entity; + } + + if (entity is Map) { + return _decodeMap(entity); + } + + return entity; + } + + T _decodeMap(Map values) { + final jsonFactory = factories[T]; + if (jsonFactory == null || jsonFactory is! $JsonFactory) { + return throw "Could not find factory for type $T. Is '$T: $T.fromJsonFactory' included in the CustomJsonDecoder instance creation in bootstrapper.dart?"; + } + + return jsonFactory(values); + } + + List _decodeList(Iterable values) => + values.where((v) => v != null).map((v) => decode(v) as T).toList(); +} + +class $JsonSerializableConverter extends chopper.JsonConverter { + @override + FutureOr> convertResponse( + chopper.Response response) async { + if (response.bodyString.isEmpty) { + // In rare cases, when let's say 204 (no content) is returned - + // we cannot decode the missing json with the result type specified + return chopper.Response(response.base, null, error: response.error); + } + + final jsonRes = await super.convertResponse(response); + return jsonRes.copyWith( + body: $jsonDecoder.decode(jsonRes.body) as ResultType); + } +} + +final $jsonDecoder = $CustomJsonDecoder(generatedMapping); + +// ignore: unused_element +String? _dateToJson(DateTime? date) { + if (date == null) { + return null; + } + + final year = date.year.toString(); + final month = date.month < 10 ? '0${date.month}' : date.month.toString(); + final day = date.day < 10 ? '0${date.day}' : date.day.toString(); + + return '$year-$month-$day'; +} + +class Wrapped { + final T value; + const Wrapped.value(this.value); +} diff --git a/launchpad_app/lib/gen/launchpad_api.swagger.g.dart b/launchpad_app/lib/gen/launchpad_api.swagger.g.dart new file mode 100644 index 00000000..390717f1 --- /dev/null +++ b/launchpad_app/lib/gen/launchpad_api.swagger.g.dart @@ -0,0 +1,198 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'launchpad_api.swagger.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Status _$StatusFromJson(Map json) => Status( + authenticated: json['authenticated'] as bool, + email: json['email'] as String, + ); + +Map _$StatusToJson(Status instance) => { + 'authenticated': instance.authenticated, + 'email': instance.email, + }; + +ReleaseInfo _$ReleaseInfoFromJson(Map json) => ReleaseInfo( + tag: json['tag'] as String?, + version: json['version'] as String, + installer: json['installer'] as String, + releasePage: json['releasePage'] as String, + timestamp: json['timestamp'] as int?, + ); + +Map _$ReleaseInfoToJson(ReleaseInfo instance) => + { + 'tag': instance.tag, + 'version': instance.version, + 'installer': instance.installer, + 'releasePage': instance.releasePage, + 'timestamp': instance.timestamp, + }; + +PlatformDocument _$PlatformDocumentFromJson(Map json) => + PlatformDocument( + id: json['id'] as String, + data: Platform.fromJson(json['data'] as Map), + ); + +Map _$PlatformDocumentToJson(PlatformDocument instance) => + { + 'id': instance.id, + 'data': instance.data.toJson(), + }; + +Platform _$PlatformFromJson(Map json) => Platform( + key: json['key'] as String, + name: json['name'] as String, + launcherType: json['launcherType'] as String, + gameType: json['gameType'] as String, + $class: json['class'] as String, + enabled: json['enabled'] as bool, + registry: json['registry'] == null + ? null + : Platform$Registry.fromJson( + json['registry'] as Map), + links: json['links'] == null + ? null + : Platform$Links.fromJson(json['links'] as Map), + ); + +Map _$PlatformToJson(Platform instance) => { + 'key': instance.key, + 'name': instance.name, + 'launcherType': instance.launcherType, + 'gameType': instance.gameType, + 'class': instance.$class, + 'enabled': instance.enabled, + 'registry': instance.registry?.toJson(), + 'links': instance.links?.toJson(), + }; + +LauncherTypeDocument _$LauncherTypeDocumentFromJson( + Map json) => + LauncherTypeDocument( + id: json['id'] as String, + data: LauncherType.fromJson(json['data'] as Map), + ); + +Map _$LauncherTypeDocumentToJson( + LauncherTypeDocument instance) => + { + 'id': instance.id, + 'data': instance.data.toJson(), + }; + +LauncherType _$LauncherTypeFromJson(Map json) => LauncherType( + key: json['key'] as String, + name: json['name'] as String, + defaults: json['defaults'] as Object, + enabled: json['enabled'] as bool, + ); + +Map _$LauncherTypeToJson(LauncherType instance) => + { + 'key': instance.key, + 'name': instance.name, + 'defaults': instance.defaults, + 'enabled': instance.enabled, + }; + +GameTypeDocument _$GameTypeDocumentFromJson(Map json) => + GameTypeDocument( + id: json['id'] as String, + data: GameType.fromJson(json['data'] as Map), + ); + +Map _$GameTypeDocumentToJson(GameTypeDocument instance) => + { + 'id': instance.id, + 'data': instance.data.toJson(), + }; + +GameType _$GameTypeFromJson(Map json) => GameType( + key: json['key'] as String, + name: json['name'] as String, + defaults: json['defaults'] as Object, + enabled: json['enabled'] as bool, + ); + +Map _$GameTypeToJson(GameType instance) => { + 'key': instance.key, + 'name': instance.name, + 'defaults': instance.defaults, + 'enabled': instance.enabled, + }; + +RegistryValue _$RegistryValueFromJson(Map json) => + RegistryValue( + view: json['view'] as int, + key: json['key'] as String, + value: json['value'] as String, + ); + +Map _$RegistryValueToJson(RegistryValue instance) => + { + 'view': instance.view, + 'key': instance.key, + 'value': instance.value, + }; + +GameDocument _$GameDocumentFromJson(Map json) => GameDocument( + id: json['id'] as String, + data: Game.fromJson(json['data'] as Map), + ); + +Map _$GameDocumentToJson(GameDocument instance) => + { + 'id': instance.id, + 'data': instance.data.toJson(), + }; + +Game _$GameFromJson(Map json) => Game( + key: json['key'] as String, + platform: json['platform'] as String, + defaults: json['defaults'] as Object, + ); + +Map _$GameToJson(Game instance) => { + 'key': instance.key, + 'platform': instance.platform, + 'defaults': instance.defaults, + }; + +Platform$Registry _$Platform$RegistryFromJson(Map json) => + Platform$Registry( + uninstallCmd: json['uninstallCmd'] == null + ? null + : RegistryValue.fromJson( + json['uninstallCmd'] as Map), + installDir: json['installDir'] == null + ? null + : RegistryValue.fromJson(json['installDir'] as Map), + version: json['version'] == null + ? null + : RegistryValue.fromJson(json['version'] as Map), + ); + +Map _$Platform$RegistryToJson(Platform$Registry instance) => + { + 'uninstallCmd': instance.uninstallCmd?.toJson(), + 'installDir': instance.installDir?.toJson(), + 'version': instance.version?.toJson(), + }; + +Platform$Links _$Platform$LinksFromJson(Map json) => + Platform$Links( + install: json['install'] as String?, + website: json['website'] as String?, + ); + +Map _$Platform$LinksToJson(Platform$Links instance) => + { + 'install': instance.install, + 'website': instance.website, + }; diff --git a/launchpad_app/lib/main.dart b/launchpad_app/lib/main.dart new file mode 100644 index 00000000..d8f6c7e6 --- /dev/null +++ b/launchpad_app/lib/main.dart @@ -0,0 +1,154 @@ +import 'package:catcher/catcher.dart'; +import 'package:fluent_ui/fluent_ui.dart' hide Page; +import 'package:flutter/foundation.dart'; +import 'package:flutter_acrylic/flutter_acrylic.dart' as flutter_acrylic; +import 'package:flutter_intro/flutter_intro.dart'; +import 'package:hive/hive.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/main_window/data/persisted_state_storage.dart'; +import 'package:launchpad_app/src/routing/router.dart'; +import 'package:launchpad_app/src/utils/globals.dart'; +import 'package:launchpad_app/src/utils/theme_provider.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:protocol_handler/protocol_handler.dart'; +import 'package:state_persistence/state_persistence.dart'; +import 'package:system_theme/system_theme.dart'; +import 'package:url_strategy/url_strategy.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:windows_single_instance/windows_single_instance.dart'; + +/// Checks if the current environment is a desktop environment. +bool get isDesktop { + if (kIsWeb) return false; + return [ + TargetPlatform.windows, + TargetPlatform.linux, + TargetPlatform.macOS, + ].contains(defaultTargetPlatform); +} + +bool get isWindows { + if (kIsWeb) return false; + return (defaultTargetPlatform == TargetPlatform.windows); +} + +void main(List args) async { + WidgetsFlutterBinding.ensureInitialized(); + + final container = ProviderContainer(); + + await protocolHandler.register("launchpad"); + + if (isWindows) { + await WindowsSingleInstance.ensureSingleInstance( + args, "com.volantisdev.launchpad", onSecondWindow: (args) { + String? path; + + for (var arg in args) { + const prefix = "launchpad://"; + + if (arg.startsWith(prefix)) { + path = arg.replaceFirst(prefix, "/"); + + break; + } + } + + if (path != null && path.isNotEmpty) { + container.read(routerProvider).go(path); + } + }); + } + + // if it's not on the web, windows or android, load the accent color + if (!kIsWeb && + [ + TargetPlatform.windows, + TargetPlatform.android, + ].contains(defaultTargetPlatform)) { + SystemTheme.accentColor.load(); + } + + setPathUrlStrategy(); + + var path = await getApplicationSupportDirectory(); + Hive.init(path.path); + + if (isDesktop) { + await flutter_acrylic.Window.initialize(); + await WindowManager.instance.ensureInitialized(); + windowManager.waitUntilReadyToShow().then((_) async { + await windowManager.setTitleBarStyle( + TitleBarStyle.hidden, + windowButtonVisibility: false, + ); + await windowManager.setSize(const Size(1200, 800)); + await windowManager.setMinimumSize(const Size(675, 600)); + await windowManager.center(); + await windowManager.show(); + await windowManager.setPreventClose(true); + await windowManager.setSkipTaskbar(false); + }); + } + + Catcher( + rootWidget: UncontrolledProviderScope( + container: container, + child: Intro(child: const MyApp()), + ), + debugConfig: CatcherOptions(DialogReportMode(), [ConsoleHandler()]), + releaseConfig: CatcherOptions(DialogReportMode(), [ + EmailManualHandler(["support@volantisdev.com"]) + ]), + ); +} + +class MyApp extends HookConsumerWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final appTheme = ref.watch(appThemeProvider); + final router = ref.watch(routerProvider); + + return PersistedAppState( + storage: HiveStateStorage(), + child: FluentApp.router( + title: appTitle, + themeMode: appTheme.mode, + debugShowCheckedModeBanner: false, + color: appTheme.color, + darkTheme: ThemeData( + brightness: Brightness.dark, + accentColor: appTheme.color, + visualDensity: VisualDensity.standard, + focusTheme: FocusThemeData( + glowFactor: is10footScreen() ? 2.0 : 0.0, + ), + ), + theme: ThemeData( + accentColor: appTheme.color, + visualDensity: VisualDensity.standard, + focusTheme: FocusThemeData( + glowFactor: is10footScreen() ? 2.0 : 0.0, + ), + ), + locale: appTheme.locale, + builder: (context, child) { + return Directionality( + textDirection: appTheme.textDirection, + child: NavigationPaneTheme( + data: NavigationPaneThemeData( + backgroundColor: appTheme.windowEffect != + flutter_acrylic.WindowEffect.disabled + ? Colors.transparent + : null, + ), + child: child!, + ), + ); + }, + routerDelegate: router.routerDelegate, + routeInformationParser: router.routeInformationParser)); + } +} diff --git a/launchpad_app/lib/proto/BlizzardProductDb.pb.dart b/launchpad_app/lib/proto/BlizzardProductDb.pb.dart new file mode 100644 index 00000000..e1124447 --- /dev/null +++ b/launchpad_app/lib/proto/BlizzardProductDb.pb.dart @@ -0,0 +1,1399 @@ +/// +// Generated code. Do not modify. +// source: BlizzardProductDb.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'BlizzardProductDb.pbenum.dart'; + +export 'BlizzardProductDb.pbenum.dart'; + +class ProductInstall extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProductInstall', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uid') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'productCode', protoName: 'productCode') + ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'settings', subBuilder: UserSettings.create) + ..aOM(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'cachedProductState', protoName: 'cachedProductState', subBuilder: CachedProductState.create) + ..aOM(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'productOperations', protoName: 'productOperations', subBuilder: ProductOperations.create) + ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'keyword') + ..hasRequiredFields = false + ; + + ProductInstall._() : super(); + factory ProductInstall({ + $core.String? uid, + $core.String? productCode, + UserSettings? settings, + CachedProductState? cachedProductState, + ProductOperations? productOperations, + $core.String? keyword, + }) { + final _result = create(); + if (uid != null) { + _result.uid = uid; + } + if (productCode != null) { + _result.productCode = productCode; + } + if (settings != null) { + _result.settings = settings; + } + if (cachedProductState != null) { + _result.cachedProductState = cachedProductState; + } + if (productOperations != null) { + _result.productOperations = productOperations; + } + if (keyword != null) { + _result.keyword = keyword; + } + return _result; + } + factory ProductInstall.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ProductInstall.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ProductInstall clone() => ProductInstall()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ProductInstall copyWith(void Function(ProductInstall) updates) => super.copyWith((message) => updates(message as ProductInstall)) as ProductInstall; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static ProductInstall create() => ProductInstall._(); + ProductInstall createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ProductInstall getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ProductInstall? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get uid => $_getSZ(0); + @$pb.TagNumber(1) + set uid($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasUid() => $_has(0); + @$pb.TagNumber(1) + void clearUid() => clearField(1); + + @$pb.TagNumber(2) + $core.String get productCode => $_getSZ(1); + @$pb.TagNumber(2) + set productCode($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasProductCode() => $_has(1); + @$pb.TagNumber(2) + void clearProductCode() => clearField(2); + + @$pb.TagNumber(3) + UserSettings get settings => $_getN(2); + @$pb.TagNumber(3) + set settings(UserSettings v) { setField(3, v); } + @$pb.TagNumber(3) + $core.bool hasSettings() => $_has(2); + @$pb.TagNumber(3) + void clearSettings() => clearField(3); + @$pb.TagNumber(3) + UserSettings ensureSettings() => $_ensure(2); + + @$pb.TagNumber(4) + CachedProductState get cachedProductState => $_getN(3); + @$pb.TagNumber(4) + set cachedProductState(CachedProductState v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasCachedProductState() => $_has(3); + @$pb.TagNumber(4) + void clearCachedProductState() => clearField(4); + @$pb.TagNumber(4) + CachedProductState ensureCachedProductState() => $_ensure(3); + + @$pb.TagNumber(5) + ProductOperations get productOperations => $_getN(4); + @$pb.TagNumber(5) + set productOperations(ProductOperations v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasProductOperations() => $_has(4); + @$pb.TagNumber(5) + void clearProductOperations() => clearField(5); + @$pb.TagNumber(5) + ProductOperations ensureProductOperations() => $_ensure(4); + + @$pb.TagNumber(6) + $core.String get keyword => $_getSZ(5); + @$pb.TagNumber(6) + set keyword($core.String v) { $_setString(5, v); } + @$pb.TagNumber(6) + $core.bool hasKeyword() => $_has(5); + @$pb.TagNumber(6) + void clearKeyword() => clearField(6); +} + +class UserSettings extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserSettings', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'installPath', protoName: 'installPath') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'playRegion', protoName: 'playRegion') + ..e(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desktopShortcut', $pb.PbFieldType.OE, protoName: 'desktopShortcut', defaultOrMaker: UserSettings_ShortcutOption.SHORTCUT_NONE, valueOf: UserSettings_ShortcutOption.valueOf, enumValues: UserSettings_ShortcutOption.values) + ..e(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startmenuShortcut', $pb.PbFieldType.OE, protoName: 'startmenuShortcut', defaultOrMaker: UserSettings_ShortcutOption.SHORTCUT_NONE, valueOf: UserSettings_ShortcutOption.valueOf, enumValues: UserSettings_ShortcutOption.values) + ..e(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'languageSettings', $pb.PbFieldType.OE, protoName: 'languageSettings', defaultOrMaker: UserSettings_LanguageSettingType.LANGSETTING_NONE, valueOf: UserSettings_LanguageSettingType.valueOf, enumValues: UserSettings_LanguageSettingType.values) + ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'selectedTextLanguage', protoName: 'selectedTextLanguage') + ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'selectedSpeechLanguage', protoName: 'selectedSpeechLanguage') + ..pc(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'languages', $pb.PbFieldType.PM, subBuilder: LanguageSetting.create) + ..aOS(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gfxOverrideTags', protoName: 'gfxOverrideTags') + ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'versionbranch') + ..aOS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'countryCode3Letter', protoName: 'countryCode3Letter') + ..aOS(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'countryCode2Letter', protoName: 'countryCode2Letter') + ..aOS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'productExtra', protoName: 'productExtra') + ..hasRequiredFields = false + ; + + UserSettings._() : super(); + factory UserSettings({ + $core.String? installPath, + $core.String? playRegion, + UserSettings_ShortcutOption? desktopShortcut, + UserSettings_ShortcutOption? startmenuShortcut, + UserSettings_LanguageSettingType? languageSettings, + $core.String? selectedTextLanguage, + $core.String? selectedSpeechLanguage, + $core.Iterable? languages, + $core.String? gfxOverrideTags, + $core.String? versionbranch, + $core.String? countryCode3Letter, + $core.String? countryCode2Letter, + $core.String? productExtra, + }) { + final _result = create(); + if (installPath != null) { + _result.installPath = installPath; + } + if (playRegion != null) { + _result.playRegion = playRegion; + } + if (desktopShortcut != null) { + _result.desktopShortcut = desktopShortcut; + } + if (startmenuShortcut != null) { + _result.startmenuShortcut = startmenuShortcut; + } + if (languageSettings != null) { + _result.languageSettings = languageSettings; + } + if (selectedTextLanguage != null) { + _result.selectedTextLanguage = selectedTextLanguage; + } + if (selectedSpeechLanguage != null) { + _result.selectedSpeechLanguage = selectedSpeechLanguage; + } + if (languages != null) { + _result.languages.addAll(languages); + } + if (gfxOverrideTags != null) { + _result.gfxOverrideTags = gfxOverrideTags; + } + if (versionbranch != null) { + _result.versionbranch = versionbranch; + } + if (countryCode3Letter != null) { + _result.countryCode3Letter = countryCode3Letter; + } + if (countryCode2Letter != null) { + _result.countryCode2Letter = countryCode2Letter; + } + if (productExtra != null) { + _result.productExtra = productExtra; + } + return _result; + } + factory UserSettings.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UserSettings.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UserSettings clone() => UserSettings()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UserSettings copyWith(void Function(UserSettings) updates) => super.copyWith((message) => updates(message as UserSettings)) as UserSettings; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static UserSettings create() => UserSettings._(); + UserSettings createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UserSettings getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UserSettings? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get installPath => $_getSZ(0); + @$pb.TagNumber(1) + set installPath($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasInstallPath() => $_has(0); + @$pb.TagNumber(1) + void clearInstallPath() => clearField(1); + + @$pb.TagNumber(2) + $core.String get playRegion => $_getSZ(1); + @$pb.TagNumber(2) + set playRegion($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasPlayRegion() => $_has(1); + @$pb.TagNumber(2) + void clearPlayRegion() => clearField(2); + + @$pb.TagNumber(3) + UserSettings_ShortcutOption get desktopShortcut => $_getN(2); + @$pb.TagNumber(3) + set desktopShortcut(UserSettings_ShortcutOption v) { setField(3, v); } + @$pb.TagNumber(3) + $core.bool hasDesktopShortcut() => $_has(2); + @$pb.TagNumber(3) + void clearDesktopShortcut() => clearField(3); + + @$pb.TagNumber(4) + UserSettings_ShortcutOption get startmenuShortcut => $_getN(3); + @$pb.TagNumber(4) + set startmenuShortcut(UserSettings_ShortcutOption v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasStartmenuShortcut() => $_has(3); + @$pb.TagNumber(4) + void clearStartmenuShortcut() => clearField(4); + + @$pb.TagNumber(5) + UserSettings_LanguageSettingType get languageSettings => $_getN(4); + @$pb.TagNumber(5) + set languageSettings(UserSettings_LanguageSettingType v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasLanguageSettings() => $_has(4); + @$pb.TagNumber(5) + void clearLanguageSettings() => clearField(5); + + @$pb.TagNumber(6) + $core.String get selectedTextLanguage => $_getSZ(5); + @$pb.TagNumber(6) + set selectedTextLanguage($core.String v) { $_setString(5, v); } + @$pb.TagNumber(6) + $core.bool hasSelectedTextLanguage() => $_has(5); + @$pb.TagNumber(6) + void clearSelectedTextLanguage() => clearField(6); + + @$pb.TagNumber(7) + $core.String get selectedSpeechLanguage => $_getSZ(6); + @$pb.TagNumber(7) + set selectedSpeechLanguage($core.String v) { $_setString(6, v); } + @$pb.TagNumber(7) + $core.bool hasSelectedSpeechLanguage() => $_has(6); + @$pb.TagNumber(7) + void clearSelectedSpeechLanguage() => clearField(7); + + @$pb.TagNumber(8) + $core.List get languages => $_getList(7); + + @$pb.TagNumber(9) + $core.String get gfxOverrideTags => $_getSZ(8); + @$pb.TagNumber(9) + set gfxOverrideTags($core.String v) { $_setString(8, v); } + @$pb.TagNumber(9) + $core.bool hasGfxOverrideTags() => $_has(8); + @$pb.TagNumber(9) + void clearGfxOverrideTags() => clearField(9); + + @$pb.TagNumber(10) + $core.String get versionbranch => $_getSZ(9); + @$pb.TagNumber(10) + set versionbranch($core.String v) { $_setString(9, v); } + @$pb.TagNumber(10) + $core.bool hasVersionbranch() => $_has(9); + @$pb.TagNumber(10) + void clearVersionbranch() => clearField(10); + + @$pb.TagNumber(11) + $core.String get countryCode3Letter => $_getSZ(10); + @$pb.TagNumber(11) + set countryCode3Letter($core.String v) { $_setString(10, v); } + @$pb.TagNumber(11) + $core.bool hasCountryCode3Letter() => $_has(10); + @$pb.TagNumber(11) + void clearCountryCode3Letter() => clearField(11); + + @$pb.TagNumber(12) + $core.String get countryCode2Letter => $_getSZ(11); + @$pb.TagNumber(12) + set countryCode2Letter($core.String v) { $_setString(11, v); } + @$pb.TagNumber(12) + $core.bool hasCountryCode2Letter() => $_has(11); + @$pb.TagNumber(12) + void clearCountryCode2Letter() => clearField(12); + + @$pb.TagNumber(13) + $core.String get productExtra => $_getSZ(12); + @$pb.TagNumber(13) + set productExtra($core.String v) { $_setString(12, v); } + @$pb.TagNumber(13) + $core.bool hasProductExtra() => $_has(12); + @$pb.TagNumber(13) + void clearProductExtra() => clearField(13); +} + +class LanguageSetting extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'LanguageSetting', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'language') + ..e(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'option', $pb.PbFieldType.OE, defaultOrMaker: LanguageSetting_LanguageOption.LANGOPTION_NONE, valueOf: LanguageSetting_LanguageOption.valueOf, enumValues: LanguageSetting_LanguageOption.values) + ..hasRequiredFields = false + ; + + LanguageSetting._() : super(); + factory LanguageSetting({ + $core.String? language, + LanguageSetting_LanguageOption? option, + }) { + final _result = create(); + if (language != null) { + _result.language = language; + } + if (option != null) { + _result.option = option; + } + return _result; + } + factory LanguageSetting.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory LanguageSetting.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + LanguageSetting clone() => LanguageSetting()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + LanguageSetting copyWith(void Function(LanguageSetting) updates) => super.copyWith((message) => updates(message as LanguageSetting)) as LanguageSetting; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static LanguageSetting create() => LanguageSetting._(); + LanguageSetting createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static LanguageSetting getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static LanguageSetting? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get language => $_getSZ(0); + @$pb.TagNumber(1) + set language($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasLanguage() => $_has(0); + @$pb.TagNumber(1) + void clearLanguage() => clearField(1); + + @$pb.TagNumber(2) + LanguageSetting_LanguageOption get option => $_getN(1); + @$pb.TagNumber(2) + set option(LanguageSetting_LanguageOption v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasOption() => $_has(1); + @$pb.TagNumber(2) + void clearOption() => clearField(2); +} + +class CachedProductState extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CachedProductState', createEmptyInstance: create) + ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'baseProductState', protoName: 'baseProductState', subBuilder: BaseProductState.create) + ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'backfillProgress', protoName: 'backfillProgress', subBuilder: BackfillProgress.create) + ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'repairProgress', protoName: 'repairProgress', subBuilder: RepairProgress.create) + ..aOM(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updateProgress', protoName: 'updateProgress', subBuilder: UpdateProgress.create) + ..hasRequiredFields = false + ; + + CachedProductState._() : super(); + factory CachedProductState({ + BaseProductState? baseProductState, + BackfillProgress? backfillProgress, + RepairProgress? repairProgress, + UpdateProgress? updateProgress, + }) { + final _result = create(); + if (baseProductState != null) { + _result.baseProductState = baseProductState; + } + if (backfillProgress != null) { + _result.backfillProgress = backfillProgress; + } + if (repairProgress != null) { + _result.repairProgress = repairProgress; + } + if (updateProgress != null) { + _result.updateProgress = updateProgress; + } + return _result; + } + factory CachedProductState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory CachedProductState.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + CachedProductState clone() => CachedProductState()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + CachedProductState copyWith(void Function(CachedProductState) updates) => super.copyWith((message) => updates(message as CachedProductState)) as CachedProductState; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static CachedProductState create() => CachedProductState._(); + CachedProductState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static CachedProductState getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static CachedProductState? _defaultInstance; + + @$pb.TagNumber(1) + BaseProductState get baseProductState => $_getN(0); + @$pb.TagNumber(1) + set baseProductState(BaseProductState v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasBaseProductState() => $_has(0); + @$pb.TagNumber(1) + void clearBaseProductState() => clearField(1); + @$pb.TagNumber(1) + BaseProductState ensureBaseProductState() => $_ensure(0); + + @$pb.TagNumber(2) + BackfillProgress get backfillProgress => $_getN(1); + @$pb.TagNumber(2) + set backfillProgress(BackfillProgress v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasBackfillProgress() => $_has(1); + @$pb.TagNumber(2) + void clearBackfillProgress() => clearField(2); + @$pb.TagNumber(2) + BackfillProgress ensureBackfillProgress() => $_ensure(1); + + @$pb.TagNumber(3) + RepairProgress get repairProgress => $_getN(2); + @$pb.TagNumber(3) + set repairProgress(RepairProgress v) { setField(3, v); } + @$pb.TagNumber(3) + $core.bool hasRepairProgress() => $_has(2); + @$pb.TagNumber(3) + void clearRepairProgress() => clearField(3); + @$pb.TagNumber(3) + RepairProgress ensureRepairProgress() => $_ensure(2); + + @$pb.TagNumber(4) + UpdateProgress get updateProgress => $_getN(3); + @$pb.TagNumber(4) + set updateProgress(UpdateProgress v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasUpdateProgress() => $_has(3); + @$pb.TagNumber(4) + void clearUpdateProgress() => clearField(4); + @$pb.TagNumber(4) + UpdateProgress ensureUpdateProgress() => $_ensure(3); +} + +class BaseProductState extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BaseProductState', createEmptyInstance: create) + ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'installed') + ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'playable') + ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updateComplete', protoName: 'updateComplete') + ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'backgroundDownloadAvailable', protoName: 'backgroundDownloadAvailable') + ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'backgroundDownloadComplete', protoName: 'backgroundDownloadComplete') + ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'currentVersion', protoName: 'currentVersion') + ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'currentVersionStr', protoName: 'currentVersionStr') + ..pc(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'installedBuildConfig', $pb.PbFieldType.PM, protoName: 'installedBuildConfig', subBuilder: BuildConfig.create) + ..pc(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'backgroundDownloadBuildConfig', $pb.PbFieldType.PM, protoName: 'backgroundDownloadBuildConfig', subBuilder: BuildConfig.create) + ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'decryptionKey', protoName: 'decryptionKey') + ..pPS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'completedInstallActions', protoName: 'completedInstallActions') + ..aOS(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tags') + ..hasRequiredFields = false + ; + + BaseProductState._() : super(); + factory BaseProductState({ + $core.bool? installed, + $core.bool? playable, + $core.bool? updateComplete, + $core.bool? backgroundDownloadAvailable, + $core.bool? backgroundDownloadComplete, + $core.String? currentVersion, + $core.String? currentVersionStr, + $core.Iterable? installedBuildConfig, + $core.Iterable? backgroundDownloadBuildConfig, + $core.String? decryptionKey, + $core.Iterable<$core.String>? completedInstallActions, + $core.String? tags, + }) { + final _result = create(); + if (installed != null) { + _result.installed = installed; + } + if (playable != null) { + _result.playable = playable; + } + if (updateComplete != null) { + _result.updateComplete = updateComplete; + } + if (backgroundDownloadAvailable != null) { + _result.backgroundDownloadAvailable = backgroundDownloadAvailable; + } + if (backgroundDownloadComplete != null) { + _result.backgroundDownloadComplete = backgroundDownloadComplete; + } + if (currentVersion != null) { + _result.currentVersion = currentVersion; + } + if (currentVersionStr != null) { + _result.currentVersionStr = currentVersionStr; + } + if (installedBuildConfig != null) { + _result.installedBuildConfig.addAll(installedBuildConfig); + } + if (backgroundDownloadBuildConfig != null) { + _result.backgroundDownloadBuildConfig.addAll(backgroundDownloadBuildConfig); + } + if (decryptionKey != null) { + _result.decryptionKey = decryptionKey; + } + if (completedInstallActions != null) { + _result.completedInstallActions.addAll(completedInstallActions); + } + if (tags != null) { + _result.tags = tags; + } + return _result; + } + factory BaseProductState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory BaseProductState.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + BaseProductState clone() => BaseProductState()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + BaseProductState copyWith(void Function(BaseProductState) updates) => super.copyWith((message) => updates(message as BaseProductState)) as BaseProductState; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static BaseProductState create() => BaseProductState._(); + BaseProductState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static BaseProductState getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static BaseProductState? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get installed => $_getBF(0); + @$pb.TagNumber(1) + set installed($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasInstalled() => $_has(0); + @$pb.TagNumber(1) + void clearInstalled() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get playable => $_getBF(1); + @$pb.TagNumber(2) + set playable($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasPlayable() => $_has(1); + @$pb.TagNumber(2) + void clearPlayable() => clearField(2); + + @$pb.TagNumber(3) + $core.bool get updateComplete => $_getBF(2); + @$pb.TagNumber(3) + set updateComplete($core.bool v) { $_setBool(2, v); } + @$pb.TagNumber(3) + $core.bool hasUpdateComplete() => $_has(2); + @$pb.TagNumber(3) + void clearUpdateComplete() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get backgroundDownloadAvailable => $_getBF(3); + @$pb.TagNumber(4) + set backgroundDownloadAvailable($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasBackgroundDownloadAvailable() => $_has(3); + @$pb.TagNumber(4) + void clearBackgroundDownloadAvailable() => clearField(4); + + @$pb.TagNumber(5) + $core.bool get backgroundDownloadComplete => $_getBF(4); + @$pb.TagNumber(5) + set backgroundDownloadComplete($core.bool v) { $_setBool(4, v); } + @$pb.TagNumber(5) + $core.bool hasBackgroundDownloadComplete() => $_has(4); + @$pb.TagNumber(5) + void clearBackgroundDownloadComplete() => clearField(5); + + @$pb.TagNumber(6) + $core.String get currentVersion => $_getSZ(5); + @$pb.TagNumber(6) + set currentVersion($core.String v) { $_setString(5, v); } + @$pb.TagNumber(6) + $core.bool hasCurrentVersion() => $_has(5); + @$pb.TagNumber(6) + void clearCurrentVersion() => clearField(6); + + @$pb.TagNumber(7) + $core.String get currentVersionStr => $_getSZ(6); + @$pb.TagNumber(7) + set currentVersionStr($core.String v) { $_setString(6, v); } + @$pb.TagNumber(7) + $core.bool hasCurrentVersionStr() => $_has(6); + @$pb.TagNumber(7) + void clearCurrentVersionStr() => clearField(7); + + @$pb.TagNumber(8) + $core.List get installedBuildConfig => $_getList(7); + + @$pb.TagNumber(9) + $core.List get backgroundDownloadBuildConfig => $_getList(8); + + @$pb.TagNumber(10) + $core.String get decryptionKey => $_getSZ(9); + @$pb.TagNumber(10) + set decryptionKey($core.String v) { $_setString(9, v); } + @$pb.TagNumber(10) + $core.bool hasDecryptionKey() => $_has(9); + @$pb.TagNumber(10) + void clearDecryptionKey() => clearField(10); + + @$pb.TagNumber(11) + $core.List<$core.String> get completedInstallActions => $_getList(10); + + @$pb.TagNumber(17) + $core.String get tags => $_getSZ(11); + @$pb.TagNumber(17) + set tags($core.String v) { $_setString(11, v); } + @$pb.TagNumber(17) + $core.bool hasTags() => $_has(11); + @$pb.TagNumber(17) + void clearTags() => clearField(17); +} + +class BuildConfig extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BuildConfig', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'region') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'buildConfig', protoName: 'buildConfig') + ..hasRequiredFields = false + ; + + BuildConfig._() : super(); + factory BuildConfig({ + $core.String? region, + $core.String? buildConfig, + }) { + final _result = create(); + if (region != null) { + _result.region = region; + } + if (buildConfig != null) { + _result.buildConfig = buildConfig; + } + return _result; + } + factory BuildConfig.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory BuildConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + BuildConfig clone() => BuildConfig()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + BuildConfig copyWith(void Function(BuildConfig) updates) => super.copyWith((message) => updates(message as BuildConfig)) as BuildConfig; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static BuildConfig create() => BuildConfig._(); + BuildConfig createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static BuildConfig getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static BuildConfig? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get region => $_getSZ(0); + @$pb.TagNumber(1) + set region($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasRegion() => $_has(0); + @$pb.TagNumber(1) + void clearRegion() => clearField(1); + + @$pb.TagNumber(2) + $core.String get buildConfig => $_getSZ(1); + @$pb.TagNumber(2) + set buildConfig($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasBuildConfig() => $_has(1); + @$pb.TagNumber(2) + void clearBuildConfig() => clearField(2); +} + +class BackfillProgress extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BackfillProgress', createEmptyInstance: create) + ..a<$core.double>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'progress', $pb.PbFieldType.OD) + ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'backgrounddownload') + ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'paused') + ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'downloadLimit', protoName: 'downloadLimit') + ..hasRequiredFields = false + ; + + BackfillProgress._() : super(); + factory BackfillProgress({ + $core.double? progress, + $core.bool? backgrounddownload, + $core.bool? paused, + $core.bool? downloadLimit, + }) { + final _result = create(); + if (progress != null) { + _result.progress = progress; + } + if (backgrounddownload != null) { + _result.backgrounddownload = backgrounddownload; + } + if (paused != null) { + _result.paused = paused; + } + if (downloadLimit != null) { + _result.downloadLimit = downloadLimit; + } + return _result; + } + factory BackfillProgress.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory BackfillProgress.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + BackfillProgress clone() => BackfillProgress()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + BackfillProgress copyWith(void Function(BackfillProgress) updates) => super.copyWith((message) => updates(message as BackfillProgress)) as BackfillProgress; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static BackfillProgress create() => BackfillProgress._(); + BackfillProgress createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static BackfillProgress getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static BackfillProgress? _defaultInstance; + + @$pb.TagNumber(1) + $core.double get progress => $_getN(0); + @$pb.TagNumber(1) + set progress($core.double v) { $_setDouble(0, v); } + @$pb.TagNumber(1) + $core.bool hasProgress() => $_has(0); + @$pb.TagNumber(1) + void clearProgress() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get backgrounddownload => $_getBF(1); + @$pb.TagNumber(2) + set backgrounddownload($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasBackgrounddownload() => $_has(1); + @$pb.TagNumber(2) + void clearBackgrounddownload() => clearField(2); + + @$pb.TagNumber(3) + $core.bool get paused => $_getBF(2); + @$pb.TagNumber(3) + set paused($core.bool v) { $_setBool(2, v); } + @$pb.TagNumber(3) + $core.bool hasPaused() => $_has(2); + @$pb.TagNumber(3) + void clearPaused() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get downloadLimit => $_getBF(3); + @$pb.TagNumber(4) + set downloadLimit($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasDownloadLimit() => $_has(3); + @$pb.TagNumber(4) + void clearDownloadLimit() => clearField(4); +} + +class RepairProgress extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepairProgress', createEmptyInstance: create) + ..a<$core.double>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'progress', $pb.PbFieldType.OD) + ..hasRequiredFields = false + ; + + RepairProgress._() : super(); + factory RepairProgress({ + $core.double? progress, + }) { + final _result = create(); + if (progress != null) { + _result.progress = progress; + } + return _result; + } + factory RepairProgress.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RepairProgress.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RepairProgress clone() => RepairProgress()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RepairProgress copyWith(void Function(RepairProgress) updates) => super.copyWith((message) => updates(message as RepairProgress)) as RepairProgress; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static RepairProgress create() => RepairProgress._(); + RepairProgress createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RepairProgress getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RepairProgress? _defaultInstance; + + @$pb.TagNumber(1) + $core.double get progress => $_getN(0); + @$pb.TagNumber(1) + set progress($core.double v) { $_setDouble(0, v); } + @$pb.TagNumber(1) + $core.bool hasProgress() => $_has(0); + @$pb.TagNumber(1) + void clearProgress() => clearField(1); +} + +class UpdateProgress extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateProgress', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'lastDiscSetUsed', protoName: 'lastDiscSetUsed') + ..a<$core.double>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'progress', $pb.PbFieldType.OD) + ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'discIgnored', protoName: 'discIgnored') + ..a<$fixnum.Int64>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'totalToDownload', $pb.PbFieldType.OU6, protoName: 'totalToDownload', defaultOrMaker: $fixnum.Int64.ZERO) + ..hasRequiredFields = false + ; + + UpdateProgress._() : super(); + factory UpdateProgress({ + $core.String? lastDiscSetUsed, + $core.double? progress, + $core.bool? discIgnored, + $fixnum.Int64? totalToDownload, + }) { + final _result = create(); + if (lastDiscSetUsed != null) { + _result.lastDiscSetUsed = lastDiscSetUsed; + } + if (progress != null) { + _result.progress = progress; + } + if (discIgnored != null) { + _result.discIgnored = discIgnored; + } + if (totalToDownload != null) { + _result.totalToDownload = totalToDownload; + } + return _result; + } + factory UpdateProgress.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdateProgress.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UpdateProgress clone() => UpdateProgress()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UpdateProgress copyWith(void Function(UpdateProgress) updates) => super.copyWith((message) => updates(message as UpdateProgress)) as UpdateProgress; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static UpdateProgress create() => UpdateProgress._(); + UpdateProgress createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UpdateProgress getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UpdateProgress? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get lastDiscSetUsed => $_getSZ(0); + @$pb.TagNumber(1) + set lastDiscSetUsed($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasLastDiscSetUsed() => $_has(0); + @$pb.TagNumber(1) + void clearLastDiscSetUsed() => clearField(1); + + @$pb.TagNumber(2) + $core.double get progress => $_getN(1); + @$pb.TagNumber(2) + set progress($core.double v) { $_setDouble(1, v); } + @$pb.TagNumber(2) + $core.bool hasProgress() => $_has(1); + @$pb.TagNumber(2) + void clearProgress() => clearField(2); + + @$pb.TagNumber(3) + $core.bool get discIgnored => $_getBF(2); + @$pb.TagNumber(3) + set discIgnored($core.bool v) { $_setBool(2, v); } + @$pb.TagNumber(3) + $core.bool hasDiscIgnored() => $_has(2); + @$pb.TagNumber(3) + void clearDiscIgnored() => clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get totalToDownload => $_getI64(3); + @$pb.TagNumber(4) + set totalToDownload($fixnum.Int64 v) { $_setInt64(3, v); } + @$pb.TagNumber(4) + $core.bool hasTotalToDownload() => $_has(3); + @$pb.TagNumber(4) + void clearTotalToDownload() => clearField(4); +} + +class ProductOperations extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProductOperations', createEmptyInstance: create) + ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'activeOperation', $pb.PbFieldType.OE, protoName: 'activeOperation', defaultOrMaker: ProductOperations_Operation.OP_UPDATE, valueOf: ProductOperations_Operation.valueOf, enumValues: ProductOperations_Operation.values) + ..a<$fixnum.Int64>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'priority', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) + ..hasRequiredFields = false + ; + + ProductOperations._() : super(); + factory ProductOperations({ + ProductOperations_Operation? activeOperation, + $fixnum.Int64? priority, + }) { + final _result = create(); + if (activeOperation != null) { + _result.activeOperation = activeOperation; + } + if (priority != null) { + _result.priority = priority; + } + return _result; + } + factory ProductOperations.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ProductOperations.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ProductOperations clone() => ProductOperations()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ProductOperations copyWith(void Function(ProductOperations) updates) => super.copyWith((message) => updates(message as ProductOperations)) as ProductOperations; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static ProductOperations create() => ProductOperations._(); + ProductOperations createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ProductOperations getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ProductOperations? _defaultInstance; + + @$pb.TagNumber(1) + ProductOperations_Operation get activeOperation => $_getN(0); + @$pb.TagNumber(1) + set activeOperation(ProductOperations_Operation v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasActiveOperation() => $_has(0); + @$pb.TagNumber(1) + void clearActiveOperation() => clearField(1); + + @$pb.TagNumber(2) + $fixnum.Int64 get priority => $_getI64(1); + @$pb.TagNumber(2) + set priority($fixnum.Int64 v) { $_setInt64(1, v); } + @$pb.TagNumber(2) + $core.bool hasPriority() => $_has(1); + @$pb.TagNumber(2) + void clearPriority() => clearField(2); +} + +class ProductConfig extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProductConfig', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'productCode', protoName: 'productCode') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'metadataHash', protoName: 'metadataHash') + ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timestamp') + ..hasRequiredFields = false + ; + + ProductConfig._() : super(); + factory ProductConfig({ + $core.String? productCode, + $core.String? metadataHash, + $core.String? timestamp, + }) { + final _result = create(); + if (productCode != null) { + _result.productCode = productCode; + } + if (metadataHash != null) { + _result.metadataHash = metadataHash; + } + if (timestamp != null) { + _result.timestamp = timestamp; + } + return _result; + } + factory ProductConfig.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ProductConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ProductConfig clone() => ProductConfig()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ProductConfig copyWith(void Function(ProductConfig) updates) => super.copyWith((message) => updates(message as ProductConfig)) as ProductConfig; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static ProductConfig create() => ProductConfig._(); + ProductConfig createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ProductConfig getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ProductConfig? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get productCode => $_getSZ(0); + @$pb.TagNumber(1) + set productCode($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasProductCode() => $_has(0); + @$pb.TagNumber(1) + void clearProductCode() => clearField(1); + + @$pb.TagNumber(2) + $core.String get metadataHash => $_getSZ(1); + @$pb.TagNumber(2) + set metadataHash($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasMetadataHash() => $_has(1); + @$pb.TagNumber(2) + void clearMetadataHash() => clearField(2); + + @$pb.TagNumber(3) + $core.String get timestamp => $_getSZ(2); + @$pb.TagNumber(3) + set timestamp($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasTimestamp() => $_has(2); + @$pb.TagNumber(3) + void clearTimestamp() => clearField(3); +} + +class ActiveProcess extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ActiveProcess', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'processName', protoName: 'processName') + ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pid', $pb.PbFieldType.O3) + ..pPS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uri') + ..hasRequiredFields = false + ; + + ActiveProcess._() : super(); + factory ActiveProcess({ + $core.String? processName, + $core.int? pid, + $core.Iterable<$core.String>? uri, + }) { + final _result = create(); + if (processName != null) { + _result.processName = processName; + } + if (pid != null) { + _result.pid = pid; + } + if (uri != null) { + _result.uri.addAll(uri); + } + return _result; + } + factory ActiveProcess.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ActiveProcess.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ActiveProcess clone() => ActiveProcess()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ActiveProcess copyWith(void Function(ActiveProcess) updates) => super.copyWith((message) => updates(message as ActiveProcess)) as ActiveProcess; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static ActiveProcess create() => ActiveProcess._(); + ActiveProcess createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ActiveProcess getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ActiveProcess? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get processName => $_getSZ(0); + @$pb.TagNumber(1) + set processName($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasProcessName() => $_has(0); + @$pb.TagNumber(1) + void clearProcessName() => clearField(1); + + @$pb.TagNumber(2) + $core.int get pid => $_getIZ(1); + @$pb.TagNumber(2) + set pid($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasPid() => $_has(1); + @$pb.TagNumber(2) + void clearPid() => clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.String> get uri => $_getList(2); +} + +class InstallHandshake extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'InstallHandshake', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'product') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uid') + ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'settings', subBuilder: UserSettings.create) + ..hasRequiredFields = false + ; + + InstallHandshake._() : super(); + factory InstallHandshake({ + $core.String? product, + $core.String? uid, + UserSettings? settings, + }) { + final _result = create(); + if (product != null) { + _result.product = product; + } + if (uid != null) { + _result.uid = uid; + } + if (settings != null) { + _result.settings = settings; + } + return _result; + } + factory InstallHandshake.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory InstallHandshake.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + InstallHandshake clone() => InstallHandshake()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + InstallHandshake copyWith(void Function(InstallHandshake) updates) => super.copyWith((message) => updates(message as InstallHandshake)) as InstallHandshake; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static InstallHandshake create() => InstallHandshake._(); + InstallHandshake createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static InstallHandshake getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static InstallHandshake? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get product => $_getSZ(0); + @$pb.TagNumber(1) + set product($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasProduct() => $_has(0); + @$pb.TagNumber(1) + void clearProduct() => clearField(1); + + @$pb.TagNumber(2) + $core.String get uid => $_getSZ(1); + @$pb.TagNumber(2) + set uid($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasUid() => $_has(1); + @$pb.TagNumber(2) + void clearUid() => clearField(2); + + @$pb.TagNumber(3) + UserSettings get settings => $_getN(2); + @$pb.TagNumber(3) + set settings(UserSettings v) { setField(3, v); } + @$pb.TagNumber(3) + $core.bool hasSettings() => $_has(2); + @$pb.TagNumber(3) + void clearSettings() => clearField(3); + @$pb.TagNumber(3) + UserSettings ensureSettings() => $_ensure(2); +} + +class DownloadSettings extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DownloadSettings', createEmptyInstance: create) + ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'downloadLimit', $pb.PbFieldType.O3, protoName: 'downloadLimit') + ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'backfillLimit', $pb.PbFieldType.O3, protoName: 'backfillLimit') + ..hasRequiredFields = false + ; + + DownloadSettings._() : super(); + factory DownloadSettings({ + $core.int? downloadLimit, + $core.int? backfillLimit, + }) { + final _result = create(); + if (downloadLimit != null) { + _result.downloadLimit = downloadLimit; + } + if (backfillLimit != null) { + _result.backfillLimit = backfillLimit; + } + return _result; + } + factory DownloadSettings.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DownloadSettings.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + DownloadSettings clone() => DownloadSettings()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DownloadSettings copyWith(void Function(DownloadSettings) updates) => super.copyWith((message) => updates(message as DownloadSettings)) as DownloadSettings; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static DownloadSettings create() => DownloadSettings._(); + DownloadSettings createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DownloadSettings getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DownloadSettings? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get downloadLimit => $_getIZ(0); + @$pb.TagNumber(1) + set downloadLimit($core.int v) { $_setSignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasDownloadLimit() => $_has(0); + @$pb.TagNumber(1) + void clearDownloadLimit() => clearField(1); + + @$pb.TagNumber(2) + $core.int get backfillLimit => $_getIZ(1); + @$pb.TagNumber(2) + set backfillLimit($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasBackfillLimit() => $_has(1); + @$pb.TagNumber(2) + void clearBackfillLimit() => clearField(2); +} + +class Database extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Database', createEmptyInstance: create) + ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'productInstall', $pb.PbFieldType.PM, protoName: 'productInstall', subBuilder: ProductInstall.create) + ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'activeInstalls', $pb.PbFieldType.PM, protoName: 'activeInstalls', subBuilder: InstallHandshake.create) + ..pc(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'activeProcesses', $pb.PbFieldType.PM, protoName: 'activeProcesses', subBuilder: ActiveProcess.create) + ..pc(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'productConfigs', $pb.PbFieldType.PM, protoName: 'productConfigs', subBuilder: ProductConfig.create) + ..aOM(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'downloadSettings', protoName: 'downloadSettings', subBuilder: DownloadSettings.create) + ..hasRequiredFields = false + ; + + Database._() : super(); + factory Database({ + $core.Iterable? productInstall, + $core.Iterable? activeInstalls, + $core.Iterable? activeProcesses, + $core.Iterable? productConfigs, + DownloadSettings? downloadSettings, + }) { + final _result = create(); + if (productInstall != null) { + _result.productInstall.addAll(productInstall); + } + if (activeInstalls != null) { + _result.activeInstalls.addAll(activeInstalls); + } + if (activeProcesses != null) { + _result.activeProcesses.addAll(activeProcesses); + } + if (productConfigs != null) { + _result.productConfigs.addAll(productConfigs); + } + if (downloadSettings != null) { + _result.downloadSettings = downloadSettings; + } + return _result; + } + factory Database.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Database.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + Database clone() => Database()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Database copyWith(void Function(Database) updates) => super.copyWith((message) => updates(message as Database)) as Database; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static Database create() => Database._(); + Database createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static Database getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Database? _defaultInstance; + + @$pb.TagNumber(1) + $core.List get productInstall => $_getList(0); + + @$pb.TagNumber(2) + $core.List get activeInstalls => $_getList(1); + + @$pb.TagNumber(3) + $core.List get activeProcesses => $_getList(2); + + @$pb.TagNumber(4) + $core.List get productConfigs => $_getList(3); + + @$pb.TagNumber(5) + DownloadSettings get downloadSettings => $_getN(4); + @$pb.TagNumber(5) + set downloadSettings(DownloadSettings v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasDownloadSettings() => $_has(4); + @$pb.TagNumber(5) + void clearDownloadSettings() => clearField(5); + @$pb.TagNumber(5) + DownloadSettings ensureDownloadSettings() => $_ensure(4); +} + diff --git a/launchpad_app/lib/proto/BlizzardProductDb.pbenum.dart b/launchpad_app/lib/proto/BlizzardProductDb.pbenum.dart new file mode 100644 index 00000000..c3213bee --- /dev/null +++ b/launchpad_app/lib/proto/BlizzardProductDb.pbenum.dart @@ -0,0 +1,83 @@ +/// +// Generated code. Do not modify. +// source: BlizzardProductDb.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +// ignore_for_file: UNDEFINED_SHOWN_NAME +import 'dart:core' as $core; +import 'package:protobuf/protobuf.dart' as $pb; + +class UserSettings_ShortcutOption extends $pb.ProtobufEnum { + static const UserSettings_ShortcutOption SHORTCUT_NONE = UserSettings_ShortcutOption._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SHORTCUT_NONE'); + static const UserSettings_ShortcutOption SHORTCUT_USER = UserSettings_ShortcutOption._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SHORTCUT_USER'); + static const UserSettings_ShortcutOption SHORTCUT_ALL_USERS = UserSettings_ShortcutOption._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SHORTCUT_ALL_USERS'); + + static const $core.List values = [ + SHORTCUT_NONE, + SHORTCUT_USER, + SHORTCUT_ALL_USERS, + ]; + + static final $core.Map<$core.int, UserSettings_ShortcutOption> _byValue = $pb.ProtobufEnum.initByValue(values); + static UserSettings_ShortcutOption? valueOf($core.int value) => _byValue[value]; + + const UserSettings_ShortcutOption._($core.int v, $core.String n) : super(v, n); +} + +class UserSettings_LanguageSettingType extends $pb.ProtobufEnum { + static const UserSettings_LanguageSettingType LANGSETTING_NONE = UserSettings_LanguageSettingType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGSETTING_NONE'); + static const UserSettings_LanguageSettingType LANGSETING_SINGLE = UserSettings_LanguageSettingType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGSETING_SINGLE'); + static const UserSettings_LanguageSettingType LANGSETTING_SIMPLE = UserSettings_LanguageSettingType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGSETTING_SIMPLE'); + static const UserSettings_LanguageSettingType LANGSETTING_ADVANCED = UserSettings_LanguageSettingType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGSETTING_ADVANCED'); + + static const $core.List values = [ + LANGSETTING_NONE, + LANGSETING_SINGLE, + LANGSETTING_SIMPLE, + LANGSETTING_ADVANCED, + ]; + + static final $core.Map<$core.int, UserSettings_LanguageSettingType> _byValue = $pb.ProtobufEnum.initByValue(values); + static UserSettings_LanguageSettingType? valueOf($core.int value) => _byValue[value]; + + const UserSettings_LanguageSettingType._($core.int v, $core.String n) : super(v, n); +} + +class LanguageSetting_LanguageOption extends $pb.ProtobufEnum { + static const LanguageSetting_LanguageOption LANGOPTION_NONE = LanguageSetting_LanguageOption._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGOPTION_NONE'); + static const LanguageSetting_LanguageOption LANGOPTION_TEXT = LanguageSetting_LanguageOption._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGOPTION_TEXT'); + static const LanguageSetting_LanguageOption LANGOPTION_SPEECH = LanguageSetting_LanguageOption._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGOPTION_SPEECH'); + static const LanguageSetting_LanguageOption LANGOPTION_TEXT_AND_SPEECH = LanguageSetting_LanguageOption._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LANGOPTION_TEXT_AND_SPEECH'); + + static const $core.List values = [ + LANGOPTION_NONE, + LANGOPTION_TEXT, + LANGOPTION_SPEECH, + LANGOPTION_TEXT_AND_SPEECH, + ]; + + static final $core.Map<$core.int, LanguageSetting_LanguageOption> _byValue = $pb.ProtobufEnum.initByValue(values); + static LanguageSetting_LanguageOption? valueOf($core.int value) => _byValue[value]; + + const LanguageSetting_LanguageOption._($core.int v, $core.String n) : super(v, n); +} + +class ProductOperations_Operation extends $pb.ProtobufEnum { + static const ProductOperations_Operation OP_UPDATE = ProductOperations_Operation._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OP_UPDATE'); + static const ProductOperations_Operation OP_BACKFILL = ProductOperations_Operation._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OP_BACKFILL'); + static const ProductOperations_Operation OP_REPAIR = ProductOperations_Operation._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OP_REPAIR'); + + static const $core.List values = [ + OP_UPDATE, + OP_BACKFILL, + OP_REPAIR, + ]; + + static final $core.Map<$core.int, ProductOperations_Operation> _byValue = $pb.ProtobufEnum.initByValue(values); + static ProductOperations_Operation? valueOf($core.int value) => _byValue[value]; + + const ProductOperations_Operation._($core.int v, $core.String n) : super(v, n); +} + diff --git a/launchpad_app/lib/proto/BlizzardProductDb.pbjson.dart b/launchpad_app/lib/proto/BlizzardProductDb.pbjson.dart new file mode 100644 index 00000000..71c0ce97 --- /dev/null +++ b/launchpad_app/lib/proto/BlizzardProductDb.pbjson.dart @@ -0,0 +1,256 @@ +/// +// Generated code. Do not modify. +// source: BlizzardProductDb.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +import 'dart:core' as $core; +import 'dart:convert' as $convert; +import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use productInstallDescriptor instead') +const ProductInstall$json = const { + '1': 'ProductInstall', + '2': const [ + const {'1': 'uid', '3': 1, '4': 1, '5': 9, '10': 'uid'}, + const {'1': 'productCode', '3': 2, '4': 1, '5': 9, '10': 'productCode'}, + const {'1': 'settings', '3': 3, '4': 1, '5': 11, '6': '.UserSettings', '10': 'settings'}, + const {'1': 'cachedProductState', '3': 4, '4': 1, '5': 11, '6': '.CachedProductState', '10': 'cachedProductState'}, + const {'1': 'productOperations', '3': 5, '4': 1, '5': 11, '6': '.ProductOperations', '10': 'productOperations'}, + const {'1': 'keyword', '3': 6, '4': 1, '5': 9, '10': 'keyword'}, + ], +}; + +/// Descriptor for `ProductInstall`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List productInstallDescriptor = $convert.base64Decode('Cg5Qcm9kdWN0SW5zdGFsbBIQCgN1aWQYASABKAlSA3VpZBIgCgtwcm9kdWN0Q29kZRgCIAEoCVILcHJvZHVjdENvZGUSKQoIc2V0dGluZ3MYAyABKAsyDS5Vc2VyU2V0dGluZ3NSCHNldHRpbmdzEkMKEmNhY2hlZFByb2R1Y3RTdGF0ZRgEIAEoCzITLkNhY2hlZFByb2R1Y3RTdGF0ZVISY2FjaGVkUHJvZHVjdFN0YXRlEkAKEXByb2R1Y3RPcGVyYXRpb25zGAUgASgLMhIuUHJvZHVjdE9wZXJhdGlvbnNSEXByb2R1Y3RPcGVyYXRpb25zEhgKB2tleXdvcmQYBiABKAlSB2tleXdvcmQ='); +@$core.Deprecated('Use userSettingsDescriptor instead') +const UserSettings$json = const { + '1': 'UserSettings', + '2': const [ + const {'1': 'installPath', '3': 1, '4': 1, '5': 9, '10': 'installPath'}, + const {'1': 'playRegion', '3': 2, '4': 1, '5': 9, '10': 'playRegion'}, + const {'1': 'desktopShortcut', '3': 3, '4': 1, '5': 14, '6': '.UserSettings.ShortcutOption', '10': 'desktopShortcut'}, + const {'1': 'startmenuShortcut', '3': 4, '4': 1, '5': 14, '6': '.UserSettings.ShortcutOption', '10': 'startmenuShortcut'}, + const {'1': 'languageSettings', '3': 5, '4': 1, '5': 14, '6': '.UserSettings.LanguageSettingType', '10': 'languageSettings'}, + const {'1': 'selectedTextLanguage', '3': 6, '4': 1, '5': 9, '10': 'selectedTextLanguage'}, + const {'1': 'selectedSpeechLanguage', '3': 7, '4': 1, '5': 9, '10': 'selectedSpeechLanguage'}, + const {'1': 'languages', '3': 8, '4': 3, '5': 11, '6': '.LanguageSetting', '10': 'languages'}, + const {'1': 'gfxOverrideTags', '3': 9, '4': 1, '5': 9, '10': 'gfxOverrideTags'}, + const {'1': 'versionbranch', '3': 10, '4': 1, '5': 9, '10': 'versionbranch'}, + const {'1': 'countryCode3Letter', '3': 11, '4': 1, '5': 9, '10': 'countryCode3Letter'}, + const {'1': 'countryCode2Letter', '3': 12, '4': 1, '5': 9, '10': 'countryCode2Letter'}, + const {'1': 'productExtra', '3': 13, '4': 1, '5': 9, '10': 'productExtra'}, + ], + '4': const [UserSettings_ShortcutOption$json, UserSettings_LanguageSettingType$json], +}; + +@$core.Deprecated('Use userSettingsDescriptor instead') +const UserSettings_ShortcutOption$json = const { + '1': 'ShortcutOption', + '2': const [ + const {'1': 'SHORTCUT_NONE', '2': 0}, + const {'1': 'SHORTCUT_USER', '2': 1}, + const {'1': 'SHORTCUT_ALL_USERS', '2': 2}, + ], +}; + +@$core.Deprecated('Use userSettingsDescriptor instead') +const UserSettings_LanguageSettingType$json = const { + '1': 'LanguageSettingType', + '2': const [ + const {'1': 'LANGSETTING_NONE', '2': 0}, + const {'1': 'LANGSETING_SINGLE', '2': 1}, + const {'1': 'LANGSETTING_SIMPLE', '2': 2}, + const {'1': 'LANGSETTING_ADVANCED', '2': 3}, + ], +}; + +/// Descriptor for `UserSettings`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List userSettingsDescriptor = $convert.base64Decode('CgxVc2VyU2V0dGluZ3MSIAoLaW5zdGFsbFBhdGgYASABKAlSC2luc3RhbGxQYXRoEh4KCnBsYXlSZWdpb24YAiABKAlSCnBsYXlSZWdpb24SRgoPZGVza3RvcFNob3J0Y3V0GAMgASgOMhwuVXNlclNldHRpbmdzLlNob3J0Y3V0T3B0aW9uUg9kZXNrdG9wU2hvcnRjdXQSSgoRc3RhcnRtZW51U2hvcnRjdXQYBCABKA4yHC5Vc2VyU2V0dGluZ3MuU2hvcnRjdXRPcHRpb25SEXN0YXJ0bWVudVNob3J0Y3V0Ek0KEGxhbmd1YWdlU2V0dGluZ3MYBSABKA4yIS5Vc2VyU2V0dGluZ3MuTGFuZ3VhZ2VTZXR0aW5nVHlwZVIQbGFuZ3VhZ2VTZXR0aW5ncxIyChRzZWxlY3RlZFRleHRMYW5ndWFnZRgGIAEoCVIUc2VsZWN0ZWRUZXh0TGFuZ3VhZ2USNgoWc2VsZWN0ZWRTcGVlY2hMYW5ndWFnZRgHIAEoCVIWc2VsZWN0ZWRTcGVlY2hMYW5ndWFnZRIuCglsYW5ndWFnZXMYCCADKAsyEC5MYW5ndWFnZVNldHRpbmdSCWxhbmd1YWdlcxIoCg9nZnhPdmVycmlkZVRhZ3MYCSABKAlSD2dmeE92ZXJyaWRlVGFncxIkCg12ZXJzaW9uYnJhbmNoGAogASgJUg12ZXJzaW9uYnJhbmNoEi4KEmNvdW50cnlDb2RlM0xldHRlchgLIAEoCVISY291bnRyeUNvZGUzTGV0dGVyEi4KEmNvdW50cnlDb2RlMkxldHRlchgMIAEoCVISY291bnRyeUNvZGUyTGV0dGVyEiIKDHByb2R1Y3RFeHRyYRgNIAEoCVIMcHJvZHVjdEV4dHJhIk4KDlNob3J0Y3V0T3B0aW9uEhEKDVNIT1JUQ1VUX05PTkUQABIRCg1TSE9SVENVVF9VU0VSEAESFgoSU0hPUlRDVVRfQUxMX1VTRVJTEAIidAoTTGFuZ3VhZ2VTZXR0aW5nVHlwZRIUChBMQU5HU0VUVElOR19OT05FEAASFQoRTEFOR1NFVElOR19TSU5HTEUQARIWChJMQU5HU0VUVElOR19TSU1QTEUQAhIYChRMQU5HU0VUVElOR19BRFZBTkNFRBAD'); +@$core.Deprecated('Use languageSettingDescriptor instead') +const LanguageSetting$json = const { + '1': 'LanguageSetting', + '2': const [ + const {'1': 'language', '3': 1, '4': 1, '5': 9, '10': 'language'}, + const {'1': 'option', '3': 2, '4': 1, '5': 14, '6': '.LanguageSetting.LanguageOption', '10': 'option'}, + ], + '4': const [LanguageSetting_LanguageOption$json], +}; + +@$core.Deprecated('Use languageSettingDescriptor instead') +const LanguageSetting_LanguageOption$json = const { + '1': 'LanguageOption', + '2': const [ + const {'1': 'LANGOPTION_NONE', '2': 0}, + const {'1': 'LANGOPTION_TEXT', '2': 1}, + const {'1': 'LANGOPTION_SPEECH', '2': 2}, + const {'1': 'LANGOPTION_TEXT_AND_SPEECH', '2': 3}, + ], +}; + +/// Descriptor for `LanguageSetting`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List languageSettingDescriptor = $convert.base64Decode('Cg9MYW5ndWFnZVNldHRpbmcSGgoIbGFuZ3VhZ2UYASABKAlSCGxhbmd1YWdlEjcKBm9wdGlvbhgCIAEoDjIfLkxhbmd1YWdlU2V0dGluZy5MYW5ndWFnZU9wdGlvblIGb3B0aW9uInEKDkxhbmd1YWdlT3B0aW9uEhMKD0xBTkdPUFRJT05fTk9ORRAAEhMKD0xBTkdPUFRJT05fVEVYVBABEhUKEUxBTkdPUFRJT05fU1BFRUNIEAISHgoaTEFOR09QVElPTl9URVhUX0FORF9TUEVFQ0gQAw=='); +@$core.Deprecated('Use cachedProductStateDescriptor instead') +const CachedProductState$json = const { + '1': 'CachedProductState', + '2': const [ + const {'1': 'baseProductState', '3': 1, '4': 1, '5': 11, '6': '.BaseProductState', '10': 'baseProductState'}, + const {'1': 'backfillProgress', '3': 2, '4': 1, '5': 11, '6': '.BackfillProgress', '10': 'backfillProgress'}, + const {'1': 'repairProgress', '3': 3, '4': 1, '5': 11, '6': '.RepairProgress', '10': 'repairProgress'}, + const {'1': 'updateProgress', '3': 4, '4': 1, '5': 11, '6': '.UpdateProgress', '10': 'updateProgress'}, + ], +}; + +/// Descriptor for `CachedProductState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List cachedProductStateDescriptor = $convert.base64Decode('ChJDYWNoZWRQcm9kdWN0U3RhdGUSPQoQYmFzZVByb2R1Y3RTdGF0ZRgBIAEoCzIRLkJhc2VQcm9kdWN0U3RhdGVSEGJhc2VQcm9kdWN0U3RhdGUSPQoQYmFja2ZpbGxQcm9ncmVzcxgCIAEoCzIRLkJhY2tmaWxsUHJvZ3Jlc3NSEGJhY2tmaWxsUHJvZ3Jlc3MSNwoOcmVwYWlyUHJvZ3Jlc3MYAyABKAsyDy5SZXBhaXJQcm9ncmVzc1IOcmVwYWlyUHJvZ3Jlc3MSNwoOdXBkYXRlUHJvZ3Jlc3MYBCABKAsyDy5VcGRhdGVQcm9ncmVzc1IOdXBkYXRlUHJvZ3Jlc3M='); +@$core.Deprecated('Use baseProductStateDescriptor instead') +const BaseProductState$json = const { + '1': 'BaseProductState', + '2': const [ + const {'1': 'installed', '3': 1, '4': 1, '5': 8, '10': 'installed'}, + const {'1': 'playable', '3': 2, '4': 1, '5': 8, '10': 'playable'}, + const {'1': 'updateComplete', '3': 3, '4': 1, '5': 8, '10': 'updateComplete'}, + const {'1': 'backgroundDownloadAvailable', '3': 4, '4': 1, '5': 8, '10': 'backgroundDownloadAvailable'}, + const {'1': 'backgroundDownloadComplete', '3': 5, '4': 1, '5': 8, '10': 'backgroundDownloadComplete'}, + const {'1': 'currentVersion', '3': 6, '4': 1, '5': 9, '10': 'currentVersion'}, + const {'1': 'currentVersionStr', '3': 7, '4': 1, '5': 9, '10': 'currentVersionStr'}, + const {'1': 'installedBuildConfig', '3': 8, '4': 3, '5': 11, '6': '.BuildConfig', '10': 'installedBuildConfig'}, + const {'1': 'backgroundDownloadBuildConfig', '3': 9, '4': 3, '5': 11, '6': '.BuildConfig', '10': 'backgroundDownloadBuildConfig'}, + const {'1': 'decryptionKey', '3': 10, '4': 1, '5': 9, '10': 'decryptionKey'}, + const {'1': 'completedInstallActions', '3': 11, '4': 3, '5': 9, '10': 'completedInstallActions'}, + const {'1': 'tags', '3': 17, '4': 1, '5': 9, '10': 'tags'}, + ], +}; + +/// Descriptor for `BaseProductState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List baseProductStateDescriptor = $convert.base64Decode('ChBCYXNlUHJvZHVjdFN0YXRlEhwKCWluc3RhbGxlZBgBIAEoCFIJaW5zdGFsbGVkEhoKCHBsYXlhYmxlGAIgASgIUghwbGF5YWJsZRImCg51cGRhdGVDb21wbGV0ZRgDIAEoCFIOdXBkYXRlQ29tcGxldGUSQAobYmFja2dyb3VuZERvd25sb2FkQXZhaWxhYmxlGAQgASgIUhtiYWNrZ3JvdW5kRG93bmxvYWRBdmFpbGFibGUSPgoaYmFja2dyb3VuZERvd25sb2FkQ29tcGxldGUYBSABKAhSGmJhY2tncm91bmREb3dubG9hZENvbXBsZXRlEiYKDmN1cnJlbnRWZXJzaW9uGAYgASgJUg5jdXJyZW50VmVyc2lvbhIsChFjdXJyZW50VmVyc2lvblN0chgHIAEoCVIRY3VycmVudFZlcnNpb25TdHISQAoUaW5zdGFsbGVkQnVpbGRDb25maWcYCCADKAsyDC5CdWlsZENvbmZpZ1IUaW5zdGFsbGVkQnVpbGRDb25maWcSUgodYmFja2dyb3VuZERvd25sb2FkQnVpbGRDb25maWcYCSADKAsyDC5CdWlsZENvbmZpZ1IdYmFja2dyb3VuZERvd25sb2FkQnVpbGRDb25maWcSJAoNZGVjcnlwdGlvbktleRgKIAEoCVINZGVjcnlwdGlvbktleRI4Chdjb21wbGV0ZWRJbnN0YWxsQWN0aW9ucxgLIAMoCVIXY29tcGxldGVkSW5zdGFsbEFjdGlvbnMSEgoEdGFncxgRIAEoCVIEdGFncw=='); +@$core.Deprecated('Use buildConfigDescriptor instead') +const BuildConfig$json = const { + '1': 'BuildConfig', + '2': const [ + const {'1': 'region', '3': 1, '4': 1, '5': 9, '10': 'region'}, + const {'1': 'buildConfig', '3': 2, '4': 1, '5': 9, '10': 'buildConfig'}, + ], +}; + +/// Descriptor for `BuildConfig`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List buildConfigDescriptor = $convert.base64Decode('CgtCdWlsZENvbmZpZxIWCgZyZWdpb24YASABKAlSBnJlZ2lvbhIgCgtidWlsZENvbmZpZxgCIAEoCVILYnVpbGRDb25maWc='); +@$core.Deprecated('Use backfillProgressDescriptor instead') +const BackfillProgress$json = const { + '1': 'BackfillProgress', + '2': const [ + const {'1': 'progress', '3': 1, '4': 1, '5': 1, '10': 'progress'}, + const {'1': 'backgrounddownload', '3': 2, '4': 1, '5': 8, '10': 'backgrounddownload'}, + const {'1': 'paused', '3': 3, '4': 1, '5': 8, '10': 'paused'}, + const {'1': 'downloadLimit', '3': 4, '4': 1, '5': 8, '10': 'downloadLimit'}, + ], +}; + +/// Descriptor for `BackfillProgress`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List backfillProgressDescriptor = $convert.base64Decode('ChBCYWNrZmlsbFByb2dyZXNzEhoKCHByb2dyZXNzGAEgASgBUghwcm9ncmVzcxIuChJiYWNrZ3JvdW5kZG93bmxvYWQYAiABKAhSEmJhY2tncm91bmRkb3dubG9hZBIWCgZwYXVzZWQYAyABKAhSBnBhdXNlZBIkCg1kb3dubG9hZExpbWl0GAQgASgIUg1kb3dubG9hZExpbWl0'); +@$core.Deprecated('Use repairProgressDescriptor instead') +const RepairProgress$json = const { + '1': 'RepairProgress', + '2': const [ + const {'1': 'progress', '3': 1, '4': 1, '5': 1, '10': 'progress'}, + ], +}; + +/// Descriptor for `RepairProgress`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List repairProgressDescriptor = $convert.base64Decode('Cg5SZXBhaXJQcm9ncmVzcxIaCghwcm9ncmVzcxgBIAEoAVIIcHJvZ3Jlc3M='); +@$core.Deprecated('Use updateProgressDescriptor instead') +const UpdateProgress$json = const { + '1': 'UpdateProgress', + '2': const [ + const {'1': 'lastDiscSetUsed', '3': 1, '4': 1, '5': 9, '10': 'lastDiscSetUsed'}, + const {'1': 'progress', '3': 2, '4': 1, '5': 1, '10': 'progress'}, + const {'1': 'discIgnored', '3': 3, '4': 1, '5': 8, '10': 'discIgnored'}, + const {'1': 'totalToDownload', '3': 4, '4': 1, '5': 4, '10': 'totalToDownload'}, + ], +}; + +/// Descriptor for `UpdateProgress`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List updateProgressDescriptor = $convert.base64Decode('Cg5VcGRhdGVQcm9ncmVzcxIoCg9sYXN0RGlzY1NldFVzZWQYASABKAlSD2xhc3REaXNjU2V0VXNlZBIaCghwcm9ncmVzcxgCIAEoAVIIcHJvZ3Jlc3MSIAoLZGlzY0lnbm9yZWQYAyABKAhSC2Rpc2NJZ25vcmVkEigKD3RvdGFsVG9Eb3dubG9hZBgEIAEoBFIPdG90YWxUb0Rvd25sb2Fk'); +@$core.Deprecated('Use productOperationsDescriptor instead') +const ProductOperations$json = const { + '1': 'ProductOperations', + '2': const [ + const {'1': 'activeOperation', '3': 1, '4': 1, '5': 14, '6': '.ProductOperations.Operation', '10': 'activeOperation'}, + const {'1': 'priority', '3': 2, '4': 1, '5': 4, '10': 'priority'}, + ], + '4': const [ProductOperations_Operation$json], +}; + +@$core.Deprecated('Use productOperationsDescriptor instead') +const ProductOperations_Operation$json = const { + '1': 'Operation', + '2': const [ + const {'1': 'OP_UPDATE', '2': 0}, + const {'1': 'OP_BACKFILL', '2': 1}, + const {'1': 'OP_REPAIR', '2': 2}, + ], +}; + +/// Descriptor for `ProductOperations`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List productOperationsDescriptor = $convert.base64Decode('ChFQcm9kdWN0T3BlcmF0aW9ucxJGCg9hY3RpdmVPcGVyYXRpb24YASABKA4yHC5Qcm9kdWN0T3BlcmF0aW9ucy5PcGVyYXRpb25SD2FjdGl2ZU9wZXJhdGlvbhIaCghwcmlvcml0eRgCIAEoBFIIcHJpb3JpdHkiOgoJT3BlcmF0aW9uEg0KCU9QX1VQREFURRAAEg8KC09QX0JBQ0tGSUxMEAESDQoJT1BfUkVQQUlSEAI='); +@$core.Deprecated('Use productConfigDescriptor instead') +const ProductConfig$json = const { + '1': 'ProductConfig', + '2': const [ + const {'1': 'productCode', '3': 1, '4': 1, '5': 9, '10': 'productCode'}, + const {'1': 'metadataHash', '3': 2, '4': 1, '5': 9, '10': 'metadataHash'}, + const {'1': 'timestamp', '3': 3, '4': 1, '5': 9, '10': 'timestamp'}, + ], +}; + +/// Descriptor for `ProductConfig`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List productConfigDescriptor = $convert.base64Decode('Cg1Qcm9kdWN0Q29uZmlnEiAKC3Byb2R1Y3RDb2RlGAEgASgJUgtwcm9kdWN0Q29kZRIiCgxtZXRhZGF0YUhhc2gYAiABKAlSDG1ldGFkYXRhSGFzaBIcCgl0aW1lc3RhbXAYAyABKAlSCXRpbWVzdGFtcA=='); +@$core.Deprecated('Use activeProcessDescriptor instead') +const ActiveProcess$json = const { + '1': 'ActiveProcess', + '2': const [ + const {'1': 'processName', '3': 1, '4': 1, '5': 9, '10': 'processName'}, + const {'1': 'pid', '3': 2, '4': 1, '5': 5, '10': 'pid'}, + const {'1': 'uri', '3': 3, '4': 3, '5': 9, '10': 'uri'}, + ], +}; + +/// Descriptor for `ActiveProcess`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List activeProcessDescriptor = $convert.base64Decode('Cg1BY3RpdmVQcm9jZXNzEiAKC3Byb2Nlc3NOYW1lGAEgASgJUgtwcm9jZXNzTmFtZRIQCgNwaWQYAiABKAVSA3BpZBIQCgN1cmkYAyADKAlSA3VyaQ=='); +@$core.Deprecated('Use installHandshakeDescriptor instead') +const InstallHandshake$json = const { + '1': 'InstallHandshake', + '2': const [ + const {'1': 'product', '3': 1, '4': 1, '5': 9, '10': 'product'}, + const {'1': 'uid', '3': 2, '4': 1, '5': 9, '10': 'uid'}, + const {'1': 'settings', '3': 3, '4': 1, '5': 11, '6': '.UserSettings', '10': 'settings'}, + ], +}; + +/// Descriptor for `InstallHandshake`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List installHandshakeDescriptor = $convert.base64Decode('ChBJbnN0YWxsSGFuZHNoYWtlEhgKB3Byb2R1Y3QYASABKAlSB3Byb2R1Y3QSEAoDdWlkGAIgASgJUgN1aWQSKQoIc2V0dGluZ3MYAyABKAsyDS5Vc2VyU2V0dGluZ3NSCHNldHRpbmdz'); +@$core.Deprecated('Use downloadSettingsDescriptor instead') +const DownloadSettings$json = const { + '1': 'DownloadSettings', + '2': const [ + const {'1': 'downloadLimit', '3': 1, '4': 1, '5': 5, '10': 'downloadLimit'}, + const {'1': 'backfillLimit', '3': 2, '4': 1, '5': 5, '10': 'backfillLimit'}, + ], +}; + +/// Descriptor for `DownloadSettings`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List downloadSettingsDescriptor = $convert.base64Decode('ChBEb3dubG9hZFNldHRpbmdzEiQKDWRvd25sb2FkTGltaXQYASABKAVSDWRvd25sb2FkTGltaXQSJAoNYmFja2ZpbGxMaW1pdBgCIAEoBVINYmFja2ZpbGxMaW1pdA=='); +@$core.Deprecated('Use databaseDescriptor instead') +const Database$json = const { + '1': 'Database', + '2': const [ + const {'1': 'productInstall', '3': 1, '4': 3, '5': 11, '6': '.ProductInstall', '10': 'productInstall'}, + const {'1': 'activeInstalls', '3': 2, '4': 3, '5': 11, '6': '.InstallHandshake', '10': 'activeInstalls'}, + const {'1': 'activeProcesses', '3': 3, '4': 3, '5': 11, '6': '.ActiveProcess', '10': 'activeProcesses'}, + const {'1': 'productConfigs', '3': 4, '4': 3, '5': 11, '6': '.ProductConfig', '10': 'productConfigs'}, + const {'1': 'downloadSettings', '3': 5, '4': 1, '5': 11, '6': '.DownloadSettings', '10': 'downloadSettings'}, + ], +}; + +/// Descriptor for `Database`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List databaseDescriptor = $convert.base64Decode('CghEYXRhYmFzZRI3Cg5wcm9kdWN0SW5zdGFsbBgBIAMoCzIPLlByb2R1Y3RJbnN0YWxsUg5wcm9kdWN0SW5zdGFsbBI5Cg5hY3RpdmVJbnN0YWxscxgCIAMoCzIRLkluc3RhbGxIYW5kc2hha2VSDmFjdGl2ZUluc3RhbGxzEjgKD2FjdGl2ZVByb2Nlc3NlcxgDIAMoCzIOLkFjdGl2ZVByb2Nlc3NSD2FjdGl2ZVByb2Nlc3NlcxI2Cg5wcm9kdWN0Q29uZmlncxgEIAMoCzIOLlByb2R1Y3RDb25maWdSDnByb2R1Y3RDb25maWdzEj0KEGRvd25sb2FkU2V0dGluZ3MYBSABKAsyES5Eb3dubG9hZFNldHRpbmdzUhBkb3dubG9hZFNldHRpbmdz'); diff --git a/launchpad_app/lib/proto/BlizzardProductDb.pbserver.dart b/launchpad_app/lib/proto/BlizzardProductDb.pbserver.dart new file mode 100644 index 00000000..b5a9b9c8 --- /dev/null +++ b/launchpad_app/lib/proto/BlizzardProductDb.pbserver.dart @@ -0,0 +1,9 @@ +/// +// Generated code. Do not modify. +// source: BlizzardProductDb.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name + +export 'BlizzardProductDb.pb.dart'; + diff --git a/launchpad_app/lib/proto/BlizzardProductDb.proto b/launchpad_app/lib/proto/BlizzardProductDb.proto new file mode 100644 index 00000000..f6fb8637 --- /dev/null +++ b/launchpad_app/lib/proto/BlizzardProductDb.proto @@ -0,0 +1,138 @@ +syntax = "proto3"; + +message ProductInstall { + string uid = 1; + string productCode = 2; + UserSettings settings = 3; + CachedProductState cachedProductState = 4; + ProductOperations productOperations = 5; + string keyword = 6; +} + +message UserSettings { + enum ShortcutOption { + SHORTCUT_NONE = 0; + SHORTCUT_USER = 1; + SHORTCUT_ALL_USERS = 2; + } + + enum LanguageSettingType { + LANGSETTING_NONE = 0; + LANGSETING_SINGLE = 1; + LANGSETTING_SIMPLE = 2; + LANGSETTING_ADVANCED = 3; + } + + string installPath = 1; + string playRegion = 2; + ShortcutOption desktopShortcut = 3; + ShortcutOption startmenuShortcut = 4; + LanguageSettingType languageSettings = 5; + string selectedTextLanguage = 6; + string selectedSpeechLanguage = 7; + repeated LanguageSetting languages = 8; + string gfxOverrideTags = 9; + string versionbranch = 10; + string countryCode3Letter = 11; + string countryCode2Letter = 12; + string productExtra = 13; +} + +message LanguageSetting { + enum LanguageOption { + LANGOPTION_NONE = 0; + LANGOPTION_TEXT = 1; + LANGOPTION_SPEECH = 2; + LANGOPTION_TEXT_AND_SPEECH = 3; + } + + string language = 1; + LanguageOption option = 2; +} + +message CachedProductState { + BaseProductState baseProductState = 1; + BackfillProgress backfillProgress = 2; + RepairProgress repairProgress = 3; + UpdateProgress updateProgress = 4; +} + +message BaseProductState { + bool installed = 1; + bool playable = 2; + bool updateComplete = 3; + bool backgroundDownloadAvailable = 4; + bool backgroundDownloadComplete = 5; + string currentVersion = 6; + string currentVersionStr = 7; + repeated BuildConfig installedBuildConfig = 8; + repeated BuildConfig backgroundDownloadBuildConfig = 9; + string decryptionKey = 10; + repeated string completedInstallActions = 11; + string tags = 17; +} + +message BuildConfig { + string region = 1; + string buildConfig = 2; +} + +message BackfillProgress { + double progress = 1; + bool backgrounddownload = 2; + bool paused = 3; + bool downloadLimit = 4; +} + +message RepairProgress { + double progress = 1; +} + +message UpdateProgress { + string lastDiscSetUsed = 1; + double progress = 2; + bool discIgnored = 3; + uint64 totalToDownload = 4; +} + +message ProductOperations { + enum Operation { + OP_UPDATE = 0; + OP_BACKFILL = 1; + OP_REPAIR = 2; + } + + Operation activeOperation = 1; + uint64 priority = 2; +} + +message ProductConfig { + string productCode = 1; + string metadataHash = 2; + string timestamp = 3; +} + +message ActiveProcess { + string processName = 1; + int32 pid = 2; + repeated string uri = 3; +} + +message InstallHandshake { + string product = 1; + string uid = 2; + UserSettings settings = 3; +} + +message DownloadSettings { + int32 downloadLimit = 1; + int32 backfillLimit = 2; +} + +message Database { + repeated ProductInstall productInstall = 1; + repeated InstallHandshake activeInstalls = 2; + repeated ActiveProcess activeProcesses = 3; + repeated ProductConfig productConfigs = 4; + DownloadSettings downloadSettings = 5; +} diff --git a/launchpad_app/lib/routes/forms.dart b/launchpad_app/lib/routes/forms.dart new file mode 100644 index 00000000..10dd8ecb --- /dev/null +++ b/launchpad_app/lib/routes/forms.dart @@ -0,0 +1,5 @@ +export '../screens/forms/auto_suggest_box.dart'; +export '../screens/forms/combobox.dart'; +export '../screens/forms/date_picker.dart'; +export '../screens/forms/text_box.dart'; +export '../screens/forms/time_picker.dart'; diff --git a/launchpad_app/lib/routes/inputs.dart b/launchpad_app/lib/routes/inputs.dart new file mode 100644 index 00000000..b46e7974 --- /dev/null +++ b/launchpad_app/lib/routes/inputs.dart @@ -0,0 +1,4 @@ +export '../screens/inputs/button.dart'; +export '../screens/inputs/checkbox.dart'; +export '../screens/inputs/slider.dart'; +export '../screens/inputs/toggle_switch.dart'; diff --git a/launchpad_app/lib/routes/navigation.dart b/launchpad_app/lib/routes/navigation.dart new file mode 100644 index 00000000..78cf5596 --- /dev/null +++ b/launchpad_app/lib/routes/navigation.dart @@ -0,0 +1,3 @@ +export '../screens/navigation/navigation_view.dart'; +export '../screens/navigation/tab_view.dart'; +export '../screens/navigation/tree_view.dart'; diff --git a/launchpad_app/lib/routes/surfaces.dart b/launchpad_app/lib/routes/surfaces.dart new file mode 100644 index 00000000..530d1cc2 --- /dev/null +++ b/launchpad_app/lib/routes/surfaces.dart @@ -0,0 +1,9 @@ +export '../screens/surface/acrylic.dart'; +export '../screens/surface/commandbars.dart'; +export '../screens/surface/content_dialog.dart'; +export '../screens/surface/expander.dart'; +export '../screens/surface/flyouts.dart'; +export '../screens/surface/info_bars.dart'; +export '../screens/surface/progress_indicators.dart'; +export '../screens/surface/tiles.dart'; +export '../screens/surface/tooltip.dart'; diff --git a/launchpad_app/lib/screens/forms/auto_suggest_box.dart b/launchpad_app/lib/screens/forms/auto_suggest_box.dart new file mode 100644 index 00000000..7e68eca5 --- /dev/null +++ b/launchpad_app/lib/screens/forms/auto_suggest_box.dart @@ -0,0 +1,392 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class AutoSuggestBoxPage extends StatefulHookConsumerWidget { + const AutoSuggestBoxPage({Key? key}) : super(key: key); + + @override + createState() => _AutoSuggestBoxPageState(); +} + +class _AutoSuggestBoxPageState extends ConsumerState + with PageMixin { + String? selectedCat; + Cat? selectedObjectCat; + var enabled = true; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: PageHeader( + title: const Text('AutoSuggestBox'), + commandBar: ToggleSwitch( + content: const Text('Disabled'), + checked: !enabled, + onChanged: (v) => setState(() => enabled = !v), + ), + ), + children: [ + const Text( + 'A text control that makes suggestions to users as they type. The app ' + 'is notified when text has been changed by the user and is responsible ' + 'for providing relevant suggestions for this control to display.', + ), + subtitle(content: const Text('A basic AutoSuggestBox')), + CardHighlight( + codeSnippet: ''' +String? selectedCat; + +AutoSuggestBox( + items: cats.map((cat) { + return AutoSuggestBoxItem( + value: cat, + label: cat, + onFocusChange: (focused) { + if (focused) { + debugPrint('Focused \$cat'); + } + } + ); + }).toList(), + onSelected: (item) { + setState(() => selected = item); + }, +), + +const cats = [ + 'Abyssinian', + 'Aegean', + 'American Bobtail', + 'American Curl', + ... +];''', + child: Row(children: [ + SizedBox( + width: 350.0, + child: AutoSuggestBox( + enabled: enabled, + items: cats + .map>( + (cat) => AutoSuggestBoxItem( + value: cat, + label: cat, + onFocusChange: (focused) { + if (focused) debugPrint('Focused $cat'); + }, + ), + ) + .toList(), + onSelected: (item) { + setState(() => selectedCat = item.value); + }, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 8.0), + child: Text(selectedCat ?? ''), + ), + ), + ]), + ), + const Text( + 'The control can be used with a custom value class. With this feature,' + ' AutoSuggestBox can be used as a replacement of a ComboBox.', + ), + subtitle( + content: const Text('A AutoSuggestBox with a custom type "Cat"')), + CardHighlight( + codeSnippet: ''' +class Cat { + final int id; + final String name; + final bool hasTag; + + const Cat(this.id, this.name, this.hasTag); +} + +Cat? selectedObjectCat; + +AutoSuggestBox( + items: objectCats + .map>( + (cat) => AutoSuggestBoxItem( + value: cat, + label: cat.name, + onFocusChange: (focused) { + if (focused) { + debugPrint('Focused #\${cat.id} - \${cat.name}'); + } + }, + ), + ) + .toList(), + onSelected: (item) { + setState(() => selectedObjectCat = item.value); + }, +), + +const objectCats = [ + Cat(1, 'Abyssinian', true), + Cat(2, 'Aegean', true), + Cat(3, 'American Bobtail', false), + Cat(4, 'American Curl', true), + Cat(5, 'American Ringtail', false), + Cat(6, 'American Shorthair', true), + ... +];''', + child: Row(children: [ + SizedBox( + width: 350.0, + child: AutoSuggestBox( + enabled: enabled, + items: objectCats + .map>( + (cat) => AutoSuggestBoxItem( + value: cat, + label: cat.name, + onFocusChange: (focused) { + if (focused) { + debugPrint('Focused $cat'); + } + }, + ), + ) + .toList(), + onSelected: (item) { + setState(() => selectedObjectCat = item.value); + }, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 8.0), + child: Text(selectedObjectCat != null + ? 'Cat #${selectedObjectCat!.id} "${selectedObjectCat!.name}" ${selectedObjectCat!.hasTag ? '[🏷 TAGGED]' : "[❌ NON TAGGED]"}' + : ''), + ), + ), + ]), + ), + ], + ); + } +} + +const cats = [ + 'Abyssinian', + 'Aegean', + 'American Bobtail', + 'American Curl', + 'American Ringtail', + 'American Shorthair', + 'American Wirehair', + 'Aphrodite Giant', + 'Arabian Mau', + 'Asian cat', + 'Asian Semi-longhair', + 'Australian Mist', + 'Balinese', + 'Bambino', + 'Bengal', + 'Birman', + 'Bombay', + 'Brazilian Shorthair', + 'British Longhair', + 'British Shorthair', + 'Burmese', + 'Burmilla', + 'California Spangled', + 'Chantilly-Tiffany', + 'Chartreux', + 'Chausie', + 'Colorpoint Shorthair', + 'Cornish Rex', + 'Cymric', + 'Cyprus', + 'Devon Rex', + 'Donskoy', + 'Dragon Li', + 'Dwelf', + 'Egyptian Mau', + 'European Shorthair', + 'Exotic Shorthair', + 'Foldex', + 'German Rex', + 'Havana Brown', + 'Highlander', + 'Himalayan', + 'Japanese Bobtail', + 'Javanese', + 'Kanaani', + 'Khao Manee', + 'Kinkalow', + 'Korat', + 'Korean Bobtail', + 'Korn Ja', + 'Kurilian Bobtail', + 'Lambkin', + 'LaPerm', + 'Lykoi', + 'Maine Coon', + 'Manx', + 'Mekong Bobtail', + 'Minskin', + 'Napoleon', + 'Munchkin', + 'Nebelung', + 'Norwegian Forest Cat', + 'Ocicat', + 'Ojos Azules', + 'Oregon Rex', + 'Oriental Bicolor', + 'Oriental Longhair', + 'Oriental Shorthair', + 'Persian (modern)', + 'Persian (traditional)', + 'Peterbald', + 'Pixie-bob', + 'Ragamuffin', + 'Ragdoll', + 'Raas', + 'Russian Blue', + 'Russian White', + 'Sam Sawet', + 'Savannah', + 'Scottish Fold', + 'Selkirk Rex', + 'Serengeti', + 'Serrade Petit', + 'Siamese', + 'Siberian or´Siberian Forest Cat', + 'Singapura', + 'Snowshoe', + 'Sokoke', + 'Somali', + 'Sphynx', + 'Suphalak', + 'Thai', + 'Thai Lilac', + 'Tonkinese', + 'Toyger', + 'Turkish Angora', + 'Turkish Van', + 'Turkish Vankedisi', + 'Ukrainian Levkoy', + 'Wila Krungthep', + 'York Chocolate', +]; + +const objectCats = [ + Cat(1, 'Abyssinian', true), + Cat(2, 'Aegean', true), + Cat(3, 'American Bobtail', false), + Cat(4, 'American Curl', true), + Cat(5, 'American Ringtail', false), + Cat(6, 'American Shorthair', true), + Cat(7, 'American Wirehair', false), + Cat(8, 'Aphrodite Giant', true), + Cat(9, 'Arabian Mau', false), + Cat(10, 'Asian cat', true), + Cat(11, 'Asian Semi-longhair', false), + Cat(12, 'Australian Mist', false), + Cat(13, 'Balinese', false), + Cat(14, 'Bambino', false), + Cat(15, 'Bengal', true), + Cat(16, 'Birman', false), + Cat(17, 'Bombay', true), + Cat(18, 'Brazilian Shorthair', false), + Cat(19, 'British Longhair', true), + Cat(20, 'British Shorthair', false), + Cat(21, 'Burmese', true), + Cat(22, 'Burmilla', false), + Cat(23, 'California Spangled', false), + Cat(24, 'Chantilly-Tiffany', true), + Cat(25, 'Chartreux', true), + Cat(26, 'Chausie', false), + Cat(27, 'Colorpoint Shorthair', true), + Cat(28, 'Cornish Rex', false), + Cat(29, 'Cymric', false), + Cat(30, 'Cyprus', false), + Cat(31, 'Devon Rex', false), + Cat(32, 'Donskoy', false), + Cat(33, 'Dragon Li', false), + Cat(34, 'Dwelf', false), + Cat(35, 'Egyptian Mau', true), + Cat(36, 'European Shorthair', false), + Cat(37, 'Exotic Shorthair', false), + Cat(38, 'Foldex', false), + Cat(39, 'German Rex', true), + Cat(40, 'Havana Brown', true), + Cat(41, 'Highlander', true), + Cat(42, 'Himalayan', true), + Cat(43, 'Japanese Bobtail', true), + Cat(44, 'Javanese', true), + Cat(45, 'Kanaani', true), + Cat(46, 'Khao Manee', true), + Cat(47, 'Kinkalow', true), + Cat(48, 'Korat', false), + Cat(49, 'Korean Bobtail', true), + Cat(50, 'Korn Ja', false), + Cat(51, 'Kurilian Bobtail', true), + Cat(52, 'Lambkin', false), + Cat(53, 'LaPerm', true), + Cat(54, 'Lykoi', true), + Cat(55, 'Maine Coon', false), + Cat(56, 'Manx', true), + Cat(57, 'Mekong Bobtail', true), + Cat(58, 'Minskin', false), + Cat(59, 'Napoleon', true), + Cat(60, 'Munchkin', false), + Cat(61, 'Nebelung', false), + Cat(62, 'Norwegian Forest Cat', false), + Cat(63, 'Ocicat', true), + Cat(64, 'Ojos Azules', true), + Cat(65, 'Oregon Rex', false), + Cat(66, 'Oriental Bicolor', true), + Cat(67, 'Oriental Longhair', false), + Cat(68, 'Oriental Shorthair', true), + Cat(69, 'Persian (modern)', true), + Cat(70, 'Persian (traditional)', true), + Cat(71, 'Peterbald', false), + Cat(72, 'Pixie-bob', false), + Cat(73, 'Ragamuffin', true), + Cat(74, 'Ragdoll', true), + Cat(75, 'Raas', true), + Cat(76, 'Russian Blue', true), + Cat(77, 'Russian White', true), + Cat(78, 'Sam Sawet', false), + Cat(79, 'Savannah', false), + Cat(80, 'Scottish Fold', false), + Cat(81, 'Selkirk Rex', false), + Cat(82, 'Serengeti', false), + Cat(83, 'Serrade Petit', false), + Cat(84, 'Siamese', false), + Cat(85, 'Siberian or´Siberian Forest Cat', false), + Cat(86, 'Singapura', true), + Cat(87, 'Snowshoe', false), + Cat(88, 'Sokoke', false), + Cat(89, 'Somali', false), + Cat(90, 'Sphynx', true), + Cat(91, 'Suphalak', true), + Cat(92, 'Thai', false), + Cat(93, 'Thai Lilac', false), + Cat(94, 'Tonkinese', false), + Cat(95, 'Toyger', true), + Cat(96, 'Turkish Angora', true), + Cat(97, 'Turkish Van', false), + Cat(98, 'Turkish Vankedisi', false), + Cat(99, 'Ukrainian Levkoy', false), + Cat(100, 'Wila Krungthep', true), + Cat(101, 'York Chocolate', true), +]; + +class Cat { + final int id; + final String name; + final bool hasTag; + + const Cat(this.id, this.name, this.hasTag); +} diff --git a/launchpad_app/lib/screens/forms/combobox.dart b/launchpad_app/lib/screens/forms/combobox.dart new file mode 100644 index 00000000..3398e67d --- /dev/null +++ b/launchpad_app/lib/screens/forms/combobox.dart @@ -0,0 +1,418 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import 'auto_suggest_box.dart'; + +class ComboBoxPage extends StatefulHookConsumerWidget { + const ComboBoxPage({Key? key}) : super(key: key); + + @override + createState() => _ComboBoxPageState(); +} + +class _ComboBoxPageState extends ConsumerState with PageMixin { + String? selectedColor = 'Green'; + String? selectedCat; + var fontSize = 20.0; + var disabled = false; + final comboboxKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return ScaffoldPage.scrollable( + header: PageHeader( + title: const Text('ComboBox'), + commandBar: ToggleSwitch( + checked: disabled, + onChanged: (v) { + setState(() => disabled = v); + }, + content: const Text('Disabled'), + ), + ), + children: [ + const Text( + 'Use a combo box (also known as a drop-down list) to present a list of ' + 'items that a user can select from. A combo box starts in a compact ' + 'state and expands to show a list of selectable items.\n\n' + 'When the combo box is closed, it either displays the current selection ' + 'or is empty if there is no selected item. When the user expands the ' + 'combo box, it displays the list of selectable items.\n\n' + 'Use a ComboBox when you need to conserve on-screen space and when ' + 'users select only one option at a time. A ComboBox shows only the ' + 'currently selected item.', + ), + subtitle( + content: const Text( + 'A ComboBox with items defined inline and its width set', + ), + ), + CardHighlight( + codeSnippet: + '''// Green by default +Color selectedColor = 'Green'; + +ComboBox( + value: selectedColor, + items: colors.entries.map((e) { + return ComboBoxItem( + child: Text(e.key), + value: e.key, + ); + }).toList(), + onChanged: disabled ? null : (color) => setState(() => selectedColor = color), +),''', + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + ComboBox( + isExpanded: false, + popupColor: colors[selectedColor], + value: selectedColor, + items: colors.entries.map((e) { + return ComboBoxItem( + value: e.key, + child: Text(e.key), + ); + }).toList(), + onChanged: disabled + ? null + : (color) { + setState(() => selectedColor = color); + }, + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 8.0), + height: 30, + width: 100, + color: colors[selectedColor], + ), + ]), + ), + subtitle( + content: const Text('A ComboBox with a long list of items'), + ), + CardHighlight( + codeSnippet: + '''var cats= [...]; + +ComboBox( + value: selectedCat, + items: cats.map>((e) { + return ComboBoxItem( + child: Text(e), + value: e, + ); + }).toList(), + onChanged: disabled + ? null + : (color) { + setState(() => selectedCat = color); + }, + placeholder: const Text('Select a cat breed'), +),''', + child: Wrap(spacing: 10.0, runSpacing: 10.0, children: [ + ComboBox( + isExpanded: false, + value: selectedCat, + items: cats.map>((e) { + return ComboBoxItem( + value: e, + child: Text(e), + ); + }).toList(), + onChanged: disabled + ? null + : (color) { + setState(() => selectedCat = color); + }, + placeholder: const Text('Select a cat breed'), + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 8.0), + height: 30, + child: Text(selectedCat ?? ''), + ), + ]), + ), + subtitle(content: const Text('An editable ComboBox')), + description( + content: const Text( + 'By default, a combo box lets the user select from a pre-defined ' + 'list of options. However, there are cases where the list contains ' + 'only a subset of valid values, and the user should be able to enter ' + 'other values that aren\'t listed. To support this, you can make the' + ' combo box editable.', + ), + ), + CardHighlight( + codeSnippet: + '''static const fontSizes = [ + 8, + 9, + ..., +]; + +double fontSize = 20.0; + +EditableComboBox( + value: fontSize.toInt(), + items: cats.map>((e) { + return ComboBoxItem( + child: Text('\$e'), + value: e.toInt(), + ); + }).toList(), + onChanged: disabled + ? null + : (size) { + setState(() => fontSize = size?.toDouble() ?? fontSize); + }, + placeholder: const Text('Select a font size'), + onFieldSubmitted: (String text) { + // When the value in the text field is changed, this callback is called + // It's up to the developer to handle the text change + + try { + final newSize = int.parse(text); + + if (newSize < 8 || newSize > 100) { + throw UnsupportedError( + 'The font size must be a number between 8 and 100.', + ); + } + + setState(() => fontSize = newSize.toDouble()); + } catch (e) { + showDialog( + context: context, + builder: (context) { + return ContentDialog( + content: const Text( + 'The font size must be a number between 8 and 100.', + ), + actions: [ + FilledButton( + child: const Text('Close'), + onPressed: Navigator.of(context).pop, + ), + ], + ); + }, + ); + } + return fontSize.toInt().toString(); + }, +),''', + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + width: 150, + child: EditableComboBox( + isExpanded: false, + value: fontSize.toInt(), + items: fontSizes.map>((fontSize) { + return ComboBoxItem( + value: fontSize.toInt(), + child: Text('${fontSize.toInt()}'), + ); + }).toList(), + onChanged: disabled + ? null + : (size) { + setState( + () => fontSize = (size ?? fontSize).toDouble()); + }, + placeholder: const Text('Font size'), + onFieldSubmitted: (text) { + try { + final newSize = int.parse(text); + + if (newSize < 8 || newSize > 100) { + throw UnsupportedError( + 'The font size must be a number between 8 and 100.', + ); + } + + setState(() => fontSize = newSize.toDouble()); + } catch (e) { + showDialog( + context: context, + builder: (context) { + return ContentDialog( + content: const Text( + 'The font size must be a number between 8 and 100.', + ), + actions: [ + FilledButton( + onPressed: Navigator.of(context).pop, + child: const Text('Close'), + ), + ], + ); + }, + ); + } + return '${fontSize.toInt()}'; + }, + ), + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 8.0), + constraints: const BoxConstraints(minHeight: 50.0), + child: Text( + 'You can set the font size for this text', + style: TextStyle(fontSize: fontSize), + ), + ), + ]), + ), + subtitle(content: const Text('A ComboBox Form Field')), + CardHighlight( + codeSnippet: + '''var colors = { ... }; +Color selectedColor = 'Green'; + +Form( + autovalidateMode: AutovalidateMode.always, + child: ComboboxFormField( + value: selectedColor, + items: colors.entries.map((e) { + return ComboBoxItem( + child: Text(e.key), + value: e.key, + ); + }).toList(), + onChanged: disabled ? null : (color) => setState(() => selectedColor = color), + validator: (text) { + if (text == null || text.isEmpty) { + return 'Please provide a value'; + } + + final acceptedValues = colors.keys.skip(4); + + if (!acceptedValues.contains(text)) { + return '\$text is not a valid value today'; + } + + return null; + }, + ), +),''', + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Form( + autovalidateMode: AutovalidateMode.always, + child: ComboboxFormField( + popupColor: colors[selectedColor], + value: selectedColor, + items: colors.entries.map((e) { + return ComboBoxItem( + value: e.key, + child: Text(e.key), + ); + }).toList(), + onChanged: disabled + ? null + : (color) => setState(() => selectedColor = color), + validator: (text) { + if (text == null || text.isEmpty) { + return 'Please provide a value'; + } + + final acceptedValues = colors.keys.skip(4); + + if (!acceptedValues.contains(text)) { + return '$text is not a valid value today'; + } + + return null; + }, + ), + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 8.0), + height: 30, + width: 100, + color: colors[selectedColor], + ), + ]), + ), + subtitle(content: const Text('Open popup programatically')), + CardHighlight( + codeSnippet: + '''// A GlobalKey is used to access the current +// state of the combo box. With it, it's possible to call .openPopup() and .closePopup() +// which will open and close the popup, respectively +// +// It is possible to use the key with ComboBox and EditableComboBox +final comboboxKey = GlobalKey(); + +ComboBox( + key: comboboxKey, + // define the other properties here + ... +), + +Button( + child: const Text('Open popup'), + onPressed: () => comboboxKey.currentState?.openPopup(), +),''', + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + ComboBox( + key: comboboxKey, + isExpanded: false, + popupColor: colors[selectedColor], + value: selectedColor, + items: colors.entries.map((e) { + return ComboBoxItem( + value: e.key, + child: Text(e.key), + ); + }).toList(), + onChanged: disabled + ? null + : (color) => setState(() => selectedColor = color), + ), + const SizedBox(width: 8.0), + Button( + onPressed: + disabled ? null : () => comboboxKey.currentState?.openPopup(), + child: const Text('Open popup'), + ), + ]), + ), + ], + ); + } + + var colors = { + 'Blue': Colors.blue, + 'Green': Colors.green, + 'Red': Colors.red, + 'Yellow': Colors.yellow, + 'Grey': Colors.grey, + 'Magenta': Colors.magenta, + 'Orange': Colors.orange, + 'Purple': Colors.purple, + 'Teal': Colors.teal, + }; + + static const fontSizes = [ + 8, + 9, + 10, + 11, + 12, + 14, + 16, + 18, + 20, + 24, + 28, + 36, + 48, + 72, + ]; +} diff --git a/launchpad_app/lib/screens/forms/date_picker.dart b/launchpad_app/lib/screens/forms/date_picker.dart new file mode 100644 index 00000000..67deede0 --- /dev/null +++ b/launchpad_app/lib/screens/forms/date_picker.dart @@ -0,0 +1,72 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class DatePickerPage extends StatefulHookConsumerWidget { + const DatePickerPage({Key? key}) : super(key: key); + + @override + createState() => _DatePickerPageState(); +} + +class _DatePickerPageState extends ConsumerState + with PageMixin { + DateTime? simpleTime; + DateTime? hiddenTime; + + @override + Widget build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('DatePicker')), + children: [ + const Text( + 'Use a DatePicker to let users set a date in your app, for example to ' + 'schedule an appointment. The DatePicker displays three controls for ' + 'month, date, and year. These controls are easy to use with touch or ' + 'mouse, and they can be styled and configured in several different ways.' + '\n\nThe entry point displays the chosen date, and when the user ' + 'selects the entry point, a picker surface expands vertically from the ' + 'middle for the user to make a selection. The date picker overlays ' + 'other UI; it doesn\'t push other UI out of the way.', + ), + subtitle(content: const Text('A simple DatePicker with a header')), + CardHighlight( + codeSnippet: '''DateTime? selected; + +DatePicker( + header: 'Pick a date', + selected: selected, + onChanged: (time) => setState(() => selected = time), +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: DatePicker( + header: 'Pick a date', + selected: simpleTime, + onChanged: (time) => setState(() => simpleTime = time), + ), + ), + ), + subtitle(content: const Text('A DatePicker with year hidden')), + CardHighlight( + codeSnippet: '''DateTime? selected; + +DatePicker( + selected: selected, + onChanged: (time) => setState(() => selected = time), + showYear: false, +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: DatePicker( + selected: hiddenTime, + onChanged: (v) => setState(() => hiddenTime = v), + showYear: false, + ), + ), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/forms/text_box.dart b/launchpad_app/lib/screens/forms/text_box.dart new file mode 100644 index 00000000..ebbae850 --- /dev/null +++ b/launchpad_app/lib/screens/forms/text_box.dart @@ -0,0 +1,90 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class TextBoxPage extends ScrollablePage { + TextBoxPage({super.key}); + + @override + buildHeader(BuildContext context, WidgetRef ref) { + return const PageHeader(title: Text('TextBox')); + } + + @override + buildScrollable(BuildContext context, WidgetRef ref) { + return [ + const Text( + 'The TextBox control lets a user type text into an app. It\'s typically ' + 'used to capture a single line of text, but can be configured to capture ' + 'multiple lines of text. The text displays on the screen in a simple, ' + 'uniform, plaintext format.\n\n' + 'TextBox has a number of features that can simplify text entry. It comes ' + 'with a familiar, built-in context menu with support for copying and ' + 'pasting text. The "clear all" button lets a user quickly delete all ' + 'text that has been entered. It also has spell checking capabilities ' + 'built in and enabled by default.', + ), + subtitle(content: const Text('A simple TextBox')), + CardHighlight( + codeSnippet: '''TextBox()''', + child: Row(children: const [ + Expanded(child: TextBox()), + SizedBox(width: 10.0), + Expanded(child: TextBox(enabled: false, placeholder: 'Disabled')) + ]), + ), + subtitle( + content: const Text('A TextBox with a header and placeholder text'), + ), + const CardHighlight( + codeSnippet: '''TextBox( + header: 'Enter your name:', + placeholder: 'Name', + expands: false, +),''', + child: TextBox( + header: 'Enter your name:', + placeholder: 'Name', + expands: false, + ), + ), + subtitle( + content: const Text('A read-only TextBox with various properties set'), + ), + const CardHighlight( + codeSnippet: '''TextBox( + readOnly: true, + placeholder: 'I am super excited to be here', + style: TextStyle( + fontFamily: 'Arial, + fontSize: 24.0, + letterSpacing: 8, + color: Color(0xFF5178BE), + fontStyle: FontStyle.italic, + ), +),''', + child: TextBox( + readOnly: true, + placeholder: 'I am super excited to be here!', + style: TextStyle( + fontFamily: 'Arial', + fontSize: 24.0, + letterSpacing: 8, + color: Color(0xFF5178BE), + fontStyle: FontStyle.italic, + ), + ), + ), + subtitle(content: const Text('A multi-line TextBox')), + const CardHighlight( + codeSnippet: '''TextBox( + maxLines: null, +),''', + child: TextBox( + maxLines: null, + ), + ), + ]; + } +} diff --git a/launchpad_app/lib/screens/forms/time_picker.dart b/launchpad_app/lib/screens/forms/time_picker.dart new file mode 100644 index 00000000..413b4105 --- /dev/null +++ b/launchpad_app/lib/screens/forms/time_picker.dart @@ -0,0 +1,101 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class TimePickerPage extends StatefulHookConsumerWidget { + const TimePickerPage({Key? key}) : super(key: key); + + @override + createState() => _TimePickerPageState(); +} + +class _TimePickerPageState extends ConsumerState + with PageMixin { + DateTime? simpleTime; + DateTime? arrivalTime; + DateTime? hhTime; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('TimePicker')), + children: [ + const Text( + 'Use a TimePicker to let users set a time in your app, for example to ' + 'set a reminder. The TimePicker displays three controls for hour, ' + 'minute, and AM/PM. These controls are easy to use with touch or mouse, ' + 'and they can be styled and configured in several different ways.\n\n' + 'The entry point displays the chosen time, and when the user selects ' + 'the entry point, a picker surface expands vertically from the middle ' + 'for the user to make a selection. The time picker overlays other UI; ' + 'it doesn\'t push other UI out of the way.', + ), + subtitle(content: const Text('A simple TimePicker')), + CardHighlight( + codeSnippet: '''DateTime? selected; + +TimePicker( + selected: selected, + onChanged: (time) => setState(() => selected = time), +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: TimePicker( + selected: simpleTime, + onChanged: (time) => setState(() => simpleTime = time), + ), + ), + ), + subtitle( + content: const Text( + 'A TimePicker with a header and minute increments specified', + ), + ), + CardHighlight( + codeSnippet: '''DateTime? selected; + +TimePicker( + selected: selected, + onChanged: (time) => setState(() => selected = time), + header: 'Arrival time', + minuteIncrement: 15, +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: TimePicker( + header: 'Arrival time', + selected: arrivalTime, + onChanged: (time) => setState(() => arrivalTime = time), + minuteIncrement: 15, + ), + ), + ), + subtitle( + content: const Text( + 'A TimePicker using a 24-hour clock', + ), + ), + CardHighlight( + codeSnippet: '''DateTime? selected; + +TimePicker( + selected: selected, + onChanged: (time) => setState(() => selected = time), + header: '24 hour clock', + hourFormat: HourFormat.HH, +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: TimePicker( + header: '24 hour clock', + selected: hhTime, + onChanged: (v) => setState(() => hhTime = v), + hourFormat: HourFormat.HH, + ), + ), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/inputs/button.dart b/launchpad_app/lib/screens/inputs/button.dart new file mode 100644 index 00000000..5a6d9a00 --- /dev/null +++ b/launchpad_app/lib/screens/inputs/button.dart @@ -0,0 +1,322 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../src/common_widgets/card_highlight.dart'; + +class ButtonPage extends StatefulHookConsumerWidget { + const ButtonPage({Key? key}) : super(key: key); + + @override + createState() => _ButtonPageState(); +} + +class _ButtonPageState extends ConsumerState with PageMixin { + var simpleDisabled = false; + var filledDisabled = false; + var iconDisabled = false; + var toggleDisabled = false; + var toggleState = false; + var splitButtonDisabled = false; + var radioButtonDisabled = false; + var radioButtonSelected = -1; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Button')), + children: [ + const Text( + 'The Button control provides a Click event to respond to user input from a touch, mouse, keyboard, stylus, or other input device. You can put different kinds of content in a button, such as text or an image, or you can restyle a button to give it a new look.', + ), + subtitle(content: const Text('A simple button with text content')), + description( + content: const Text('A button that initiates an immediate action.'), + ), + CardHighlight( + codeSnippet: '''Button( + child: const Text('Standard Button'), + onPressed: disabled ? null : () => debugPrint('pressed button'), +)''', + child: Row(children: [ + Button( + onPressed: simpleDisabled ? null : () {}, + child: const Text('Standard Button'), + ), + const Spacer(), + ToggleSwitch( + checked: simpleDisabled, + onChanged: (v) { + setState(() { + simpleDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle(content: const Text('Accent Style applied to Button')), + CardHighlight( + codeSnippet: '''FilledButton( + child: const Text('Filled Button'), + onPressed: disabled ? null : () => debugPrint('pressed button'), +)''', + child: Row(children: [ + FilledButton( + onPressed: filledDisabled ? null : () {}, + child: const Text('Filled Button'), + ), + const Spacer(), + ToggleSwitch( + checked: filledDisabled, + onChanged: (v) { + setState(() { + filledDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle( + content: const Text('A Button with graphical content (IconButton)'), + ), + CardHighlight( + codeSnippet: '''IconButton( + icon: const Icon(FluentIcons.graph_symbol, size: 24.0), + onPressed: disabled ? null : () => debugPrint('pressed button'), +)''', + child: Row(children: [ + IconButton( + icon: const Icon(FluentIcons.graph_symbol, size: 24.0), + onPressed: iconDisabled ? null : () {}, + ), + const Spacer(), + ToggleSwitch( + checked: iconDisabled, + onChanged: (v) { + setState(() { + iconDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle( + content: const Text('A simple ToggleButton with text content')), + description( + content: const Text( + 'A ToggleButton looks like a Button, but works like a CheckBox. It ' + 'typically has two states, checked (on) or unchecked (off).', + ), + ), + CardHighlight( + codeSnippet: '''bool checked = false; + +ToggleButton( + child: const Text('Toggle Button'), + checked: checked, + onPressed: disabled ? null : (v) => setState(() => checked = v), +)''', + child: Row(children: [ + ToggleButton( + checked: toggleState, + onChanged: toggleDisabled + ? null + : (v) { + setState(() { + toggleState = v; + }); + }, + child: const Text('Toggle Button'), + ), + const Spacer(), + ToggleSwitch( + checked: toggleDisabled, + onChanged: (v) { + setState(() { + toggleDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle(content: const Text('DropDownButton')), + const Text( + 'A control that drops down a flyout of choices from which one can be chosen', + ), + CardHighlight( + codeSnippet: '''DropDownButton( + title: Text('Email'), + items: [ + MenuFlyoutItem(text: const Text('Send'), onPressed: () {}), + MenuFlyoutItem(text: const Text('Reply'), onPressed: () {}), + MenuFlyoutItem(text: const Text('Reply all'), onPressed: () {}), + ], +)''', + child: Row(children: [ + DropDownButton( + title: Text('Email'), + items: [ + MenuFlyoutItem(text: const Text('Send'), onPressed: () {}), + MenuFlyoutItem(text: const Text('Reply'), onPressed: () {}), + MenuFlyoutItem(text: const Text('Reply all'), onPressed: () {}), + ], + ), + SizedBox(width: 10.0), + DropDownButton( + title: Icon(FluentIcons.edit_mail, size: 22.0), + items: [ + MenuFlyoutItem( + leading: Icon(FluentIcons.send), + text: const Text('Send'), + onPressed: () {}, + ), + MenuFlyoutItem( + leading: Icon(FluentIcons.reply), + text: const Text('Reply'), + onPressed: () {}, + ), + MenuFlyoutItem( + leading: Icon(FluentIcons.reply_all), + text: const Text('Reply all'), + onPressed: () {}, + ), + ], + ), + ]), + ), + subtitle(content: const Text('SplitButton')), + description( + content: const Text( + 'Represents a button with two parts that can be invoked separately. ' + 'One part behaves like a standard button and the other part invokes ' + 'a flyout.', + ), + ), + CardHighlight( + codeSnippet: '''SplitButtonBar( + buttons: [ + Button( + child: Container( + height: 24.0, + width: 24.0, + color: Colors.green, + ), + onPressed: () {}, + ), + IconButton( + icon: const Icon(FluentIcons.chevron_down, size: 10.0), + onPressed: () {}, + ), + ], +)''', + child: Row(children: [ + SplitButtonBar( + buttons: [ + Button( + onPressed: splitButtonDisabled ? null : () {}, + child: Container( + decoration: BoxDecoration( + color: splitButtonDisabled + ? FluentTheme.of(context).accentColor.darker + : FluentTheme.of(context).accentColor, + borderRadius: const BorderRadiusDirectional.horizontal( + start: Radius.circular(4.0), + ), + ), + height: 24, + width: 24, + ), + ), + IconButton( + icon: const SizedBox( + // height: splitButtonHeight, + child: Icon(FluentIcons.chevron_down, size: 10.0), + ), + onPressed: splitButtonDisabled ? null : () {}, + ), + ], + ), + const Spacer(), + ToggleSwitch( + checked: splitButtonDisabled, + onChanged: (v) { + setState(() { + splitButtonDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle(content: const Text('RadioButton')), + description( + content: const Text( + 'Radio buttons, also called option buttons, let users select one option ' + 'from a collection of two or more mutually exclusive, but related, ' + 'options. Radio buttons are always used in groups, and each option is ' + 'represented by one radio button in the group.', + ), + ), + CardHighlight( + codeSnippet: '''int? selected; + +Column( + children: List.generate(3, (index) { + return RadioButton( + checked: selected == index, + onChanged: (checked) { + if (checked) { + setState(() => selected = index); + } + } + ); + }), +)''', + child: Row(children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: List.generate( + 3, + (index) { + return Padding( + padding: EdgeInsetsDirectional.only( + bottom: index == 2 ? 0.0 : 14.0), + child: RadioButton( + checked: radioButtonSelected == index, + onChanged: radioButtonDisabled + ? null + : (v) { + if (v) { + setState(() { + radioButtonSelected = index; + }); + } + }, + content: Text('RadioButton ${index + 1}'), + ), + ); + }, + ), + ), + const Spacer(), + ToggleSwitch( + checked: radioButtonDisabled, + onChanged: (v) { + setState(() { + radioButtonDisabled = v; + }); + }, + content: const Text('Disabled'), + ) + ]), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/inputs/checkbox.dart b/launchpad_app/lib/screens/inputs/checkbox.dart new file mode 100644 index 00000000..39737aa9 --- /dev/null +++ b/launchpad_app/lib/screens/inputs/checkbox.dart @@ -0,0 +1,131 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class CheckBoxPage extends StatefulHookConsumerWidget { + const CheckBoxPage({Key? key}) : super(key: key); + + @override + createState() => _CheckBoxPageState(); +} + +class _CheckBoxPageState extends ConsumerState with PageMixin { + var firstChecked = false; + var firstDisabled = false; + bool? secondChecked = false; + var secondDisabled = false; + var iconDisabled = false; + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Checkbox')), + children: [ + const Text( + 'CheckBox controls let the user select a combination of binary options. In contrast, RadioButton controls allow the user to select from mutually exclusive options. The indeterminate state is used to indicate that an option is set for some, but not all, child options. Don\'t allow users to set an indeterminate state directly to indicate a third option.', + ), + subtitle(content: const Text('A 2-state Checkbox')), + CardHighlight( + codeSnippet: '''bool checked = false; + +Checkbox( + checked: checked, + onPressed: disabled ? null : (v) => setState(() => checked = v), +)''', + child: Row(children: [ + Checkbox( + checked: firstChecked, + onChanged: firstDisabled + ? null + : (v) => setState(() => firstChecked = v!), + content: const Text('Two-state Checkbox'), + ), + const Spacer(), + ToggleSwitch( + checked: firstDisabled, + onChanged: (v) { + setState(() { + firstDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle(content: const Text('A 3-state Checkbox')), + CardHighlight( + codeSnippet: '''bool checked = false; + +Checkbox( + checked: checked, + onPressed: disabled ? null : (v) { + setState(() { + // if v (the new value) is true, then true + // if v is false, then null (third state) + // if v is null (was third state before), then false + // otherwise (just to be safe), it's true + checked = (v == true + ? true + : v == false + ? null + : v == null + ? false + : true); + }); + }, +)''', + child: Row(children: [ + Checkbox( + checked: secondChecked, + // checked: null, + onChanged: secondDisabled + ? null + : (v) { + setState(() { + secondChecked = v == true + ? true + : v == false + ? null + : v == null + ? false + : true; + }); + }, + content: const Text('Three-state Checkbox'), + ), + const Spacer(), + ToggleSwitch( + checked: secondDisabled, + onChanged: (v) { + setState(() { + secondDisabled = v; + }); + }, + content: const Text('Disabled'), + ), + ]), + ), + subtitle( + content: const Text('Using a 3-state Checkbox (TreeView)'), + ), + Card( + child: TreeView( + items: [ + TreeViewItem( + content: const Text('Select all'), + children: treeViewItems, + ), + ], + selectionMode: TreeViewSelectionMode.multiple, + ), + ), + ], + ); + } + + final treeViewItems = [ + TreeViewItem(content: const Text('Option 1')), + TreeViewItem(content: const Text('Option 2')), + TreeViewItem(content: const Text('Option 3')), + ]; +} diff --git a/launchpad_app/lib/screens/inputs/slider.dart b/launchpad_app/lib/screens/inputs/slider.dart new file mode 100644 index 00000000..b9250199 --- /dev/null +++ b/launchpad_app/lib/screens/inputs/slider.dart @@ -0,0 +1,99 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class SliderPage extends StatefulHookConsumerWidget { + const SliderPage({Key? key}) : super(key: key); + + @override + createState() => _SliderPageState(); +} + +class _SliderPageState extends ConsumerState with PageMixin { + var disabled = false; + var firstValue = 23.0; + var verticalValue = 50.0; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: PageHeader( + title: const Text('Slider'), + commandBar: ToggleSwitch( + checked: disabled, + onChanged: (v) => setState(() => disabled = v), + content: const Text('Disabled'), + ), + ), + children: [ + const Text( + 'Use a Slider when you want your users to be able to set defined, ' + 'contiguous values (such as volume or brightness) or a range of discrete ' + 'values (such as screen resolution settings).\n\n' + 'A slider is a good choice when you know that users think of the value ' + 'as a relative quantity, not a numeric value. For example, users think ' + 'about setting their audio volume to low or medium—not about setting ' + 'the value to 2 or 5.', + ), + subtitle(content: const Text('A simple Slider')), + CardHighlight( + codeSnippet: '''double value = 0; + +Slider( + label: '\${value.toInt()}', + value: value, + onChanged: disabled ? null : (v) => setState(() => value = v), +), +''', + child: Row(children: [ + Slider( + label: '${firstValue.toInt()}', + value: firstValue, + onChanged: disabled + ? null + : (v) { + setState(() => firstValue = v); + }, + ), + const Spacer(), + Text('Output:\n${firstValue.toInt()}'), + ]), + ), + subtitle(content: const Text('A vertical slider')), + description( + content: const Text( + '''You can orient your slider horizontally or vertically. Use these guidelines to determine which layout to use. + + * Use a natural orientation. For example, if the slider represents a real-world value that is normally shown vertically (such as temperature), use a vertical orientation. + * If the control is used to seek within media, like in a video app, use a horizontal orientation. + * When using a slider in page that can be panned in one direction (horizontally or vertically), use a different orientation for the slider than the panning direction. Otherwise, users might swipe the slider and change its value accidentally when they try to pan the page. + * If you're still not sure which orientation to use, use the one that best fits your page layout.''', + ), + ), + CardHighlight( + codeSnippet: '''double value = 0; + +Slider( + vertical: true, + label: '\${value.toInt()}', + value: value, + onChanged: disabled ? null : (v) => setState(() => value = v), +), +''', + child: Row(children: [ + Slider( + vertical: true, + label: '${verticalValue.toInt()}', + value: verticalValue, + onChanged: + disabled ? null : (v) => setState(() => verticalValue = v), + ), + const Spacer(), + Text('Output:\n${verticalValue.toInt()}'), + ]), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/inputs/toggle_switch.dart b/launchpad_app/lib/screens/inputs/toggle_switch.dart new file mode 100644 index 00000000..9660d7a4 --- /dev/null +++ b/launchpad_app/lib/screens/inputs/toggle_switch.dart @@ -0,0 +1,86 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; + +class ToggleSwitchPage extends StatefulHookConsumerWidget { + const ToggleSwitchPage({Key? key}) : super(key: key); + + @override + createState() => _ToggleSwitchPageState(); +} + +class _ToggleSwitchPageState extends ConsumerState + with PageMixin { + var disabled = false; + var firstValue = false; + var secondValue = true; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: PageHeader( + title: const Text('ToggleSwitch'), + commandBar: ToggleSwitch( + checked: disabled, + onChanged: (v) => setState(() => disabled = v), + content: const Text('Disabled'), + ), + ), + children: [ + const Text( + 'The toggle switch represents a physical switch that allows users to ' + 'turn things on or off, like a light switch. Use toggle switch controls ' + 'to present users with two mutually exclusive options (such as on/off), ' + 'where choosing an option provides immediate results.', + ), + subtitle(content: const Text('A simple ToggleSwitch')), + CardHighlight( + codeSnippet: '''bool checked = false; + +ToggleSwitch( + checked: checked, + onPressed: disabled ? null : (v) => setState(() => checked = v), +)''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: ToggleSwitch( + checked: firstValue, + onChanged: + disabled ? null : (v) => setState(() => firstValue = v), + content: Text(firstValue ? 'On' : 'Off'), + ), + ), + ), + subtitle( + content: const Text('A ToggleSwitch with custom header and content'), + ), + CardHighlight( + codeSnippet: '''bool checked = false; + +ToggleSwitch( + checked: checked, + onPressed: disabled ? null : (v) => setState(() => checked = v), + content: Text(checked ? 'Working' : 'Do work'), +)''', + child: Row(children: [ + InfoLabel( + label: 'Header', + child: ToggleSwitch( + checked: secondValue, + onChanged: + disabled ? null : (v) => setState(() => secondValue = v), + content: Text(secondValue ? 'Working' : 'Do work'), + ), + ), + if (secondValue) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 8.0), + child: ProgressRing(), + ) + ]), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/navigation/navigation_view.dart b/launchpad_app/lib/screens/navigation/navigation_view.dart new file mode 100644 index 00000000..76190f7e --- /dev/null +++ b/launchpad_app/lib/screens/navigation/navigation_view.dart @@ -0,0 +1,262 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../src/common_widgets/page.dart'; + +class NavigationViewPage extends StatefulHookConsumerWidget { + const NavigationViewPage({Key? key}) : super(key: key); + + @override + createState() => _NavigationViewPageState(); +} + +class _NavigationViewPageState extends ConsumerState + with PageMixin { + static const itemHeight = 300.0; + + var topIndex = 0; + + PaneDisplayMode displayMode = PaneDisplayMode.open; + var pageTransition = 'default'; + static const pageTransitions = [ + 'default', + 'entrance', + 'drill in', + 'horizontal', + ]; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('NavigationView')), + children: [ + const Text( + 'The NavigationView control provides top-level navigation for your app. ' + 'It adapts to a variety of screen sizes and supports both top and left ' + 'navigation styles.', + ), + const SizedBox(height: 10.0), + ...buildDisplayMode( + PaneDisplayMode.top, + 'Top display mode', + 'The pane is positioned above the content.', + ), + ...buildDisplayMode( + PaneDisplayMode.open, + 'Open display mode', + 'The pane is expanded and positioned to the left of the content.', + ), + ...buildDisplayMode( + PaneDisplayMode.compact, + 'Compact display mode', + 'The pane shows only icons until opened and is positioned to the left ' + 'of the content. When opened, the pane overlays the content.', + ), + ...buildDisplayMode( + PaneDisplayMode.minimal, + 'Minimal display mode', + 'Only the menu button is shown until the pane is opened. When opened, ' + 'the pane overlays the left side of the content.', + ), + ], + ); + } + + List buildDisplayMode( + PaneDisplayMode displayMode, + String title, + String desc, + ) { + if (displayMode != this.displayMode) return []; + return [ + Wrap(runSpacing: 10.0, spacing: 10.0, children: [ + InfoLabel( + label: 'Display mode', + child: ComboBox( + value: displayMode, + items: ([...PaneDisplayMode.values]..remove(PaneDisplayMode.auto)) + .map((mode) { + return ComboBoxItem(value: mode, child: Text(mode.name)); + }).toList(), + onChanged: (mode) => setState( + () => this.displayMode = mode ?? displayMode, + ), + ), + ), + InfoLabel( + label: 'Page Transition', + child: ComboBox( + items: pageTransitions + .map((e) => ComboBoxItem(value: e, child: Text(e))) + .toList(), + value: pageTransition, + onChanged: (transition) => setState( + () => pageTransition = transition ?? pageTransition, + ), + ), + ), + ]), + subtitle(content: Text(title)), + description(content: Text(desc)), + CardHighlight( + codeSnippet: '''NavigationView( + appBar: const NavigationAppBar( + title: Text('NavigationView'), + ), + pane: NavigationPane( + selected: topIndex, + onChanged: (index) => setState(() => topIndex = index), + displayMode: displayMode, + items: [ + PaneItem( + icon: const Icon(FluentIcons.home), + title: const Text('Home'), + body: BodyItem(), + ), + PaneItem( + icon: const Icon(FluentIcons.issue_tracking), + title: const Text('Track an order'), + infoBadge: const InfoBadge(source: Text('8')), + body: BodyItem(), + ), + PaneItemExpander( + icon: const Icon(FluentIcons.account_management), + title: const Text('Account'), + body: BodyItem(), + items: [ + PaneItem( + icon: const Icon(FluentIcons.mail), + title: const Text('Mail'), + body: BodyItem(), + ), + PaneItem( + icon: const Icon(FluentIcons.calendar), + title: const Text('Calendar'), + body: BodyItem(), + ), + ], + ), + ], + ), +)''', + child: SizedBox( + height: itemHeight, + child: NavigationView( + appBar: const NavigationAppBar( + title: Text('NavigationView'), + ), + pane: NavigationPane( + selected: topIndex, + onChanged: (index) => setState(() => topIndex = index), + displayMode: displayMode, + items: [ + PaneItem( + icon: const Icon(FluentIcons.home), + title: const Text('Home'), + body: const _NavigationBodyItem(), + ), + PaneItem( + icon: const Icon(FluentIcons.issue_tracking), + title: const Text('Track orders'), + infoBadge: const InfoBadge(source: Text('8')), + body: const _NavigationBodyItem( + header: 'Badging', + content: Text( + 'Badging is a non-intrusive and intuitive way to display ' + 'notifications or bring focus to an area within an app - ' + 'whether that be for notifications, indicating new content, ' + 'or showing an alert. An InfoBadge is a small piece of UI ' + 'that can be added into an app and customized to display a ' + 'number, icon, or a simple dot.', + ), + ), + ), + PaneItemExpander( + icon: const Icon(FluentIcons.account_management), + title: const Text('Account'), + body: const _NavigationBodyItem( + header: 'PaneItemExpander', + content: Text( + 'Some apps may have a more complex hierarchical structure ' + 'that requires more than just a flat list of navigation ' + 'items. You may want to use top-level navigation items to ' + 'display categories of pages, with children items displaying ' + 'specific pages. It is also useful if you have hub-style ' + 'pages that only link to other pages. For these kinds of ' + 'cases, you should create a hierarchical NavigationView.', + ), + ), + items: [ + PaneItemHeader(header: const Text('Apps')), + PaneItem( + icon: const Icon(FluentIcons.mail), + title: const Text('Mail'), + body: const _NavigationBodyItem(), + ), + PaneItem( + icon: const Icon(FluentIcons.calendar), + title: const Text('Calendar'), + body: const _NavigationBodyItem(), + ), + ], + ), + ], + footerItems: [ + PaneItem( + icon: const Icon(FluentIcons.settings), + title: const Text('Settings'), + body: const _NavigationBodyItem(), + ), + ], + ), + transitionBuilder: pageTransition == 'default' + ? null + : (child, animation) { + switch (pageTransition) { + case 'entrance': + return EntrancePageTransition( + animation: animation, + child: child, + ); + case 'drill in': + return DrillInPageTransition( + animation: animation, + child: child, + ); + case 'horizontal': + return HorizontalSlidePageTransition( + animation: animation, + child: child, + ); + default: + throw UnsupportedError( + '$pageTransition is not a supported transition', + ); + } + }, + ), + ), + ), + ]; + } +} + +class _NavigationBodyItem extends StatelessWidget { + const _NavigationBodyItem({ + Key? key, + this.header, + this.content, + }) : super(key: key); + + final String? header; + final Widget? content; + + @override + build(BuildContext context) { + return ScaffoldPage.withPadding( + header: PageHeader(title: Text(header ?? 'This is a header text')), + content: content ?? const SizedBox.shrink(), + ); + } +} diff --git a/launchpad_app/lib/screens/navigation/tab_view.dart b/launchpad_app/lib/screens/navigation/tab_view.dart new file mode 100644 index 00000000..ad75217f --- /dev/null +++ b/launchpad_app/lib/screens/navigation/tab_view.dart @@ -0,0 +1,164 @@ +import 'dart:math'; + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class TabViewPage extends StatefulHookConsumerWidget { + const TabViewPage({Key? key}) : super(key: key); + + @override + createState() => _TabViewPageState(); +} + +class _TabViewPageState extends ConsumerState with PageMixin { + var currentIndex = 0; + List? tabs; + + TabWidthBehavior tabWidthBehavior = TabWidthBehavior.equal; + CloseButtonVisibilityMode closeButtonVisibilityMode = + CloseButtonVisibilityMode.always; + var showScrollButtons = true; + var wheelScroll = false; + + Tab generateTab(int index) { + late Tab tab; + tab = Tab( + text: Text('Document $index'), + semanticLabel: 'Document #$index', + icon: const FlutterLogo(), + body: Container( + color: + Colors.accentColors[Random().nextInt(Colors.accentColors.length)], + ), + onClosed: () { + setState(() { + tabs!.remove(tab); + + if (currentIndex > 0) currentIndex--; + }); + }, + ); + return tab; + } + + @override + build(BuildContext context) { + tabs ??= List.generate(3, generateTab); + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('TabView')), + children: [ + const Text( + 'The TabView control is a way to display a set of tabs and their ' + 'respective content. TabViews are useful for displaying several pages ' + '(or documents) of content while giving a user the capability to ' + 'rearrange, open, or close new tabs.', + ), + subtitle( + content: const Text( + 'A TabView with support for adding, closing and rearranging tabs', + ), + ), + Card( + child: Wrap( + spacing: 10.0, + runSpacing: 10.0, + crossAxisAlignment: WrapCrossAlignment.end, + children: [ + SizedBox( + width: 150, + child: InfoLabel( + label: 'Tab width behavior', + child: ComboBox( + isExpanded: true, + value: tabWidthBehavior, + items: TabWidthBehavior.values.map((behavior) { + return ComboBoxItem( + value: behavior, + child: Text(behavior.name), + ); + }).toList(), + onChanged: (behavior) { + if (behavior != null) { + setState(() => tabWidthBehavior = behavior); + } + }, + ), + ), + ), + SizedBox( + width: 150, + child: InfoLabel( + label: 'Close button visibility', + child: ComboBox( + isExpanded: true, + value: closeButtonVisibilityMode, + items: CloseButtonVisibilityMode.values.map((mode) { + return ComboBoxItem( + value: mode, + child: Text(mode.name), + ); + }).toList(), + onChanged: (mode) { + if (mode != null) { + setState(() => closeButtonVisibilityMode = mode); + } + }, + ), + ), + ), + Checkbox( + checked: showScrollButtons, + onChanged: (v) => setState(() => showScrollButtons = v!), + content: const Text('Show scroll buttons'), + ), + Checkbox( + checked: wheelScroll, + onChanged: (v) => setState(() => wheelScroll = v!), + content: const Text('Wheel scroll'), + ), + ], + ), + ), + CardHighlight( + codeSnippet: '''''', + child: SizedBox( + height: 400, + child: TabView( + tabs: tabs!, + currentIndex: currentIndex, + onChanged: (index) => setState(() => currentIndex = index), + tabWidthBehavior: tabWidthBehavior, + closeButtonVisibility: closeButtonVisibilityMode, + showScrollButtons: showScrollButtons, + wheelScroll: wheelScroll, + onNewPressed: () { + setState(() { + final index = tabs!.length + 1; + final tab = generateTab(index); + tabs!.add(tab); + }); + }, + onReorder: (oldIndex, newIndex) { + setState(() { + if (oldIndex < newIndex) { + newIndex -= 1; + } + final item = tabs!.removeAt(oldIndex); + tabs!.insert(newIndex, item); + + if (currentIndex == newIndex) { + currentIndex = oldIndex; + } else if (currentIndex == oldIndex) { + currentIndex = newIndex; + } + }); + }, + ), + ), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/navigation/tree_view.dart b/launchpad_app/lib/screens/navigation/tree_view.dart new file mode 100644 index 00000000..78db905f --- /dev/null +++ b/launchpad_app/lib/screens/navigation/tree_view.dart @@ -0,0 +1,278 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class TreeViewPage extends StatefulHookConsumerWidget { + const TreeViewPage({Key? key}) : super(key: key); + + @override + createState() => _TreeViewPageState(); +} + +class _TreeViewPageState extends ConsumerState with PageMixin { + final treeViewKey = GlobalKey(); + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('TreeView')), + children: [ + const Text( + 'The tree view control enables a hierarchical list with expanding and ' + 'collapsing nodes that contain nested items. It can be used to ' + 'illustrate a folder structure or nested relationships in your UI.\n\n' + 'The tree view uses a combination of indentation and icons to represent ' + 'the nested relationship between parent nodes and child nodes. Collapsed ' + 'nodes use a chevron pointing to the right, and expanded nodes use a ' + 'chevron pointing down.', + ), + subtitle( + content: const Text('A TreeView with Multi-selection enabled'), + ), + CardHighlight( + codeSnippet: r'''final items = [ + TreeViewItem( + content: const Text('Personal Documents'), + value: 'personal_docs', + children: [ + TreeViewItem( + content: const Text('Home Remodel'), + value: 'home_remodel', + children: [ + TreeViewItem( + content: const Text('Contractor Contact Info'), + value: 'contr_cont_inf', + ), + TreeViewItem( + content: const Text('Paint Color Scheme'), + value: 'paint_color_scheme', + ), + TreeViewItem( + content: const Text('Flooring wood grain type'), + value: 'flooring_wood_grain_type', + ), + TreeViewItem( + content: const Text('Kitchen cabinet style'), + value: 'kitchen_cabinet_style', + ), + ], + ), + TreeViewItem( + content: const Text('Tax Documents'), + value: 'tax_docs', + children: [ + TreeViewItem(content: const Text('2017'), value: "tax_2017"), + TreeViewItem( + content: const Text('Middle Years'), + value: 'tax_middle_years', + children: [ + TreeViewItem(content: const Text('2018'), value: "tax_2018"), + TreeViewItem(content: const Text('2019'), value: "tax_2019"), + TreeViewItem(content: const Text('2020'), value: "tax_2020"), + ], + ), + TreeViewItem(content: const Text('2021'), value: "tax_2021"), + TreeViewItem(content: const Text('Current Year'), value: "tax_cur"), + ], + ), + ], + ), +]; + +TreeView( + selectionMode: TreeViewSelectionMode.multiple, + shrinkWrap: true, + items: items, + onItemInvoked: (item) async => debugPrint('onItemInvoked: \$item'), + onSelectionChanged: (selectedItems) async => debugPrint( + 'onSelectionChanged: \${selectedItems.map((i) => i.value)}'), + onSecondaryTap: (item, details) async { + debugPrint('onSecondaryTap $item at ${details.globalPosition}'); + }, +) +''', + child: TreeView( + key: treeViewKey, + selectionMode: TreeViewSelectionMode.multiple, + shrinkWrap: true, + items: items, + onItemInvoked: (item, reason) async => + debugPrint('onItemInvoked(reason=$reason): $item'), + onSelectionChanged: (selectedItems) async => debugPrint( + 'onSelectionChanged: ${selectedItems.map((i) => i.value)}'), + onSecondaryTap: (item, details) async { + debugPrint('onSecondaryTap $item at ${details.globalPosition}'); + }, + ), + ), + subtitle(content: const Text('A TreeView with lazy-loading items')), + CardHighlight( + codeSnippet: r'''final lazyItems = [ + TreeViewItem( + content: const Text('Item with lazy loading'), + value: 'lazy_load', + // This means the item will be expandable, although there are no + // children yet. + lazy: true, + // Ensure the list is modifiable. + children: [], + onExpandToggle: (item, getsExpanded) async { + // If it's already populated, return. + if (item.children.isNotEmpty) return; + + // Do your fetching... + await Future.delayed(const Duration(seconds: 2)); + + // ...and add the fetched nodes. + item.children.addAll([ + TreeViewItem( + content: const Text('Lazy item 1'), + value: 'lazy_1', + ), + TreeViewItem( + content: const Text('Lazy item 2'), + value: 'lazy_2', + ), + TreeViewItem( + content: const Text('Lazy item 3'), + value: 'lazy_3', + ), + TreeViewItem( + content: const Text( + 'Lazy item 4 (this text should not overflow)', + overflow: TextOverflow.ellipsis, + ), + value: 'lazy_4', + ), + ]); + }, + ), +]; + +TreeView( + shrinkWrap: true, + items: lazyItems, + onItemInvoked: (item) async => debugPrint('onItemInvoked: $item'), + onSelectionChanged: (selectedItems) async => debugPrint( + 'onSelectionChanged: ${selectedItems.map((i) => i.value)}'), + onSecondaryTap: (item, details) async { + debugPrint('onSecondaryTap $item at ${details.globalPosition}'); + }, +) +''', + child: Column( + children: [ + TreeView( + shrinkWrap: true, + items: lazyItems, + onItemInvoked: (item, reason) async => + debugPrint('onItemInvoked(reason=$reason): $item'), + onSelectionChanged: (selectedItems) async => debugPrint( + 'onSelectionChanged: ${selectedItems.map((i) => i.value)}'), + onSecondaryTap: (item, details) async { + debugPrint( + 'onSecondaryTap $item at ${details.globalPosition}'); + }, + ), + ], + ), + ), + ], + ); + } + + late final items = [ + TreeViewItem( + content: const Text('Personal Documents'), + value: 'personal_docs', + children: [ + TreeViewItem( + content: const Text('Home Remodel'), + value: 'home_remodel', + children: [ + TreeViewItem( + content: const Text('Contractor Contact Info'), + value: 'contr_cont_inf', + ), + TreeViewItem( + content: const Text('Paint Color Scheme'), + value: 'paint_color_scheme', + ), + TreeViewItem( + content: const Text('Flooring wood grain type'), + value: 'flooring_wood_grain_type', + ), + TreeViewItem( + content: const Text('Kitchen cabinet style'), + value: 'kitchen_cabinet_style', + ), + ], + ), + TreeViewItem( + content: const Text('Tax Documents'), + value: 'tax_docs', + children: [ + TreeViewItem(content: const Text('2017'), value: "tax_2017"), + TreeViewItem( + content: const Text('Middle Years'), + value: 'tax_middle_years', + children: [ + TreeViewItem(content: const Text('2018'), value: "tax_2018"), + TreeViewItem( + content: const Text('2019'), + value: "tax_2019", + selected: true), + TreeViewItem(content: const Text('2020'), value: "tax_2020"), + ], + ), + TreeViewItem(content: const Text('2021'), value: "tax_2021"), + TreeViewItem(content: const Text('Current Year'), value: "tax_cur"), + ], + ), + ], + ), + ]; + + late final lazyItems = [ + TreeViewItem( + content: const Text('Item with lazy loading'), + value: 'lazy_load', + // This means the item will be expandable, although there are no + // children yet. + lazy: true, + // Ensure the list is modifiable. + children: [], + onExpandToggle: (item, getsExpanded) async { + // If it's already populated, return. + if (item.children.isNotEmpty) return; + + // Do your fetching... + await Future.delayed(const Duration(seconds: 2)); + + // ...and add the fetched nodes. + item.children.addAll([ + TreeViewItem( + content: const Text('Lazy item 1'), + value: 'lazy_1', + ), + TreeViewItem( + content: const Text('Lazy item 2'), + value: 'lazy_2', + ), + TreeViewItem( + content: const Text('Lazy item 3'), + value: 'lazy_3', + ), + TreeViewItem( + content: const Text( + 'Lazy item 4 (this text should not overflow)', + overflow: TextOverflow.ellipsis, + ), + value: 'lazy_4', + ), + ]); + }, + ), + ]; +} diff --git a/launchpad_app/lib/screens/surface/acrylic.dart b/launchpad_app/lib/screens/surface/acrylic.dart new file mode 100644 index 00000000..cc3f8bfd --- /dev/null +++ b/launchpad_app/lib/screens/surface/acrylic.dart @@ -0,0 +1,224 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../src/features/settings/presentation/settings.dart'; + +const questionMark = Padding( + padding: EdgeInsetsDirectional.only(start: 4.0), + child: Icon(FluentIcons.status_circle_question_mark, size: 14.0), +); + +InlineSpan _buildLabel(String label, String description) { + return TextSpan( + text: label, + children: [ + WidgetSpan( + child: Tooltip( + useMousePosition: false, + message: description, + child: questionMark, + ), + ), + ], + ); +} + +class AcrylicPage extends StatefulHookConsumerWidget { + const AcrylicPage({Key? key}) : super(key: key); + + @override + createState() => _AcrylicPageState(); +} + +class _AcrylicPageState extends ConsumerState with PageMixin { + var tintOpacity = 0.8; + var luminosityOpacity = 0.8; + var blurAmount = 30.0; + var elevation = 0.0; + Color? color; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Acrylic')), + children: [ + const Text( + 'A translucent material recommended for panel backgrounds. Acrylic is a ' + 'type of Brush that creates a translucent texture. You can apply acrylic ' + 'to app surfaces to add depth and help establish a visual hierarchy.', + ), + subtitle(content: const Text('Default background acrylic brush.')), + Card( + child: SizedBox( + height: 300, + width: 500, + child: Stack(children: const [ + _AcrylicChildren(), + Positioned.fill( + child: Padding( + padding: EdgeInsets.all(12.0), + child: Acrylic(), + ), + ), + ]), + ), + ), + subtitle(content: const Text('Custom acrylic brush.')), + Card( + child: SizedBox( + height: 300, + width: 500, + child: Row(children: [ + Expanded( + child: Stack(children: [ + const _AcrylicChildren(), + Positioned.fill( + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Acrylic( + tintAlpha: tintOpacity, + luminosityAlpha: luminosityOpacity, + blurAmount: blurAmount, + elevation: elevation, + tint: color, + ), + ), + ), + ]), + ), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + InfoLabel.rich( + label: _buildLabel( + 'Tint color', + 'the color/tint overlay layer.', + ), + child: ComboBox( + placeholder: const Text('Tint color '), + onChanged: (c) => setState(() => color = c), + value: color, + items: [ + ComboBoxItem( + value: Colors.white, + child: Row(children: [ + buildColorBox(Colors.white), + const SizedBox(width: 10.0), + const Text('White'), + ]), + ), + ComboBoxItem( + value: const Color(0xE4000000), + child: Row(children: [ + buildColorBox(const Color(0xE4000000)), + const SizedBox(width: 10.0), + const Text('Black'), + ]), + ), + ...List.generate(Colors.accentColors.length, (index) { + final color = Colors.accentColors[index]; + return ComboBoxItem( + value: color, + child: Row(children: [ + buildColorBox(color), + const SizedBox(width: 10.0), + Text(accentColorNames[index + 1]), + ]), + ); + }), + ], + ), + ), + InfoLabel.rich( + label: _buildLabel( + 'Tint opacity', + 'the opacity of the tint layer.', + ), + child: Slider( + value: tintOpacity, + min: 0.0, + max: 1.0, + onChanged: (v) => setState(() => tintOpacity = v), + ), + ), + InfoLabel.rich( + label: _buildLabel( + 'Tint luminosity opacity', + 'controls the amount of saturation that is allowed through ' + 'the acrylic surface from the background.', + ), + child: Slider( + value: luminosityOpacity, + min: 0.0, + max: 1.0, + onChanged: (v) => setState(() => luminosityOpacity = v), + ), + ), + InfoLabel( + label: 'Blur amount', + child: Slider( + value: blurAmount, + min: 0.0, + max: 100, + onChanged: (v) => setState(() => blurAmount = v), + ), + ), + InfoLabel( + label: 'Elevation', + child: Slider( + value: elevation, + min: 0, + max: 20, + onChanged: (v) => setState(() => elevation = v), + ), + ), + ]), + ]), + ), + ), + ], + ); + } + + Widget buildColorBox(Color color) { + const double boxSize = 16.0; + return Container( + height: boxSize, + width: boxSize, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4.0), + ), + ); + } +} + +class _AcrylicChildren extends StatelessWidget { + const _AcrylicChildren({Key? key}) : super(key: key); + + @override + build(BuildContext context) { + return Stack(children: [ + Container( + height: 200, + width: 100, + color: Colors.blue.lightest, + ), + Align( + alignment: AlignmentDirectional.center, + child: Container( + height: 152, + width: 152, + color: Colors.magenta, + ), + ), + Align( + alignment: AlignmentDirectional.bottomEnd, + child: Container( + height: 100, + width: 80, + color: Colors.yellow, + ), + ), + ]); + } +} diff --git a/launchpad_app/lib/screens/surface/commandbars.dart b/launchpad_app/lib/screens/surface/commandbars.dart new file mode 100644 index 00000000..f7367bed --- /dev/null +++ b/launchpad_app/lib/screens/surface/commandbars.dart @@ -0,0 +1,301 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../src/common_widgets/page.dart'; + +class CommandBarsPage extends StatefulHookConsumerWidget { + const CommandBarsPage({Key? key}) : super(key: key); + + @override + createState() => _CommandBarsPageState(); +} + +class _CommandBarsPageState extends ConsumerState + with PageMixin { + final simpleCommandBarItems = [ + CommandBarBuilderItem( + builder: (context, mode, w) => Tooltip( + message: "Create something new!", + child: w, + ), + wrappedItem: CommandBarButton( + icon: const Icon(FluentIcons.add), + label: const Text('New'), + onPressed: () {}, + ), + ), + CommandBarBuilderItem( + builder: (context, mode, w) => Tooltip( + message: "Delete what is currently selected!", + child: w, + ), + wrappedItem: CommandBarButton( + icon: const Icon(FluentIcons.delete), + label: const Text('Delete'), + onPressed: () {}, + ), + ), + CommandBarButton( + icon: const Icon(FluentIcons.archive), + label: const Text('Archive'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.move), + label: const Text('Move'), + onPressed: () {}, + ), + const CommandBarButton( + icon: Icon(FluentIcons.cancel), + label: Text('Disabled'), + onPressed: null, + ), + ]; + + final moreCommandBarItems = [ + CommandBarButton( + icon: const Icon(FluentIcons.reply), + label: const Text('Reply'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.reply_all), + label: const Text('Reply All'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.forward), + label: const Text('Forward'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.search), + label: const Text('Search'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.pin), + label: const Text('Pin'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.unpin), + label: const Text('Unpin'), + onPressed: () {}, + ), + ]; + + final evenMoreCommandBarItems = [ + CommandBarButton( + icon: const Icon(FluentIcons.accept), + label: const Text('Accept'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.calculator_multiply), + label: const Text('Reject'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.share), + label: const Text('Share'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.add_favorite), + label: const Text('Add Favorite'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.back), + label: const Text('Backward'), + onPressed: () {}, + ), + CommandBarButton( + icon: const Icon(FluentIcons.forward), + label: const Text('Forward'), + onPressed: () {}, + ), + ]; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: PageHeader( + title: const Text('CommandBar'), + commandBar: CommandBar( + mainAxisAlignment: MainAxisAlignment.end, + primaryItems: [ + ...simpleCommandBarItems, + ], + ), + ), + children: [ + const Text( + 'Command bars provide users with easy access to your app\'s most ' + 'common tasks. Command bars can provide access to app-level or ' + 'page-specific commands and can be used with any navigation pattern.', + ), + subtitle(content: const Text('Simple command bar (no wrapping)')), + CommandBar( + overflowBehavior: CommandBarOverflowBehavior.noWrap, + primaryItems: [ + ...simpleCommandBarItems, + ], + ), + subtitle( + content: const Text( + 'Command bar with many items (wrapping, auto-compact < 600px)', + ), + ), + CommandBar( + overflowBehavior: CommandBarOverflowBehavior.wrap, + compactBreakpointWidth: 600, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + ], + ), + subtitle( + content: const Text( + 'Carded compact command bar with many items (clipped)', + ), + ), + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 230), + child: CommandBarCard( + child: CommandBar( + overflowBehavior: CommandBarOverflowBehavior.clip, + isCompact: true, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + ], + ), + ), + ), + subtitle( + content: const Text( + 'Carded compact command bar with many items (dynamic overflow)', + ), + ), + CommandBarCard( + child: CommandBar( + overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + const CommandBarSeparator(), + ...evenMoreCommandBarItems, + ], + ), + ), + subtitle( + content: const Text( + 'End-aligned command bar with many items (dynamic overflow, auto-compact < 900px)', + ), + ), + CommandBar( + mainAxisAlignment: MainAxisAlignment.end, + overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, + compactBreakpointWidth: 900, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + const CommandBarSeparator(), + ...evenMoreCommandBarItems, + ], + ), + subtitle( + content: const Text( + 'End-aligned command bar with permanent secondary items (dynamic overflow)', + ), + ), + CommandBar( + mainAxisAlignment: MainAxisAlignment.end, + overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + ], + secondaryItems: evenMoreCommandBarItems, + ), + subtitle( + content: const Text( + 'Command bar with secondary items (wrapping)', + ), + ), + CommandBar( + overflowBehavior: CommandBarOverflowBehavior.wrap, + primaryItems: simpleCommandBarItems, + secondaryItems: moreCommandBarItems, + ), + subtitle( + content: const Text( + 'Carded complex command bar with many items (horizontal scrolling)', + ), + ), + CommandBarCard( + child: Row(children: [ + Expanded( + child: CommandBar( + overflowBehavior: CommandBarOverflowBehavior.scrolling, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + ], + ), + ), + // End-aligned button(s) + CommandBar( + overflowBehavior: CommandBarOverflowBehavior.noWrap, + primaryItems: [ + CommandBarButton( + icon: const Icon(FluentIcons.refresh), + onPressed: () {}, + ), + ], + ), + ]), + ), + subtitle( + content: const Text( + 'Carded complex command bar with many items (dynamic overflow)', + ), + ), + CommandBarCard( + child: Row(children: [ + Expanded( + child: CommandBar( + overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, + overflowItemAlignment: MainAxisAlignment.end, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + ], + secondaryItems: evenMoreCommandBarItems, + ), + ), + // End-aligned button(s) + CommandBar( + overflowBehavior: CommandBarOverflowBehavior.noWrap, + primaryItems: [ + CommandBarButton( + icon: const Icon(FluentIcons.refresh), + onPressed: () {}, + ), + ], + ), + ]), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/surface/content_dialog.dart b/launchpad_app/lib/screens/surface/content_dialog.dart new file mode 100644 index 00000000..aaa5dfe1 --- /dev/null +++ b/launchpad_app/lib/screens/surface/content_dialog.dart @@ -0,0 +1,99 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class ContentDialogPage extends StatefulHookConsumerWidget { + const ContentDialogPage({Key? key}) : super(key: key); + + @override + createState() => _ContentDialogPageState(); +} + +class _ContentDialogPageState extends ConsumerState + with PageMixin { + String? result = ''; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('ContentDialog')), + children: [ + const Text( + 'Dialog controls are modal UI overlays that provide contextual app ' + 'information. They block interactions with the app window until being ' + 'explicitly dismissed. They often request some kind of action from the ' + 'user.', + ), + subtitle(content: const Text('A basic content dialog with content')), + CardHighlight( + codeSnippet: '''Button( + child: const Text('Show dialog'), + onPressed: () => showContentDialog(context), +), + +void showContentDialog(BuildContext context) async { + final result = await showDialog( + context: context, + builder: (context) => ContentDialog( + title: const Text('Delete file permanently?'), + content: const Text( + 'If you delete this file, you won't be able to recover it. Do you want to delete it?', + ), + actions: [ + Button( + child: const Text('Delete'), + onPressed: () { + Navigator.pop(context, 'User deleted file'); + // Delete file here + }, + ), + FilledButton( + child: const Text('Cancel'), + onPressed: () => Navigator.pop(context, 'User canceled dialog'), + ), + ], + ), + ); + setState(() {}); +}''', + child: Row(children: [ + Button( + child: const Text('Show dialog'), + onPressed: () => showContentDialog(context), + ), + const SizedBox(width: 10.0), + Text(result ?? ''), + const Spacer(), + ]), + ), + ], + ); + } + + void showContentDialog(BuildContext context) async { + result = await showDialog( + context: context, + builder: (context) => ContentDialog( + title: const Text('Delete file permanently?'), + content: const Text( + 'If you delete this file, you won\'t be able to recover it. Do you want to delete it?', + ), + actions: [ + Button( + child: const Text('Delete'), + onPressed: () { + Navigator.pop(context, 'User deleted file'); + // Delete file here + }, + ), + FilledButton( + child: const Text('Cancel'), + onPressed: () => Navigator.pop(context, 'User canceled dialog'), + ), + ], + ), + ); + setState(() {}); + } +} diff --git a/launchpad_app/lib/screens/surface/expander.dart b/launchpad_app/lib/screens/surface/expander.dart new file mode 100644 index 00000000..cbb439ca --- /dev/null +++ b/launchpad_app/lib/screens/surface/expander.dart @@ -0,0 +1,197 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../src/common_widgets/card_highlight.dart'; + +class ExpanderPage extends StatefulHookConsumerWidget { + const ExpanderPage({Key? key}) : super(key: key); + + @override + createState() => _ExpanderPageState(); +} + +class _ExpanderPageState extends ConsumerState with PageMixin { + final expanderKey = GlobalKey(); + + var crustOpen = false; + var crusts = [ + 'Classic', + 'Whole wheat', + 'Gluten free', + ]; + var crust = 'Whole wheat'; + var sizes = [ + 'Regular', + 'Thin', + 'Pan', + 'Stuffed', + ]; + var size = 'Pan'; + var checked = false; + + @override + build(BuildContext context) { + final open = expanderKey.currentState?.isExpanded ?? false; + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Expander')), + children: [ + description( + content: const Text( + 'The Expander control lets you show or hide less important content ' + 'that\'s related to a piece of primary content that\'s always visible. ' + 'Items contained in the Header are always visible. The user can expand ' + 'and collapse the Content area, where secondary content is displayed, ' + 'by interacting with the header. When the content area is expanded, it ' + 'pushes other UI elements out of the way; it does not overlay other UI. ' + 'The Expander can expand upwards or downwards.\n\n' + 'Both the Header and Content areas can contain any content, from simple ' + 'text to complex UI layouts. For example, you can use the control to ' + 'show additional options for an item.\n\n' + 'Use an Expander when some primary content should always be visible, ' + 'but related secondary content may be hidden until needed. This UI is ' + 'commonly used when display space is limited and when information or ' + 'options can be grouped together. Hiding the secondary content until ' + 'it\'s needed can also help to focus the user on the most important ' + 'parts of your app.', + ), + ), + subtitle(content: const Text('Simple expander')), + description( + content: const Text( + 'In this example, the trailing vanishes when the expander is open.', + ), + ), + CardHighlight( + codeSnippet: '''Expander( + leading: RadioButton( + checked: checked, + onChanged: (v) => setState(() => checked = v), + ), + header: Text('This text is in header'), + content: Text('This text is in content'), +)''', + child: Expander( + header: const Text('Choose your crust'), + onStateChanged: (open) => setState(() => crustOpen = open), + trailing: crustOpen + ? null + : Text( + '$crust, $size', + style: FluentTheme.of(context).typography.caption, + ), + content: + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: crusts + .map( + (e) => Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: crust == e, + onChanged: (selected) { + if (selected) setState(() => crust = e); + }, + content: Text(e), + ), + ), + ) + .toList(), + ), + const SizedBox(width: 12.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: sizes + .map((e) => Padding( + padding: + const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: size == e, + onChanged: (selected) { + if (selected) setState(() => size = e); + }, + content: Text(e), + ), + )) + .toList(), + ), + ]), + ), + ), + subtitle(content: const Text('Scrollable content')), + const CardHighlight( + codeSnippet: '''Expander( + header: Text('Open to see the scrollable text'), + content: SizedBox( + height: 300, + child: SingleChildScrollView( + child: Text('A LONG TEXT HERE'), + ), + ), +)''', + child: Expander( + header: Text('Open to see the scrollable text'), + content: SizedBox( + height: 300, + child: SingleChildScrollView( + child: SelectableText( + '''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis porta lectus lacus, ut viverra ex aliquet at. Sed ac tempus magna. Ut velit diam, condimentum ac bibendum sit amet, aliquam at quam. Mauris bibendum, elit ut mollis molestie, neque risus lacinia libero, id fringilla lacus odio a nisl. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam viverra tincidunt diam, id porta justo iaculis ac. Aenean ornare bibendum rutrum. Aenean dignissim egestas augue id elementum. Suspendisse dapibus, felis nec varius porta, purus turpis sodales est, sit amet consectetur velit turpis in orci. Curabitur sed tortor purus. Donec ut ligula tortor. Quisque ac nulla dui. Praesent sed diam id dui pharetra facilisis. Maecenas lacinia augue eu metus luctus, vitae efficitur ex accumsan. Sed viverra tellus quis ex tempus, sit amet aliquam mauris hendrerit. Proin tempus nisl mauris, eget ultricies ligula aliquet id. + +Fusce molestie quis augue vel eleifend. Praesent ligula velit, porta id diam sed, malesuada molestie odio. Proin egestas nisl vel leo accumsan, vel ullamcorper ipsum dapibus. Curabitur libero augue, porttitor dictum mauris ut, dignissim blandit lacus. Suspendisse lacinia augue elit, sit amet auctor eros pretium sit amet. Proin ullamcorper augue nulla, sit amet rhoncus nisl gravida ac. Aenean auctor ligula in nibh fermentum fermentum. Aliquam erat volutpat. Sed molestie vulputate diam, id rhoncus augue mattis vitae. Ut tempus tempus dui, in imperdiet elit tincidunt id. Integer congue urna eu nisl bibendum accumsan. Aliquam commodo tempor turpis sit amet suscipit. + +Donec sit amet semper sem. Pellentesque commodo mi in est sagittis ultricies in ut elit. Donec vulputate commodo vestibulum. Pellentesque pulvinar tortor vel suscipit hendrerit. Vestibulum interdum, est et aliquam dapibus, tellus elit pharetra nisl, in volutpat sapien ipsum in velit. Donec gravida erat tellus, et molestie diam interdum sed. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis dolor nisl, viverra sed pellentesque molestie, tempor ut enim. Duis suscipit massa sed dolor suscipit mollis. In lobortis efficitur egestas. Integer blandit, dolor eu tristique mollis, lorem urna convallis arcu, non iaculis elit purus vel velit. Nam eget aliquet quam, vitae luctus urna. Nunc vehicula sagittis risus, vitae pretium lacus ornare semper. In ornare massa vitae odio consequat, eu lacinia mi imperdiet. Vivamus at augue diam. Fusce eu purus magna. + +Fusce tempor, dolor in porttitor porttitor, turpis leo ullamcorper urna, vitae ultrices lorem augue eget nulla. Nulla sodales venenatis tellus quis feugiat. Phasellus sit amet condimentum nulla. Quisque felis lorem, tempus quis odio id, tincidunt volutpat ante. Fusce ultrices dui vel lorem tincidunt, in pellentesque ligula luctus. Morbi luctus est vitae eros blandit dictum. Quisque convallis diam sed arcu volutpat, eget placerat turpis cursus. Aliquam dapibus finibus luctus. Praesent vestibulum viverra risus, nec sollicitudin mi mattis eu. Nulla vestibulum, nibh eget sagittis placerat, elit eros egestas libero, eu luctus justo ante eget tellus. Etiam quis lacus gravida, consequat diam in, laoreet sapien. + +Fusce nunc neque, imperdiet id justo non, porttitor finibus massa. Ut quis risus quis tellus ultricies accumsan et et lorem. Nam pulvinar luctus velit, ut vehicula neque sagittis nec. Integer commodo, metus auctor rutrum finibus, tellus justo feugiat leo, sit amet tempus est justo eu augue. Cras eget nibh ac enim bibendum lobortis. Sed ultricies nunc elit, imperdiet consectetur velit scelerisque eu. Aliquam suscipit libero vel nibh porttitor, vel sodales nisi viverra. Duis vitae rutrum metus, vitae accumsan massa. Sed congue, est interdum commodo facilisis, leo libero blandit tellus, a dapibus tortor odio eget ex. Nunc aliquet nulla vel augue pulvinar, vel luctus risus sagittis. Sed non sodales urna. Phasellus quis sapien placerat, ultricies risus ut, hendrerit mi. Donec pretium ligula non arcu posuere porttitor. Pellentesque eleifend mollis ex non eleifend. Nam sed elit mollis mauris laoreet aliquam eget vel elit.''', + ), + ), + ), + ), + ), + subtitle(content: const Text('Expander opened programatically')), + CardHighlight( + codeSnippet: '''final expanderKey = GlobalKey(); + +Expander( + key: expanderKey, + header: Text('This text is in header'), + content: Text('This text is in content'), + onStateChanged: (open) { + print('state changed to open=$open'); + }, +) + +/// Toggles the current expander state +/// +/// if it's open, now it's closed, and vice versa +void toggle() { + final open = expanderKey.currentState?.open ?? false; + + expanderKey.currentState?.open = !open; +}''', + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded( + child: Expander( + key: expanderKey, + header: const Text('This text is in header'), + content: const Text('This text is in content'), + onStateChanged: (open) => setState(() {}), + ), + ), + const SizedBox(width: 20), + ToggleSwitch( + checked: open, + onChanged: (v) { + setState(() { + expanderKey.currentState?.isExpanded = v; + }); + }, + content: Text(open ? 'Close' : 'Open'), + ), + ])), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/surface/flyouts.dart b/launchpad_app/lib/screens/surface/flyouts.dart new file mode 100644 index 00000000..1d6bf54e --- /dev/null +++ b/launchpad_app/lib/screens/surface/flyouts.dart @@ -0,0 +1,411 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class FlyoutPage extends StatefulHookConsumerWidget { + const FlyoutPage({Key? key}) : super(key: key); + + @override + createState() => _FlyoutShowcaseState(); +} + +class _FlyoutShowcaseState extends ConsumerState { + Typography get typography => FluentTheme.of(context).typography; + + var buttonController = FlyoutController(); + var flyoutController = FlyoutController(); + + @override + dispose() { + buttonController.dispose(); + flyoutController.dispose(); + super.dispose(); + } + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Flyouts')), + children: [ + const Text( + 'A Flyout displays lightweight UI that is either information, or requires user interaction. Unlike a dialog, a Flyout can be light dismissed by clicking or tapping off of it. Use it to collect input from the user, show more details about an item, or ask the user to confirm an action.', + ), + Text('A button with a Flyout', style: typography.subtitle), + const SizedBox(height: 10.0), + CardHighlight( + codeSnippet: + '''// define the controller. It'll be responsible to open/close the flyout programmatically +FlyoutController buttonController = FlyoutController(); + +Flyout( + controller: buttonController, + // [content] is the content of the flyout popup, opened when the user presses + // the button + content: (context) { + return FlyoutContent( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'All items will be removed. Do you want to continue?', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12.0), + Button( + child: const Text('Yes, empty my cart'), + onPressed: buttonController.close, + ), + ], + ), + ); + }, + child: Button( + child: const Text('Empty cart'), + onPressed: buttonController.open, + ) +)''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: Flyout( + controller: buttonController, + content: (context) { + return FlyoutContent( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'All items will be removed. Do you want to continue?', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12.0), + Button( + onPressed: buttonController.close, + child: const Text('Yes, empty my cart'), + ), + ], + ), + ); + }, + child: Button( + onPressed: buttonController.open, + child: const Text('Empty cart'), + ), + ), + ), + ), + const SizedBox(height: 20.0), + DefaultTextStyle( + style: const TextStyle(color: Colors.white), + child: Wrap(spacing: 12.0, runSpacing: 12.0, children: [ + _flyoutOnHover(), + _flyoutOnPress(), + _flyoutOnLongPress(), + _flyoutWithController(), + ]), + ), + DefaultTextStyle( + style: const TextStyle(color: Colors.white), + child: Wrap(spacing: 12.0, runSpacing: 12.0, children: [ + _flyoutAtStart(), + _flyoutAtCenter(), + _flyoutAtEnd(), + _flyoutAtCustomPosition(), + ]), + ), + const PageHeader(title: Text('Menu Flyouts'), padding: 0.0), + const Text( + 'A MenuFlyout displays lightweight UI that is light dismissed by clicking or tapping off of it. Use it to let the user choose from a contextual list of simple commands or options.', + ), + DefaultTextStyle( + style: const TextStyle(color: Colors.white), + child: Wrap(spacing: 12.0, runSpacing: 12.0, children: [ + _menuFlyout(), + _menuFlyoutWithSubItem(), + ]), + ), + ], + ); + } + + Widget _flyoutOnHover() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout on hover', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.hover, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown on hover'), + ); + }, + child: Container( + color: Colors.orange, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Hover to show flyout'), + ), + ), + ), + ]); + } + + Widget _flyoutOnPress() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout on click/press', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.press, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown on press'), + ); + }, + child: Container( + color: Colors.red, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Click/press to show flyout'), + ), + ), + ), + ]); + } + + Widget _flyoutOnLongPress() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout on long click/press', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.longPress, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown on long press'), + ); + }, + child: Container( + color: Colors.magenta, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Long press/click to show flyout'), + ), + ), + ), + ]); + } + + Widget _flyoutWithController() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout with controller', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + controller: flyoutController, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown with the controller'), + ); + }, + child: GestureDetector( + onTap: flyoutController.open, + child: Container( + color: Colors.purple, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Click/press to show flyout'), + ), + ), + ), + ), + ]); + } + + Widget _flyoutAtStart() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout displayed at start', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.press, + placement: FlyoutPlacement.start, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown at the start of the child'), + ); + }, + child: Container( + color: Colors.blue, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Press to show a flyout at start'), + ), + ), + ), + ]); + } + + Widget _flyoutAtCenter() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout displayed at center', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.press, + placement: FlyoutPlacement.center, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown at the center of the child'), + ); + }, + child: Container( + color: Colors.teal, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Press to show a flyout at center'), + ), + ), + ), + ]); + } + + Widget _flyoutAtEnd() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout displayed at end', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.press, + placement: FlyoutPlacement.end, + content: (context) { + return const FlyoutContent( + child: Text('This is a flyout shown at the end of the child'), + ); + }, + child: Container( + color: Colors.green, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Press to show a flyout at end'), + ), + ), + ), + ]); + } + + Widget _flyoutAtCustomPosition() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Flyout displayed at custom position', style: typography.subtitle), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Flyout( + openMode: FlyoutOpenMode.press, + placement: FlyoutPlacement.full, + content: (context) { + return const Align( + alignment: AlignmentDirectional.topEnd, + child: FlyoutContent( + child: Text( + 'This is a flyout shown at a custom position on the window', + ), + ), + ); + }, + child: Container( + color: Colors.yellow, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text( + 'Press to show a flyout at custom position', + style: TextStyle(color: Colors.black), + ), + ), + ), + ), + ]); + } + + Widget _menuFlyout() { + return Flyout( + content: (context) { + return MenuFlyout( + items: [ + MenuFlyoutItem( + leading: const Icon(FluentIcons.share), + text: const Text('Share'), + onPressed: () {}, + ), + MenuFlyoutItem( + leading: const Icon(FluentIcons.copy), + text: const Text('Copy'), + onPressed: () {}, + ), + MenuFlyoutItem( + leading: const Icon(FluentIcons.delete), + text: const Text('Delete'), + onPressed: () {}, + ), + const MenuFlyoutSeparator(), + MenuFlyoutItem( + text: const Text('Rename'), + onPressed: () {}, + ), + MenuFlyoutItem( + text: const Text('Select'), + onPressed: () {}, + ), + ], + ); + }, + openMode: FlyoutOpenMode.press, + child: Container( + color: Colors.orange, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Click to show flyout'), + ), + ); + } + + Widget _menuFlyoutWithSubItem() { + return Flyout( + placement: FlyoutPlacement.end, + content: (context) { + return MenuFlyout( + items: [ + MenuFlyoutSubItem( + text: const Text('New'), + items: [ + MenuFlyoutItem( + text: const Text('Plain Text Document'), + onPressed: () {}, + ), + MenuFlyoutItem( + text: const Text('Rich Text Document'), + onPressed: () {}, + ), + MenuFlyoutItem( + text: const Text('Other formats...'), + onPressed: () {}, + ), + ], + ), + MenuFlyoutItem( + text: const Text('Open'), + onPressed: () {}, + ), + MenuFlyoutItem( + text: const Text('Save'), + onPressed: () {}, + ), + const MenuFlyoutSeparator(), + MenuFlyoutItem( + text: const Text('Exit'), + onPressed: () {}, + ), + ], + ); + }, + openMode: FlyoutOpenMode.press, + child: Container( + color: Colors.orange, + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: const Text('Click to show flyout with subitem'), + ), + ); + } +} diff --git a/launchpad_app/lib/screens/surface/info_bars.dart b/launchpad_app/lib/screens/surface/info_bars.dart new file mode 100644 index 00000000..3550bb38 --- /dev/null +++ b/launchpad_app/lib/screens/surface/info_bars.dart @@ -0,0 +1,196 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../src/common_widgets/page.dart'; + +class InfoBarsPage extends StatefulHookConsumerWidget { + const InfoBarsPage({Key? key}) : super(key: key); + + @override + createState() => _InfoBarsPageState(); +} + +class _InfoBarsPageState extends ConsumerState with PageMixin { + // First info bar + var _firstOpen = true; + var severity = InfoBarSeverity.info; + + // Second info bar + var _secondOpen = true; + var _isLong = false; + var _hasActionButton = true; + var _isIconVisible = true; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('InfoBar')), + children: [ + const Text( + 'Use an InfoBar control when a user should be informed of, acknowledge,' + ' or take action on a changed application state. By default the notification' + ' will remain in the content area until closed by the user but will ' + 'not necessarily break user flow.', + ), + subtitle( + content: const Text( + 'A closable InfoBar with options to change its severity', + ), + ), + CardHighlight( + backgroundColor: FluentTheme.of(context).micaBackgroundColor, + codeSnippet: '''InfoBar( + title: const Text('Title'), + content: const Text( + 'Essential app message for your users to be informed of, ' + 'acknowledge, or take action on.', + ), + severity: $severity, + isLong: true, +)''', + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_firstOpen) + InfoBar( + title: const Text('Title'), + content: const Text( + 'Essential app message for your users to be informed of, ' + 'acknowledge, or take action on.', + ), + severity: severity, + isLong: true, + onClose: () => setState(() => _firstOpen = false), + ), + const SizedBox(height: 10.0), + const Divider(), + const SizedBox(height: 10.0), + Checkbox( + checked: _firstOpen, + onChanged: (v) => setState(() => _firstOpen = v!), + content: const Text('Is open'), + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 10.0), + width: 150.0, + child: ComboBox( + isExpanded: true, + items: InfoBarSeverity.values + .map( + (severity) => ComboBoxItem( + value: severity, + child: Text(severity.name), + ), + ) + .toList(), + value: severity, + onChanged: (v) => setState(() => severity = v ?? severity), + popupColor: () { + switch (severity) { + case InfoBarSeverity.info: + break; + case InfoBarSeverity.warning: + return Colors.warningPrimaryColor; + case InfoBarSeverity.error: + return Colors.errorPrimaryColor; + case InfoBarSeverity.success: + return Colors.successPrimaryColor; + } + }(), + ), + ), + ], + ), + ), + ), + subtitle( + content: const Text( + 'A closable InfoBar with a long and short message and action button', + ), + ), + CardHighlight( + backgroundColor: FluentTheme.of(context).micaBackgroundColor, + codeSnippet: '''InfoBar( + title: const Text('Title'), + content: Text( + ${_isLong ? '"Essential app message for your users to be informed ' + 'of, acknowledge, or take action on. Lorem Ipsum is ' + 'simply dummy text of the printing and typesetting ' + 'industry. Lorem Ipsum has been the industry\'s ' + 'standard dummy text ever since the 1500s, when an ' + 'unknown printer took a galley of type and scrambled ' + 'it to make a type specimen book."' : '"A short essential message"'} + ), + severity: $severity, + isLong: true, + ${_hasActionButton ? '''action: Button( + child: const Text('Action'), + onPressed: () {}, + )''' : null} +)''', + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_secondOpen) + InfoBar( + title: const Text('Title'), + content: Text( + _isLong + ? 'Essential app message for your users to be informed ' + 'of, acknowledge, or take action on. Lorem Ipsum is ' + 'simply dummy text of the printing and typesetting ' + 'industry. Lorem Ipsum has been the industry\'s ' + 'standard dummy text ever since the 1500s, when an ' + 'unknown printer took a galley of type and scrambled ' + 'it to make a type specimen book.' + : 'A short essential message', + ), + severity: severity, + isLong: _isLong, + onClose: () => setState(() => _secondOpen = false), + action: _hasActionButton + ? Button( + child: const Text('Action'), + onPressed: () {}, + ) + : null, + isIconVisible: _isIconVisible, + ), + const SizedBox(height: 10.0), + const Divider(), + const SizedBox(height: 10.0), + Checkbox( + checked: _secondOpen, + onChanged: (v) => setState(() => _secondOpen = v!), + content: const Text('Is open'), + ), + Checkbox( + checked: _isLong, + onChanged: (v) => setState(() => _isLong = v!), + content: const Text('Is long'), + ), + Checkbox( + checked: _hasActionButton, + onChanged: (v) => setState(() => _hasActionButton = v!), + content: const Text('Has action button'), + ), + Checkbox( + checked: _isIconVisible, + onChanged: (v) => setState(() => _isIconVisible = v!), + content: const Text('Is icon visible'), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/surface/progress_indicators.dart b/launchpad_app/lib/screens/surface/progress_indicators.dart new file mode 100644 index 00000000..960cbc6f --- /dev/null +++ b/launchpad_app/lib/screens/surface/progress_indicators.dart @@ -0,0 +1,84 @@ +import 'dart:math'; + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../src/common_widgets/card_highlight.dart'; + +class ProgressIndicatorsPage extends StatefulHookConsumerWidget { + const ProgressIndicatorsPage({Key? key}) : super(key: key); + + @override + createState() => _ProgressIndicatorsPageState(); +} + +class _ProgressIndicatorsPageState extends ConsumerState + with PageMixin { + var determinateValue = Random().nextDouble() * 100; + + @override + build(BuildContext context) { + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Progress controls')), + children: [ + description( + content: const Text( + 'A progress control provides feedback to the user that a long-running ' + 'operation is underway. It can mean that the user cannot interact with ' + 'the app when the progress indicator is visible, and can also indicate ' + 'how long the wait time might be, depending on the indicator used.', + ), + ), + subtitle(content: const Text('Indeterminate Progress Indicators')), + description( + content: const Text( + 'The indeterminate state shows that an operation is underway and its ' + 'completion time is unknown.', + ), + ), + CardHighlight( + codeSnippet: '''// indeterminate progress bar +ProgressBar(), + +// indeterminate progress ring +ProgressRing(),''', + child: RepaintBoundary( + child: Row(children: const [ + ProgressBar(), + SizedBox(width: 20.0), + ProgressRing(), + ]), + ), + ), + subtitle(content: const Text('Determinate Progress Indicators')), + description( + content: const Text( + 'The determinate state shows the percentage completed of a task. ' + 'This should be used during an operation whose duration is known, but ' + 'its progress should not block the user\'s interaction with the app.', + ), + ), + CardHighlight( + codeSnippet: '''// determinate progress bar +ProgressBar(value: ${determinateValue.toInt()}), + +// determinate progress ring +ProgressRing(value: ${determinateValue.toInt()}),''', + child: Row(children: [ + ProgressBar(value: determinateValue), + const SizedBox(width: 20.0), + ProgressRing(value: determinateValue), + const Spacer(), + InfoLabel( + label: 'Progress: ${determinateValue.toInt()}', + child: Slider( + value: determinateValue, + onChanged: (v) => setState(() => determinateValue = v), + ), + ), + ])), + ], + ); + } +} diff --git a/launchpad_app/lib/screens/surface/tiles.dart b/launchpad_app/lib/screens/surface/tiles.dart new file mode 100644 index 00000000..4585bec2 --- /dev/null +++ b/launchpad_app/lib/screens/surface/tiles.dart @@ -0,0 +1,307 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; + +class TilesPage extends StatefulHookConsumerWidget { + const TilesPage({Key? key}) : super(key: key); + + @override + createState() => _TilesPageState(); +} + +class _TilesPageState extends ConsumerState with PageMixin { + final shuffledIcons = FluentIcons.allIcons.values.toList()..shuffle(); + + // first + final firstController = ScrollController(); + var firstSelected = ''; + + // second + final secondController = ScrollController(); + var selected = []; + + // third + var thirdSelected = ''; + final thirdController = ScrollController(); + + @override + build(BuildContext context) { + final theme = FluentTheme.of(context); + return ScaffoldPage.scrollable( + header: const PageHeader(title: Text('Tiles')), + children: [ + description( + content: const Text( + 'A fluent-styled list tile. Usually used inside a ListView', + ), + ), + subtitle(content: const Text('Basic ListView with selectable tiles')), + CardHighlight( + codeSnippet: '''String selectedContact = ''; + +const contacts = ['Kendall', 'Collins', ...]; + +ListView.builder( + itemCount: contacts.length, + itemBuilder: (context, index) { + final contact = contacts[index]; + return ListTile.selectable( + title: Text(contact), + selected: selectedContact == contact, + onSelectionChange: (v) => setState(() => selectedContact = contact), + ); + } +),''', + child: Container( + height: 400, + width: 350, + decoration: BoxDecoration( + border: Border.all( + color: theme.resources.surfaceStrokeColorDefault, + ), + ), + child: ListView.builder( + controller: firstController, + shrinkWrap: true, + itemCount: contacts.length, + itemBuilder: (context, index) { + final contact = contacts[index]; + return ListTile.selectable( + title: Text(contact), + selected: firstSelected == contact, + onSelectionChange: (v) { + setState(() => firstSelected = contact); + }, + ); + }, + ), + ), + ), + subtitle( + content: const Text('ListViewItems with many properties applied'), + ), + CardHighlight( + codeSnippet: '''var selectedContacts = []; + +const contacts = ['Kendall', 'Collins', ...]; + +ListView.builder( + itemCount: contacts.length, + itemBuilder: (context, index) { + final contact = contacts[index]; + return ListTile.selectable( + title: Text(contact), + selected: selectedContacts.contains(contact), + selectionMode: ListTileSelectionMode.multiple, + onSelectionChange: (selected) { + setState(() { + if (selected) { + selectedContacts.add(contact); + } else { + selectedContacts.remove(contact); + } + }); + }, + ); + } +),''', + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + height: 400, + width: 350, + decoration: BoxDecoration( + border: Border.all( + color: theme.resources.surfaceStrokeColorDefault, + ), + ), + child: ListView.builder( + controller: secondController, + shrinkWrap: true, + itemCount: contacts.length, + itemBuilder: (context, index) { + final contact = contacts[index]; + return ListTile.selectable( + leading: const CircleAvatar(radius: 15.0), + title: Text(contact), + subtitle: const Text('With a custom subtitle'), + trailing: Icon(shuffledIcons[index]), + selectionMode: ListTileSelectionMode.multiple, + selected: selected.contains(contact), + onSelectionChange: (selected) { + setState(() { + if (selected) { + this.selected.add(contact); + } else { + this.selected.remove(contact); + } + }); + }, + ); + }, + ), + ), + ]), + ), + subtitle( + content: const Text('ListViewItems with images'), + ), + CardHighlight( + codeSnippet: '''String selectedContact = ''; + +const contacts = ['Kendall', 'Collins', ...]; + +ListView.builder( + itemCount: contacts.length, + itemBuilder: (context, index) { + final contact = contacts[index]; + return ListTile.selectable( + leading: SizedBox( + height: 100, + child: AspectRatio( + aspectRatio: 16 / 9, + child: ColoredBox( + color: Colors.accentColors[index ~/ 20], + child: const Placeholder(), + ), + ), + ), + title: Text(contact), + subtitle: const Text('With a custom subtitle'), + selectionMode: ListTileSelectionMode.single, + selected: selectedContact == contact, + onSelectionChange: (v) => setState(() => selectedContact = contact), + ); + } +),''', + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + height: 400, + width: 550, + decoration: BoxDecoration( + border: Border.all( + color: theme.resources.surfaceStrokeColorDefault, + ), + ), + child: ListView.builder( + controller: thirdController, + shrinkWrap: true, + itemCount: contacts.length, + itemBuilder: (context, index) { + final contact = contacts[index]; + return ListTile.selectable( + leading: SizedBox( + height: 100, + child: AspectRatio( + aspectRatio: 16 / 9, + child: ColoredBox( + color: Colors.accentColors[index ~/ 20], + child: const Placeholder(), + ), + ), + ), + title: Text(contact), + subtitle: const Text('With a custom subtitle'), + selectionMode: ListTileSelectionMode.single, + selected: thirdSelected == contact, + onSelectionChange: (selected) { + setState(() { + if (selected) { + thirdSelected = contact; + } + }); + }, + ); + }, + ), + ), + ]), + ), + ], + ); + } +} + +const _contactsList = '''Kendall +Collins +Adatum Corporation +Henry +Ross +Adventure Works Cycles +Vance +DeLeon +Alpine Ski House +Victoria +Burke +Bellows College +Amber +Rodriguez +Best For You Organics Company +Amari +Rivera +Contoso, Ltd. +Jessie +Irwin +Contoso Pharmaceuticals +Quinn +Campbell +Contoso Suites +Olivia +Wilson +Consolidated Messenger +Ana +Bowman +Fabrikam, Inc. +Shawn +Hughes +Fabrikam Residences +Oscar +Ward +First Up Consultants +Madison +Butler +Fourth Coffee +Graham +Barnes +Graphic Design Institute +Anthony +Ivanov +Humongous Insurance +Michael +Peltier +Lamna Healthcare Company +Morgan +Connors +Liberty's Delightful Sinful Bakery & Cafe +Andre +Lawson +Lucerne Publishing +Preston +Morales +Margie's Travel +Briana +Hernandez +Nod Publishers +Nicole +Wagner +Northwind Traders +Mario +Rogers +Proseware, Inc. +Eugenia +Lopez +Relecloud +Nathan +Rigby +School of Fine Art +Ellis +Turner +Southridge Video +Miguel +Reyes +Tailspin Toys +Hayden +Cook +Tailwind Traders'''; + +var contacts = _contactsList.split('\n'); diff --git a/launchpad_app/lib/screens/surface/tooltip.dart b/launchpad_app/lib/screens/surface/tooltip.dart new file mode 100644 index 00000000..6d4e594a --- /dev/null +++ b/launchpad_app/lib/screens/surface/tooltip.dart @@ -0,0 +1,72 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/card_highlight.dart'; +import 'package:launchpad_app/src/common_widgets/page.dart'; +import 'package:fluent_ui/fluent_ui.dart'; + +class TooltipPage extends ScrollablePage { + TooltipPage({super.key}); + + @override + buildHeader(BuildContext context, WidgetRef ref) { + return const PageHeader(title: Text('Tooltip')); + } + + @override + buildScrollable(BuildContext context, WidgetRef ref) { + return [ + const Text( + 'A ToolTip shows more information about a UI element. You might show information about what the element does, or what the user should do. The ToolTip is shown when a user hovers over or presses and holds the UI element.', + ), + subtitle(content: const Text('Button with a simple tooltip')), + CardHighlight( + codeSnippet: '''Tooltip( + message: 'Simple ToolTip', + child: Button( + child: const Text('Button with a simple tooltip'), + onPressed: () {}, + ), +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: Tooltip( + message: 'Simple ToolTip', + child: Button( + onPressed: () {}, + child: const Text('Button with a simple tooltip'), + ), + ), + ), + ), + subtitle( + content: const Text( + 'Button with an horizontal tooltip at the left without mouse position', + ), + ), + CardHighlight( + codeSnippet: '''Tooltip( + message: 'Horizontal ToolTip', + displayHorizontally: true, + useMousePosition: false, + style: const TooltipThemeData(preferBelow: true), + child: IconButton( + icon: const Icon(FluentIcons.graph_symbol, size: 24.0), + onPressed: () {}, + ), +),''', + child: Align( + alignment: AlignmentDirectional.centerStart, + child: Tooltip( + message: 'Horizontal ToolTip', + displayHorizontally: true, + useMousePosition: false, + style: const TooltipThemeData(preferBelow: true), + child: IconButton( + icon: const Icon(FluentIcons.graph_symbol, size: 24.0), + onPressed: () {}, + ), + ), + ), + ), + ]; + } +} diff --git a/launchpad_app/lib/src/common_widgets/card_highlight.dart b/launchpad_app/lib/src/common_widgets/card_highlight.dart new file mode 100644 index 00000000..35b0f62c --- /dev/null +++ b/launchpad_app/lib/src/common_widgets/card_highlight.dart @@ -0,0 +1,141 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_syntax_view/flutter_syntax_view.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +class CardHighlight extends StatefulHookConsumerWidget { + const CardHighlight({ + Key? key, + this.backgroundColor, + required this.child, + required this.codeSnippet, + }) : super(key: key); + + final Widget child; + final String codeSnippet; + + final Color? backgroundColor; + + @override + createState() => _CardHighlightState(); +} + +class _CardHighlightState extends ConsumerState + with AutomaticKeepAliveClientMixin { + var isOpen = false; + var isCopying = false; + + final expanderKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + super.build(context); + final theme = FluentTheme.of(context); + + return Column(children: [ + Card( + backgroundColor: widget.backgroundColor, + borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)), + child: SizedBox( + width: double.infinity, + child: Align( + alignment: AlignmentDirectional.topStart, + child: widget.child, + ), + ), + ), + Expander( + key: expanderKey, + headerShape: (open) => const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + onStateChanged: (state) { + // this is done because [onStateChanges] is called while the [Expander] + // is updating. By using this, we schedule the rebuilt of this widget + // to the next frame + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + if (mounted) setState(() => isOpen = state); + }); + }, + trailing: isOpen + ? Container( + height: 31, + constraints: const BoxConstraints(minWidth: 75), + child: Button( + style: ButtonStyle( + backgroundColor: isCopying + ? ButtonState.all( + theme.accentColor.defaultBrushFor(theme.brightness), + ) + : null, + ), + child: isCopying + ? Icon( + FluentIcons.check_mark, + color: theme.resources.textOnAccentFillColorPrimary, + size: 18, + ) + : Row(children: const [ + Icon(FluentIcons.copy), + SizedBox(width: 6.0), + Text('Copy') + ]), + onPressed: () { + Clipboard.setData(ClipboardData(text: widget.codeSnippet)); + setState(() => isCopying = true); + Future.delayed(const Duration(milliseconds: 1500), () { + isCopying = false; + if (mounted) setState(() {}); + }); + }, + ), + ) + : null, + header: const Text('Source code'), + content: SyntaxView( + code: widget.codeSnippet, + syntaxTheme: theme.brightness.isDark + ? SyntaxTheme.vscodeDark() + : SyntaxTheme.vscodeLight(), + ), + ), + ]); + } + + @override + bool get wantKeepAlive => true; +} + +const fluentHighlightTheme = { + 'root': TextStyle( + backgroundColor: Color(0x00ffffff), + color: Color(0xffdddddd), + ), + 'keyword': TextStyle( + color: Color.fromARGB(255, 255, 255, 255), fontWeight: FontWeight.bold), + 'selector-tag': + TextStyle(color: Color(0xffffffff), fontWeight: FontWeight.bold), + 'literal': TextStyle(color: Color(0xffffffff), fontWeight: FontWeight.bold), + 'section': TextStyle(color: Color(0xffffffff), fontWeight: FontWeight.bold), + 'link': TextStyle(color: Color(0xffffffff)), + 'subst': TextStyle(color: Color(0xffdddddd)), + 'string': TextStyle(color: Color(0xffdd8888)), + 'title': TextStyle(color: Color(0xffdd8888), fontWeight: FontWeight.bold), + 'name': TextStyle(color: Color(0xffdd8888), fontWeight: FontWeight.bold), + 'type': TextStyle(color: Color(0xffdd8888), fontWeight: FontWeight.bold), + 'attribute': TextStyle(color: Color(0xffdd8888)), + 'symbol': TextStyle(color: Color(0xffdd8888)), + 'bullet': TextStyle(color: Color(0xffdd8888)), + 'built_in': TextStyle(color: Color(0xffdd8888)), + 'addition': TextStyle(color: Color(0xffdd8888)), + 'variable': TextStyle(color: Color(0xffdd8888)), + 'template-tag': TextStyle(color: Color(0xffdd8888)), + 'template-variable': TextStyle(color: Color(0xffdd8888)), + 'comment': TextStyle(color: Color(0xff777777)), + 'quote': TextStyle(color: Color(0xff777777)), + 'deletion': TextStyle(color: Color(0xff777777)), + 'meta': TextStyle(color: Color(0xff777777)), + 'doctag': TextStyle(fontWeight: FontWeight.bold), + 'strong': TextStyle(fontWeight: FontWeight.bold), + 'emphasis': TextStyle(fontStyle: FontStyle.italic), +}; diff --git a/launchpad_app/lib/src/common_widgets/deferred_widget.dart b/launchpad_app/lib/src/common_widgets/deferred_widget.dart new file mode 100644 index 00000000..35068698 --- /dev/null +++ b/launchpad_app/lib/src/common_widgets/deferred_widget.dart @@ -0,0 +1,122 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Copied from https://github.com/flutter/gallery/blob/d030f1e5316310c48fc725f619eb980a0597366d/lib/deferred_widget.dart + +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +typedef LibraryLoader = Future Function(); +typedef DeferredWidgetBuilder = Widget Function(); + +/// Wraps the child inside a deferred module loader. +/// +/// The child is created and a single instance of the Widget is maintained in +/// state as long as closure to create widget stays the same. +/// +class DeferredWidget extends StatefulHookConsumerWidget { + DeferredWidget( + this.libraryLoader, + this.createWidget, { + super.key, + Widget? placeholder, + }) : placeholder = placeholder ?? Container(); + + final LibraryLoader libraryLoader; + final DeferredWidgetBuilder createWidget; + final Widget placeholder; + static final _moduleLoaders = >{}; + static final Set _loadedModules = {}; + + static Future preload(LibraryLoader loader) { + if (!_moduleLoaders.containsKey(loader)) { + _moduleLoaders[loader] = loader().then((dynamic _) { + _loadedModules.add(loader); + }); + } + return _moduleLoaders[loader]!; + } + + @override + createState() => _DeferredWidgetState(); +} + +class _DeferredWidgetState extends ConsumerState { + _DeferredWidgetState(); + + Widget? _loadedChild; + DeferredWidgetBuilder? _loadedCreator; + + @override + void initState() { + /// If module was already loaded immediately create widget instead of + /// waiting for future or zone turn. + if (DeferredWidget._loadedModules.contains(widget.libraryLoader)) { + _onLibraryLoaded(); + } else { + DeferredWidget.preload(widget.libraryLoader) + .then((dynamic _) => _onLibraryLoaded()); + } + super.initState(); + } + + void _onLibraryLoaded() { + setState(() { + _loadedCreator = widget.createWidget; + _loadedChild = _loadedCreator!(); + }); + } + + @override + Widget build(BuildContext context) { + /// If closure to create widget changed, create new instance, otherwise + /// treat as const Widget. + if (_loadedCreator != widget.createWidget && _loadedCreator != null) { + _loadedCreator = widget.createWidget; + _loadedChild = _loadedCreator!(); + } + return _loadedChild ?? widget.placeholder; + } +} + +/// Displays a progress indicator and text description explaining that +/// the widget is a deferred component and is currently being installed. +class DeferredLoadingPlaceholder extends StatelessWidget { + const DeferredLoadingPlaceholder({ + super.key, + this.name = 'This widget', + }); + + final String name; + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + decoration: BoxDecoration( + color: Colors.grey[700], + border: Border.all( + width: 20, + color: Colors.grey[700]!, + ), + borderRadius: const BorderRadius.all(Radius.circular(10))), + width: 250, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('$name is installing.', + style: Theme.of(context).textTheme.headlineMedium), + Container(height: 10), + Text( + '$name is a deferred component which are downloaded and installed at runtime.', + style: Theme.of(context).textTheme.bodyLarge), + Container(height: 20), + const Center(child: CircularProgressIndicator()), + ], + ), + ), + ); + } +} diff --git a/launchpad_app/lib/src/common_widgets/page.dart b/launchpad_app/lib/src/common_widgets/page.dart new file mode 100644 index 00000000..3ca985bd --- /dev/null +++ b/launchpad_app/lib/src/common_widgets/page.dart @@ -0,0 +1,149 @@ +import 'dart:async'; + +import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/common_widgets/deferred_widget.dart'; + +import 'package:fluent_ui/fluent_ui.dart'; + +mixin PageMixin { + Widget description({required Widget content}) { + return Builder(builder: (context) { + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 4.0), + child: DefaultTextStyle( + style: FluentTheme.of(context).typography.body!, + child: content, + ), + ); + }); + } + + Widget subtitle({required Widget content}) { + return Builder(builder: (context) { + return Padding( + padding: const EdgeInsetsDirectional.only(top: 14.0, bottom: 2.0), + child: DefaultTextStyle( + style: FluentTheme.of(context).typography.subtitle!, + child: content, + ), + ); + }); + } +} + +abstract class Page extends HookConsumerWidget with PageMixin { + Page({super.key}) { + _pageIndex++; + } + + final StreamController _controller = StreamController.broadcast(); + Stream get stateStream => _controller.stream; + + @override + Widget build(BuildContext context, WidgetRef ref); + + void setState(VoidCallback func) { + func(); + _controller.add(null); + } +} + +int _pageIndex = -1; + +class ScrollablePage extends Page { + ScrollablePage( + {super.key, this.header, this.bottomBar, this.children = const []}); + + final scrollController = ScrollController(); + + final Widget? header; + final Widget? bottomBar; + final List children; + + Widget? buildHeader(BuildContext context, WidgetRef ref) { + return header; + } + + List buildScrollable(BuildContext context, WidgetRef ref) { + return children; + } + + Widget? buildBottomBar(BuildContext context, WidgetRef ref) { + return bottomBar; + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ScaffoldPage( + key: PageStorageKey(_pageIndex), + header: buildHeader(context, ref), + bottomBar: buildBottomBar(context, ref), + content: ImprovedScrolling( + scrollController: scrollController, + enableCustomMouseWheelScrolling: true, + customMouseWheelScrollConfig: const CustomMouseWheelScrollConfig( + scrollAmountMultiplier: 12.5, + scrollDuration: Duration(milliseconds: 350), + scrollCurve: Curves.easeOutCubic, + mouseWheelTurnsThrottleTimeMs: 50, + ), + child: ListView( + controller: scrollController, + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsetsDirectional.only( + bottom: kPageDefaultVerticalPadding, + start: PageHeader.horizontalPadding(context), + end: PageHeader.horizontalPadding(context), + ), + children: buildScrollable(context, ref), + )), + ); + } +} + +class EmptyPage extends Page { + final Widget? child; + + EmptyPage({ + this.child, + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return child ?? const SizedBox.shrink(); + } +} + +typedef DeferredPageBuilder = Page Function(); + +class DeferredPage extends Page { + final LibraryLoader libraryLoader; + final DeferredPageBuilder createPage; + + DeferredPage({ + super.key, + required this.libraryLoader, + required this.createPage, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return DeferredWidget( + libraryLoader, () => createPage().build(context, ref)); + } +} + +extension PageExtension on List { + List transform(BuildContext context, WidgetRef ref) { + return map((page) { + return StreamBuilder( + stream: page.stateStream, + builder: (context, _) { + return page.build(context, ref); + }, + ); + }).toList(); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/domain/contributor.dart b/launchpad_app/lib/src/features/dashboard/domain/contributor.dart new file mode 100644 index 00000000..43289711 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/domain/contributor.dart @@ -0,0 +1,49 @@ +/// Current contributors of Launchpad +const contributors = [ + Contributor( + username: 'bmcclure', + imageUrl: 'https://avatars.githubusercontent.com/u/277977?v=4', + name: 'Ben McClure', + ) +]; + +class Contributor { + final String? username; + final String name; + final String imageUrl; + + const Contributor({ + required this.username, + required this.name, + required this.imageUrl, + }); + + Contributor copyWith({ + String? username, + String? name, + String? imageUrl, + }) { + return Contributor( + username: username ?? this.username, + name: name ?? this.name, + imageUrl: imageUrl ?? this.imageUrl, + ); + } + + @override + toString() => + 'Contributor(username: $username, name: $name, imageUrl: $imageUrl)'; + + @override + operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Contributor && + other.username == username && + other.name == name && + other.imageUrl == imageUrl; + } + + @override + get hashCode => username.hashCode ^ name.hashCode ^ imageUrl.hashCode; +} diff --git a/launchpad_app/lib/src/features/dashboard/domain/sponsor.dart b/launchpad_app/lib/src/features/dashboard/domain/sponsor.dart new file mode 100644 index 00000000..ae8eb1d3 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/domain/sponsor.dart @@ -0,0 +1,43 @@ +/// Current sponsors of Launchpad +const sponsors = []; + +class Sponsor { + final String? username; + final String name; + final String imageUrl; + + const Sponsor({ + required this.username, + required this.name, + required this.imageUrl, + }); + + Sponsor copyWith({ + String? username, + String? name, + String? imageUrl, + }) { + return Sponsor( + username: username ?? this.username, + name: name ?? this.name, + imageUrl: imageUrl ?? this.imageUrl, + ); + } + + @override + toString() => + 'Sponsor(username: $username, name: $name, imageUrl: $imageUrl)'; + + @override + operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Sponsor && + other.username == username && + other.name == name && + other.imageUrl == imageUrl; + } + + @override + get hashCode => username.hashCode ^ name.hashCode ^ imageUrl.hashCode; +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/changelog.dart b/launchpad_app/lib/src/features/dashboard/presentation/changelog.dart new file mode 100644 index 00000000..0b2a2f77 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/changelog.dart @@ -0,0 +1,143 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/material.dart' as m; +import 'package:flutter_markdown/flutter_markdown.dart' + deferred as flutter_markdown; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../common_widgets/deferred_widget.dart'; + +List? changelog; + +class Changelog extends StatefulHookConsumerWidget { + const Changelog({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _ChangelogState(); +} + +class _ChangelogState extends ConsumerState { + @override + void initState() { + super.initState(); + fetchChangelog(); + } + + void fetchChangelog() async { + final response = await http.get( + Uri.parse( + 'https://raw.githubusercontent.com/VolantisDev/Launchpad/master/CHANGELOG.md', + ), + ); + + if (response.statusCode == 200) { + setState(() => changelog = response.body.split('\n')..removeRange(0, 2)); + } else { + debugPrint(response.body); + } + } + + @override + Widget build(BuildContext context) { + final theme = FluentTheme.of(context); + return DeferredWidget( + flutter_markdown.loadLibrary, + () => ContentDialog( + style: const ContentDialogThemeData(padding: EdgeInsets.zero), + constraints: const BoxConstraints(maxWidth: 600), + content: () { + if (changelog == null) return const ProgressRing(); + return SingleChildScrollView( + child: flutter_markdown.Markdown( + shrinkWrap: true, + data: changelog!.map((line) { + if (line.startsWith('## [')) { + final version = line.split(']').first.replaceAll('## [', ''); + // if (line.split('-').length == 2) { + // print('GO- ${line.split('-')[0]} - ${line.split('-')[1]}'); + // } + var date = line + .split('-') + .last + .replaceAll('[', '') + .replaceAll(']', ''); + + if (!date.startsWith('##')) { + final splitDate = date.split('/'); + final dateTime = DateTime( + int.parse(splitDate[2]), + int.parse(splitDate[1]), + int.parse(splitDate[0]), + ); + final formatter = DateFormat.MMMMEEEEd(); + date = '${formatter.format(dateTime)}\n'; + } else { + date = ''; + } + return '## $version\n$date'; + } + return line; + }).join('\n'), + onTapLink: (text, href, title) { + launchUrl(Uri.parse(href!)); + }, + styleSheet: flutter_markdown.MarkdownStyleSheet.fromTheme( + m.Theme.of(context), + ).copyWith( + a: TextStyle( + color: theme.accentColor.resolveFromReverseBrightness( + theme.brightness, + level: 1, + ), + ), + ), + padding: const EdgeInsets.all(20.0), + ), + ); + }(), + ), + ); + } +} + +// + +// class CodeElementBuilder extends flutter_markdown.MarkdownElementBuilder { +// @override +// Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) { +// var language = 'dart'; + +// if (element.attributes['class'] != null) { +// String lg = element.attributes['class'] as String; +// language = lg.substring(9); +// } +// return SizedBox( +// width: +// MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width, +// child: HighlightView( +// // The original code to be highlighted +// element.textContent, + +// // Specify language +// // It is recommended to give it a value for performance +// language: language, + +// // Specify highlight theme +// // All available themes are listed in `themes` folder +// // theme: MediaQueryData.fromWindow(WidgetsBinding.instance!.window) +// // .platformBrightness == +// // Brightness.light +// // ? atomOneLightTheme +// // : atomOneDarkTheme, + +// // Specify padding +// padding: const EdgeInsets.all(8), + +// // Specify text style +// // textStyle: GoogleFonts.robotoMono(), +// ), +// ); +// } +// } diff --git a/launchpad_app/lib/src/features/dashboard/presentation/contributor.dart b/launchpad_app/lib/src/features/dashboard/presentation/contributor.dart new file mode 100644 index 00000000..3a406a06 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/contributor.dart @@ -0,0 +1,90 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:url_launcher/link.dart'; + +class ContributorDialog extends HookConsumerWidget { + const ContributorDialog({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + assert(debugCheckHasFluentTheme(context)); + + return ContentDialog( + constraints: const BoxConstraints(maxWidth: 600), + title: Row( + children: [ + const Icon(FluentIcons.rocket, size: 24.0), + const SizedBox(width: 8.0), + const Expanded(child: Text('Help Make it Happen')), + SmallIconButton( + child: Tooltip( + message: FluentLocalizations.of(context).closeButtonLabel, + child: IconButton( + icon: const Icon(FluentIcons.chrome_close), + onPressed: Navigator.of(context).pop, + ), + ), + ), + ], + ), + content: const Text( + "Launchpad is free software, and creating it takes a lot of work. If you'd like to help out, you can contribute to the project on GitHub."), + actions: [ + Link( + uri: Uri.parse( + 'https://github.com/VolantisDev/Launchpad/blob/master/CONTRIBUTING.md'), + builder: (context, open) => FilledButton( + onPressed: open, + child: const Text('Contribute to Launchpad'), + ), + ), + ], + ); + } +} + +class _Tier extends StatelessWidget { + const _Tier({ + Key? key, + required this.name, + required this.price, + required this.benefits, + }) : super(key: key); + + final String name; + final String price; + + final List benefits; + + @override + Widget build(BuildContext context) { + final theme = FluentTheme.of(context); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + name, + style: theme.typography.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + Text(price, style: theme.typography.caption), + const SizedBox(height: 20.0), + ...benefits.map((benefit) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsetsDirectional.only(end: 6.0, top: 9.0), + child: Icon(FluentIcons.circle_fill, size: 4.0), + ), + Expanded(child: Text(benefit)), + ], + ); + }), + ], + ); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/contributors.dart b/launchpad_app/lib/src/features/dashboard/presentation/contributors.dart new file mode 100644 index 00000000..ecf2a0ad --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/contributors.dart @@ -0,0 +1,99 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:launchpad_app/src/features/dashboard/domain/contributor.dart'; +import 'package:launchpad_app/src/features/dashboard/presentation/contributor.dart'; +import 'package:url_launcher/link.dart'; + +import 'sponsor.dart'; + +class Contributors extends StatelessWidget { + const Contributors({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = FluentTheme.of(context); + + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row(children: [ + Text('CONTRIBUTORS', style: theme.typography.bodyStrong), + const SizedBox(width: 4.0), + const Icon(FluentIcons.branch_pull_request, size: 16.0), + ]), + const SizedBox(height: 4.0), + Wrap( + spacing: 10.0, + runSpacing: 10.0, + children: [ + ...contributors.map((contributor) { + return Link( + uri: Uri.parse('https://www.github.com/${contributor.username}'), + builder: (context, open) { + return IconButton( + onPressed: open, + icon: ContributorButton( + imageUrl: contributor.imageUrl, + username: contributor.username ?? contributor.name, + ), + ); + }, + ); + }), + IconButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => const ContributorDialog(), + ); + }, + icon: Column(children: [ + SizedBox( + height: 45, + width: 45, + child: ShaderMask( + shaderCallback: (rect) { + return LinearGradient( + colors: [ + theme.accentColor.lighter, + theme.accentColor.normal, + ], + ).createShader(rect); + }, + blendMode: BlendMode.srcATop, + child: const Icon(FluentIcons.robot, size: 45), + ), + ), + const Text('Your mug here'), + ]), + ), + ], + ), + ]); + } +} + +class ContributorButton extends StatelessWidget { + const ContributorButton({ + Key? key, + required this.imageUrl, + required this.username, + }) : super(key: key); + + final String imageUrl; + final String username; + + @override + Widget build(BuildContext context) { + return Column(children: [ + Container( + height: 45, + width: 45, + decoration: BoxDecoration( + image: DecorationImage( + image: NetworkImage(imageUrl), + ), + shape: BoxShape.circle, + ), + ), + Text(username), + ]); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/dashboard.dart b/launchpad_app/lib/src/features/dashboard/presentation/dashboard.dart new file mode 100644 index 00000000..f893bc20 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/dashboard.dart @@ -0,0 +1,48 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/dashboard/presentation/contributors.dart'; +import 'package:launchpad_app/src/features/dashboard/presentation/sponsors.dart'; +import 'package:launchpad_app/src/features/dashboard/presentation/tour_message.dart'; +import 'package:launchpad_app/src/features/dashboard/presentation/updated_message.dart'; +import 'package:launchpad_app/src/utils/heading.dart'; +import 'package:state_persistence/state_persistence.dart'; + +import '../../../common_widgets/page.dart'; + +class DashboardPage extends StatefulHookConsumerWidget { + const DashboardPage({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _DashboardState(); +} + +class _DashboardState extends ConsumerState with PageMixin { + bool selected = true; + String? comboboxValue; + + @override + void initState() { + super.initState(); + } + + @override + build(BuildContext context) { + assert(debugCheckHasFluentTheme(context)); + final theme = FluentTheme.of(context); + + return PersistedStateBuilder(builder: (context, snapshot) { + return ScaffoldPage.scrollable( + header: PageHeader( + title: Text(appHeading), + ), + children: [ + const UpdatedMessage(), + TourMessage(key: GlobalKey()), + const Sponsors(), + const SizedBox(height: 22.0), + const Contributors(), + ], + ); + }); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/sponsor.dart b/launchpad_app/lib/src/features/dashboard/presentation/sponsor.dart new file mode 100644 index 00000000..b24fd773 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/sponsor.dart @@ -0,0 +1,110 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:url_launcher/link.dart'; + +class SponsorDialog extends HookConsumerWidget { + const SponsorDialog({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + assert(debugCheckHasFluentTheme(context)); + + return ContentDialog( + constraints: const BoxConstraints(maxWidth: 600), + title: Row( + children: [ + const Icon(FluentIcons.diamond_user, size: 24.0), + const SizedBox(width: 8.0), + const Expanded(child: Text('Benefits')), + SmallIconButton( + child: Tooltip( + message: FluentLocalizations.of(context).closeButtonLabel, + child: IconButton( + icon: const Icon(FluentIcons.chrome_close), + onPressed: Navigator.of(context).pop, + ), + ), + ), + ], + ), + content: Column(mainAxisSize: MainAxisSize.min, children: [ + const Text( + "Monthly financial contributions of any amount are greatly appreciated and will help ensure Launchpad's continued sustainability and improvement."), + const SizedBox(height: 22.0), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Expanded( + child: _Tier( + name: 'Sponsorship Benefits', + price: 'Starting at \$1 per month', + benefits: [ + 'Support free software', + 'Priority support', + 'Early access to new features', + 'A slick Discord badge and exclusive role', + 'Top sponsors get listed on the dashboard', + ], + ), + ) + ], + ) + ]), + actions: [ + Link( + uri: Uri.parse('https://github.com/sponsors/bmcclure'), + builder: (context, open) => FilledButton( + onPressed: open, + child: const Text('Become a Sponsor'), + ), + ), + ], + ); + } +} + +class _Tier extends StatelessWidget { + const _Tier({ + Key? key, + required this.name, + required this.price, + required this.benefits, + }) : super(key: key); + + final String name; + final String price; + + final List benefits; + + @override + Widget build(BuildContext context) { + final theme = FluentTheme.of(context); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + name, + style: theme.typography.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + Text(price, style: theme.typography.caption), + const SizedBox(height: 15.0), + ...benefits.map((benefit) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsetsDirectional.only(end: 6.0, top: 9.0), + child: Icon(FluentIcons.circle_fill, size: 4.0), + ), + Expanded(child: Text(benefit)), + ], + ); + }), + ], + ); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/sponsors.dart b/launchpad_app/lib/src/features/dashboard/presentation/sponsors.dart new file mode 100644 index 00000000..14289774 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/sponsors.dart @@ -0,0 +1,98 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:url_launcher/link.dart'; + +import '../domain/sponsor.dart'; +import 'sponsor.dart'; + +class Sponsors extends StatelessWidget { + const Sponsors({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = FluentTheme.of(context); + + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row(children: [ + Text('SPONSORS', style: theme.typography.bodyStrong), + const SizedBox(width: 4.0), + const Icon(FluentIcons.heart_fill, size: 16.0), + ]), + const SizedBox(height: 4.0), + Wrap( + spacing: 10.0, + runSpacing: 10.0, + children: [ + ...sponsors.map((sponsor) { + return Link( + uri: Uri.parse('https://www.github.com/${sponsor.username}'), + builder: (context, open) { + return IconButton( + onPressed: open, + icon: SponsorButton( + imageUrl: sponsor.imageUrl, + username: sponsor.username ?? sponsor.name, + ), + ); + }, + ); + }), + IconButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => const SponsorDialog(), + ); + }, + icon: Column(children: [ + SizedBox( + height: 45, + width: 45, + child: ShaderMask( + shaderCallback: (rect) { + return LinearGradient( + colors: [ + theme.accentColor.normal, + theme.accentColor.lighter, + ], + ).createShader(rect); + }, + blendMode: BlendMode.srcATop, + child: const Icon(FluentIcons.diamond_user, size: 45), + ), + ), + const Text('Become a Sponsor!'), + ]), + ), + ], + ), + ]); + } +} + +class SponsorButton extends StatelessWidget { + const SponsorButton({ + Key? key, + required this.imageUrl, + required this.username, + }) : super(key: key); + + final String imageUrl; + final String username; + + @override + Widget build(BuildContext context) { + return Column(children: [ + Container( + height: 45, + width: 45, + decoration: BoxDecoration( + image: DecorationImage( + image: NetworkImage(imageUrl), + ), + shape: BoxShape.circle, + ), + ), + Text(username), + ]); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/tour_message.dart b/launchpad_app/lib/src/features/dashboard/presentation/tour_message.dart new file mode 100644 index 00000000..0aa29248 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/tour_message.dart @@ -0,0 +1,94 @@ +import 'dart:math'; + +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_intro/flutter_intro.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:once/once.dart'; +import 'package:state_persistence/state_persistence.dart'; + +class TourMessage extends StatefulHookConsumerWidget { + const TourMessage({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _TourMessage(); +} + +class _TourMessage extends ConsumerState { + var tourBoxIsVisible = true; + + @override + build(BuildContext context) { + if (kDebugMode) { + OnceWidget.clear(key: "tour"); + } + + return PersistedStateBuilder(builder: (context, snapshot) { + return OnceWidget.showOnce("tour", builder: () { + if (snapshot.hasData) { + var tourCompleted = snapshot.data!["tour_completed"] ?? false; + var tourDismissed = snapshot.data!["tour_dismissed"] ?? false; + + if (tourBoxIsVisible && (tourCompleted || tourDismissed)) { + setState(() => tourBoxIsVisible = false); + } + + return Visibility( + visible: tourBoxIsVisible, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Welcome to Launchpad! Would you like a quick tour to help get up to speed?'), + const SizedBox(height: 10.0), + Row( + children: [ + FilledButton( + onPressed: () => Intro.of(context).start(), + child: const Text("Let's go"), + ), + const SizedBox(width: 10.0), + Button( + onPressed: () => + setState(() => tourBoxIsVisible = false), + child: const Text('Dismiss'), + ) + ], + ), + const SizedBox(height: 44.0), + ])); + } else { + return const SizedBox.shrink(); + } + }); + }); + } +} + +class SponsorButton extends StatelessWidget { + const SponsorButton({ + Key? key, + required this.imageUrl, + required this.username, + }) : super(key: key); + + final String imageUrl; + final String username; + + @override + Widget build(BuildContext context) { + return Column(children: [ + Container( + height: 60, + width: 60, + decoration: BoxDecoration( + image: DecorationImage( + image: NetworkImage(imageUrl), + ), + shape: BoxShape.circle, + ), + ), + Text(username), + ]); + } +} diff --git a/launchpad_app/lib/src/features/dashboard/presentation/updated_message.dart b/launchpad_app/lib/src/features/dashboard/presentation/updated_message.dart new file mode 100644 index 00000000..1058b702 --- /dev/null +++ b/launchpad_app/lib/src/features/dashboard/presentation/updated_message.dart @@ -0,0 +1,55 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:once/once.dart'; + +import 'changelog.dart'; + +class UpdatedMessage extends StatefulHookConsumerWidget { + const UpdatedMessage({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _UpdatedMessage(); +} + +class _UpdatedMessage extends ConsumerState { + @override + build(BuildContext context) { + final theme = FluentTheme.of(context); + + widgetBuilder() { + // TODO Put actual current version information here + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + IconButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: true, + builder: (context) => const Changelog(), + ); + }, + icon: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "What's new in Launchpad 10", + style: theme.typography.body + ?.copyWith(fontWeight: FontWeight.bold), + ), + Text('Dec 17, 2022', style: theme.typography.caption), + Text( + 'A brand new desktop app!', + style: theme.typography.bodyLarge, + ), + ], + ), + ), + const SizedBox(height: 22.0), + ]); + } + + return kDebugMode + ? widgetBuilder() + : OnceWidget.showOnEveryNewVersion(builder: widgetBuilder); + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/application/game_detector.dart b/launchpad_app/lib/src/features/game_platforms/application/game_detector.dart new file mode 100644 index 00000000..c4b04d2e --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/game_detector.dart @@ -0,0 +1,61 @@ +import 'dart:io'; + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/games/domain/game.dart'; +import 'package:launchpad_app/src/features/game_platforms/domain/game_platform.dart'; + +abstract class GameDetectorBase { + Future> detectGames(GamePlatform platform, Ref ref); +} + +class LibraryFolderDetector extends GameDetectorBase { + @override + detectGames(GamePlatform platform, Ref ref) async { + var platformType = await platform.getPlatformType(ref); + var libraryFolders = await platformType?.locateLibraryDirs(); + + var games = []; + + if (libraryFolders != null) { + for (var libraryFolder in libraryFolders) { + var dir = Directory(libraryFolder); + + if (dir.existsSync()) { + for (var file + in dir.listSync(followLinks: false).whereType()) { + var key = file.uri.pathSegments.last; + + games.add(Game( + key: dirKeyGenerator(file), + name: dirNameGenerator(file), + platformId: platform.key, + installDir: file.path, + exeFile: file.path, + )); + } + } + } + } + + return games; + } + + String dirKeyGenerator(Directory dir) { + return dir.uri.pathSegments.last; + } + + String dirNameGenerator(Directory dir) { + return dir.uri.pathSegments.last; + } +} + +class CallbackGameDetector extends GameDetectorBase { + CallbackGameDetector(this.callback); + + final Future> Function(GamePlatform platform) callback; + + @override + detectGames(GamePlatform platform, Ref ref) { + return callback(platform); + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/application/game_platform_types.dart b/launchpad_app/lib/src/features/game_platforms/application/game_platform_types.dart new file mode 100644 index 00000000..37581b5e --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/game_platform_types.dart @@ -0,0 +1,626 @@ +import 'dart:io'; + +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:image/image.dart' as img; +import 'package:launchpad_app/gen/assets.gen.dart'; +import 'package:launchpad_app/proto/BlizzardProductDb.pb.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/game_detector.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/platform_locator/platform_locator.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/platform_locator/xml_locator.dart'; +import 'package:launchpad_app/src/features/games/domain/game.dart'; +import 'package:launchpad_app/src/features/game_platforms/domain/game_platform.dart'; +import 'package:launchpad_dotnet/extract_icon.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; +import 'package:path_provider_windows/src/folders.dart'; +import 'package:path_provider_windows/src/path_provider_windows_real.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'package:vdf/vdf.dart'; +import 'package:win32/win32.dart'; +import 'package:win32_registry/win32_registry.dart'; + +import 'platform_locator/registry_locator.dart'; + +part 'game_platform_types.g.dart'; + +class GamePlatformType { + GamePlatformType({ + required this.key, + required this.name, + this.downloadUrl, + this.defaultInstallDir, + this.defaultExePath, + this.defaultIconPath, + this.defaultVersion, + List? locators, + List? gameDetectors, + this.defaultLibraryDirs, + }) { + this.locators = locators ?? []; + this.gameDetectors = gameDetectors ?? []; + } + + final String key; + final String name; + final String? downloadUrl; + final String? defaultInstallDir; + final String? defaultExePath; + final String? defaultIconPath; + final String? defaultVersion; + final List? defaultLibraryDirs; + late List locators; + late List gameDetectors; + + Future isInstalled() async { + var mainExe = await locateMainExe(); + + return (mainExe != null && File(mainExe).existsSync()); + } + + Future locateInstallDir() async { + var installDir = defaultInstallDir; + + for (var locator in locators) { + var checkInstallDir = await locator.locateInstallDir(); + + if (checkInstallDir != null) { + installDir = checkInstallDir; + + break; + } + } + + return installDir; + } + + Future locateMainExe() async { + var exePath = defaultExePath; + + for (var locator in locators) { + var checkExePath = await locator.locateMainExe(); + + if (checkExePath != null) { + exePath = checkExePath; + + break; + } + } + + if (exePath != null && + exePath != '' && + !exePath.contains(r':/') && + !exePath.contains(r':\')) { + var installDir = await locateInstallDir(); + + if (installDir != null && installDir != '') { + exePath = '$installDir/$exePath'; + } + } + + return exePath; + } + + Future> locateLibraryDirs() async { + var libraryDirs = defaultLibraryDirs ?? []; + + for (var locator in locators) { + var dirs = await locator.locateLibraryDirs(); + + if (dirs != null) { + libraryDirs.addAll(dirs); + } + } + + return libraryDirs; + } + + Future locateInstalledVersion() async { + String? version = defaultVersion; + + for (var locator in locators) { + var checkVersion = await locator.locateInstalledVersion() ?? ''; + + if (checkVersion.isNotEmpty) { + version = checkVersion; + + break; + } + } + + return version; + } + + Future locateIconPath() async { + var iconPath = defaultIconPath; + + for (var locator in locators) { + var checkIconPath = await locator.locateIcon(); + + if (checkIconPath != null && checkIconPath.isNotEmpty) { + iconPath = checkIconPath; + + break; + } + } + + return iconPath; + } + + Future createPlatformIcoFile(String exePath, String outputName, + {String? iconFolderName = "platforms"}) async { + var outputFile = await getApplicationIconPath(outputName, + iconFolderName: iconFolderName); + + if (!File(outputFile).existsSync()) { + extractApplicationIcon(exePath, outputFile, overwrite: true); + } + + return File(outputFile).existsSync(); + } + + Future getApplicationIconPath(String outputName, + {String? iconFolderName = 'platforms'}) async { + var iconDir = "${(await getApplicationSupportDirectory()).path}/icons"; + + if (iconFolderName != null) { + iconDir = '$iconDir/$iconFolderName'; + } + + if (!Directory(iconDir).existsSync()) { + Directory(iconDir).createSync(recursive: true); + } + + return "$iconDir/$outputName.ico"; + } + + Future getClosestIconSize(String iconPath, + {int size = 16}) async { + var bytes = File(iconPath).readAsBytesSync(); + var decoder = img.IcoDecoder(); + img.IcoInfo? info = decoder.startDecode(bytes) as img.IcoInfo?; + img.Image? image; + + if (info != null) { + var numberOfImages = info.images?.length ?? 0; + var frameIndex = -1; + var closestSizeOver = 0; + var overIndex = -1; + var closestSizeUnder = 0; + var underIndex = -1; + + if (numberOfImages > 0) { + for (var i = 0; i < numberOfImages; i++) { + var imageData = info.images![i]; + + if (imageData.width == size) { + frameIndex = i; + break; + } + + if (imageData.width > size) { + if (closestSizeOver == 0 || (imageData.width < closestSizeOver)) { + closestSizeOver = imageData.width; + overIndex = i; + } + } else { + if (closestSizeUnder == 0 || (imageData.width > closestSizeUnder)) { + closestSizeUnder = imageData.width; + underIndex = i; + } + } + } + + if (frameIndex == -1) { + if (closestSizeOver > -1 && closestSizeUnder > -1) { + if (closestSizeOver - size < size - closestSizeUnder) { + frameIndex = overIndex; + } else { + frameIndex = underIndex; + } + } else if (closestSizeOver > -1) { + frameIndex = overIndex; + } else if (closestSizeUnder > -1) { + frameIndex = underIndex; + } + } + + if (frameIndex > -1) { + image = decoder.decodeFrame(frameIndex); + } + } + } + + return image; + } + + Future getApplicationIconImage(String filePath, String outputName, + {String? iconFolderName, int size = 16}) async { + img.Image? iconImage; + Image? image; + + if (await createPlatformIcoFile(filePath, outputName)) { + var outputFile = await getApplicationIconPath(outputName, + iconFolderName: iconFolderName); + + // TODO Call getCLosestIconSize and find a way to render it + image = Image.file( + File(outputFile), + width: size.toDouble(), + height: size.toDouble(), + ); + } + + return image; + } + + Future locateIcon({int size = 16}) async { + var iconPath = await locateIconPath(); + Widget? icon; + + if (iconPath != null && iconPath.isNotEmpty) { + var isSvg = p.extension(iconPath) == '.svg'; + var isExe = p.extension(iconPath) == '.exe'; + var isIco = p.extension(iconPath) == '.ico'; + var isAsset = false; + + if (iconPath.startsWith('asset://')) { + isAsset = true; + iconPath = iconPath.replaceFirst('asset://', ''); + } + + try { + if (isExe) { + icon = await getApplicationIconImage(iconPath, key, + iconFolderName: "platforms"); + } else if (isIco) { + if (isAsset || File(iconPath).existsSync()) { + var bytes = File(iconPath).readAsBytesSync(); + var decoder = img.IcoDecoder(); + img.IcoInfo? info = decoder.startDecode(bytes) as img.IcoInfo?; + + if (info != null) { + var iconImage = decoder.decodeFrame(0); + + if (iconImage != null) { + icon = Image.memory(iconImage.getBytes()); + } + } + } + } else if (isSvg) { + icon = isAsset + ? SvgPicture.asset( + iconPath, + width: size.toDouble(), + height: size.toDouble(), + ) + : SvgPicture.file( + File(iconPath), + width: size.toDouble(), + height: size.toDouble(), + ); + } else { + icon = isAsset + ? Image.asset( + iconPath, + width: size.toDouble(), + height: size.toDouble(), + ) + : Image.file( + File(iconPath), + width: size.toDouble(), + height: size.toDouble(), + ); + } + } on Exception catch (_) { + icon = null; + } + } + + icon ??= const Icon(FluentIcons.game); + + return icon; + } +} + +@Riverpod(keepAlive: true) +Future> gamePlatformTypes( + GamePlatformTypesRef ref) async { + var appData = + await PathProviderWindows().getPath(WindowsKnownFolder.RoamingAppData); + + appData ??= "C:/Users/${Platform.environment['USERNAME']}/AppData/Roaming"; + + final riotPlatform = GamePlatformType( + key: 'riot', + name: 'Riot Client', + defaultExePath: "RiotClientServices.exe", + defaultIconPath: Assets.graphics.platformIconRiot.keyName, + locators: [ + RegistryLocator( + installDir: RegistryLocatorValue( + registryHive: RegistryHive.classesRoot, + registryKey: r'riotclient\shell\open\command', + stringSelector: r'^"?(.*)\RiotClientServices.exe"?.*', + ), + ), + ], + gameDetectors: [ + LibraryFolderDetector(), + ], + ); + + riotPlatform.locators.add(CallbackLocator( + libraryDirs: () async { + var installDir = await riotPlatform.locateInstallDir(); + + var libraryDirs = []; + + if (installDir != null && installDir != "") { + libraryDirs.add(Directory(installDir).parent.path); + } + + return libraryDirs; + }, + )); + + final steamPlatform = GamePlatformType( + key: 'steam', + name: 'Steam', + downloadUrl: 'https://store.steampowered.com/about/', + defaultInstallDir: r'C:\Program Files (x86)\Steam', + defaultExePath: 'Steam.exe', + defaultIconPath: Assets.graphics.platformIconSteam.keyName, + locators: [ + RegistryUninstallDataLocator( + uninstallerKey: + r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Steam', + installDir: RegistryLocatorValue( + registryHive: RegistryHive.currentUser, + registryKey: r'SOFTWARE\Valve\Steam', + registryValueName: 'SteamPath', + ), + mainExe: RegistryLocatorValue( + registryHive: RegistryHive.currentUser, + registryKey: r'SOFTWARE\Valve\Steam', + registryValueName: 'SteamExe', + ), + ) + ], + ); + + steamPlatform.locators.add(CallbackLocator( + libraryDirs: () async { + var installDir = await steamPlatform.locateInstallDir(); + + var libraryDirs = []; + + if (installDir != null && installDir != "") { + var steamApps = Directory('$installDir/steamapps'); + var vdfFile = File('$installDir/steamapps/libraryfolders.vdf'); + + if (vdfFile.existsSync()) { + var vdfData = vdf.decode(vdfFile.readAsStringSync()); + + var libraryFolders = vdfData['LibraryFolders']; + + if (libraryFolders is Map) { + libraryDirs.addAll([ + for (var libraryFolder in libraryFolders.values) + '${libraryFolder["path"]}/steamapps', + ]); + } + } else if (steamApps.existsSync()) { + libraryDirs.add(steamApps.path); + } + } + + return libraryDirs; + }, + )); + + steamPlatform.gameDetectors + .add(CallbackGameDetector((GamePlatform platform) async { + var libraryDirs = await steamPlatform.locateLibraryDirs(); + + var games = []; + + for (final libraryDir in libraryDirs) { + var dir = Directory(libraryDir); + + if (dir.existsSync()) { + for (final file in dir.listSync(followLinks: false).where((file) => + (file is File && + file.uri.pathSegments.last.endsWith('.acf') && + file.uri.pathSegments.last.startsWith('appmanifest_')))) { + var vdfData = vdf.decode((file as File).readAsStringSync()); + + if (vdfData.containsKey("AppState")) { + var state = vdfData["AppState"]; + + games.add(Game( + key: state["name"], + platformId: platform.key, + platformRef: state["appid"], + installDir: '${dir.path}/common/${state["installdir"]}', + name: state["name"], + )); + } + } + } + } + + return games; + })); + + return { + "default": GamePlatformType(key: 'default', name: 'Default'), + "bethesda": GamePlatformType( + key: 'bethesda', + name: 'Bethesda.net', + downloadUrl: 'https://bethesda.net/en/game/bethesda-launcher', + defaultInstallDir: '', + defaultExePath: 'BethesdaNetLauncher.exe', + defaultIconPath: Assets.graphics.platformIconBethesda.keyName, + defaultLibraryDirs: ["games"], + locators: [ + RegistryUninstallDataLocator( + uninstallerKey: + r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{3448917E-E4FE-4E30-9502-9FD52EABB6F5}_is1', + ) + ], + gameDetectors: [ + LibraryFolderDetector(), + ], + ), + "blizzard": GamePlatformType( + key: 'blizzard', + name: 'Battle.net', + downloadUrl: 'https://www.blizzard.com/en-us/apps/battle.net/desktop', + defaultInstallDir: r'C:\Program Files (x86)\Battle.net', + defaultExePath: 'Battle.net.exe', + defaultIconPath: Assets.graphics.platformIconBlizzard.keyName, + locators: [ + RegistryUninstallDataLocator( + uninstallerKey: + r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Battle.net', + ), + ], + gameDetectors: [ + CallbackGameDetector((GamePlatform platform) async { + var productDb = await getBlizzardProductDb(); + var games = []; + + if (productDb != null) { + for (var product in productDb.productInstall) { + var installPath = product.settings.installPath; + var folder = Directory(installPath); + var key = folder.uri.pathSegments.last; + + if (folder.existsSync()) { + games.add(Game( + key: key, + platformId: platform.key, + platformRef: product.productCode, + installDir: folder.path, + name: key, + )); + } + } + } + + return games; + }), + ]), + "ea": GamePlatformType( + key: 'ea', + name: 'EA App', + downloadUrl: 'https://www.ea.com/games/library/pc-download', + defaultInstallDir: + r'C:\Program Files\Electronic Arts\EA Desktop\EA Desktop', + defaultExePath: 'EADesktop.exe', + defaultIconPath: Assets.graphics.platformIconEa.keyName, + defaultLibraryDirs: [r'C:\Program Files\EA Games'], + locators: [ + RegistryLocator( + installDir: RegistryLocatorValue( + registryKey: r'SOFTWARE\Electronic Arts\EA Desktop', + registryValueName: 'InstallLocation', + ), + mainExe: RegistryLocatorValue( + registryKey: r'SOFTWARE\Electronic Arts\EA Desktop', + registryValueName: 'DesktopAppPath', + ), + ) + ], + gameDetectors: [ + LibraryFolderDetector(), + ], + ), + "epic": GamePlatformType( + key: 'epic', + name: 'Epic Store', + downloadUrl: 'https://www.epicgames.com/store/en-US/download', + defaultInstallDir: r'C:\Program Files (x86)\Epic Games\Launcher', + defaultExePath: + r'\Launcher\Engine\Binaries\Win64\EpicGamesLauncher.exe', + defaultIconPath: Assets.graphics.platformIconEpic.keyName, + locators: [ + RegistryUninstallDataLocator( + uninstallerKey: + r'SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties', + ), + ]), + "origin": GamePlatformType( + key: 'origin', + name: 'EA Origin', + downloadUrl: 'https://www.origin.com/usa/en-us/store/download', + defaultInstallDir: r'C:\Program Files (x86)\Origin', + defaultExePath: 'Origin.exe', + defaultIconPath: Assets.graphics.platformIconOrigin.keyName, + locators: [ + RegistryUninstallDataLocator( + uninstallerKey: + r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Origin', + ), + XmlLocator( + xmlFilePath: "$appData/Origin/local.xml", + libraryDir: + XmlLocatorValue('/Settings/Setting[@key="DownloadInPlaceDir"]'), + ) + ], + gameDetectors: [ + LibraryFolderDetector(), + ], + ), + "riot": riotPlatform, + "steam": steamPlatform, + "uplay": GamePlatformType( + key: 'uplay', + name: 'Uplay', + downloadUrl: 'https://uplay.ubi.com/', + defaultInstallDir: + r'C:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher', + defaultExePath: 'Uplay.exe', + defaultIconPath: Assets.graphics.platformIconUplay.keyName, + locators: [ + RegistryUninstallDataLocator( + uninstallerKey: + r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Uplay', + ) + ], + gameDetectors: [ + LibraryFolderDetector(), + ], + ), + }; +} + +Future getBlizzardProductDb() async { + var dir = await PathProviderWindows().getPath(FOLDERID_ProgramData); + var productDbPath = '$dir/Battle.net/Agent/product.db'; + var file = File(productDbPath); + + return (file.existsSync()) + ? Database.fromBuffer(file.readAsBytesSync()) + : null; +} + +Future getBlizzardInstallPath(String productCode) async { + var productDb = await getBlizzardProductDb(); + + String? installPath; + + if (productDb != null) { + for (final install in productDb.productInstall) { + if (install.productCode == productCode) { + installPath = install.settings.installPath; + break; + } + } + } + + return installPath; +} diff --git a/launchpad_app/lib/src/features/game_platforms/application/game_platform_types.g.dart b/launchpad_app/lib/src/features/game_platforms/application/game_platform_types.g.dart new file mode 100644 index 00000000..c14d2c21 --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/game_platform_types.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_platform_types.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $gamePlatformTypesHash() => r'8a61850571bbe96096f1981b4ff4a380f0947fac'; + +/// See also [gamePlatformTypes]. +final gamePlatformTypesProvider = FutureProvider>( + gamePlatformTypes, + name: r'gamePlatformTypesProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : $gamePlatformTypesHash, +); +typedef GamePlatformTypesRef = FutureProviderRef>; diff --git a/launchpad_app/lib/src/features/game_platforms/application/game_platforms.dart b/launchpad_app/lib/src/features/game_platforms/application/game_platforms.dart new file mode 100644 index 00000000..d1a1ce92 --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/game_platforms.dart @@ -0,0 +1,29 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../domain/game_platform.dart'; +import 'game_platform_types.dart'; + +part 'game_platforms.g.dart'; + +@Riverpod(keepAlive: true) +Future> gamePlatforms(GamePlatformsRef ref) async { + var platformTypes = ref.watch(gamePlatformTypesProvider).value; + var platforms = []; + + if (platformTypes != null) { + for (var platformType in platformTypes.values) { + if (await platformType.isInstalled()) { + platforms.add(GamePlatform( + key: platformType.key, + name: platformType.name, + platformTypeId: platformType.key, + installDir: await platformType.locateInstallDir(), + exeFile: await platformType.locateMainExe(), + iconPath: await platformType.locateIconPath(), + )); + } + } + } + + return platforms; +} diff --git a/launchpad_app/lib/src/features/game_platforms/application/game_platforms.g.dart b/launchpad_app/lib/src/features/game_platforms/application/game_platforms.g.dart new file mode 100644 index 00000000..027489ab --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/game_platforms.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_platforms.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $gamePlatformsHash() => r'1a0fcc00b4d49f1f67f056c5c52fb6507b059521'; + +/// See also [gamePlatforms]. +final gamePlatformsProvider = FutureProvider>( + gamePlatforms, + name: r'gamePlatformsProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : $gamePlatformsHash, +); +typedef GamePlatformsRef = FutureProviderRef>; diff --git a/launchpad_app/lib/src/features/game_platforms/application/platform_locator/platform_locator.dart b/launchpad_app/lib/src/features/game_platforms/application/platform_locator/platform_locator.dart new file mode 100644 index 00000000..4e136b48 --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/platform_locator/platform_locator.dart @@ -0,0 +1,77 @@ +abstract class PlatformLocatorBase { + Future locateInstallDir() { + return Future.value(null); + } + + Future locateMainExe() { + return Future.value(null); + } + + Future locateInstalledVersion() { + return Future.value(null); + } + + Future locateUninstallCommand() { + return Future.value(null); + } + + Future?> locateLibraryDirs() { + return Future.value(null); + } + + Future locateIcon() { + return Future.value(null); + } +} + +class CallbackLocator extends PlatformLocatorBase { + CallbackLocator({ + this.installDir, + this.mainExe, + this.installedVersion, + this.uninstallCommand, + this.icon, + this.libraryDirs, + }); + + final Future Function()? installDir; + final Future Function()? mainExe; + final Future Function()? installedVersion; + final Future Function()? uninstallCommand; + final Future Function()? icon; + final Future?> Function()? libraryDirs; + + @override + locateInstallDir() { + return (installDir != null) ? installDir!() : Future.value(null); + } + + @override + locateMainExe() { + return (mainExe != null) ? mainExe!() : Future.value(null); + } + + @override + locateInstalledVersion() { + return (installedVersion != null) + ? installedVersion!() + : Future.value(null); + } + + @override + locateUninstallCommand() { + return (uninstallCommand != null) + ? uninstallCommand!() + : Future.value(null); + } + + @override + locateIcon() { + return (icon != null) ? icon!() : Future.value(null); + } + + @override + locateLibraryDirs() { + return (libraryDirs != null) ? libraryDirs!() : Future.value(null); + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/application/platform_locator/registry_locator.dart b/launchpad_app/lib/src/features/game_platforms/application/platform_locator/registry_locator.dart new file mode 100644 index 00000000..55f3d3a5 --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/platform_locator/registry_locator.dart @@ -0,0 +1,140 @@ +import 'package:win32/win32.dart'; +import 'package:win32_registry/win32_registry.dart'; + +import 'platform_locator.dart'; + +class RegistryLocator extends PlatformLocatorBase { + RegistryLocator( + {this.installDir, + this.mainExe, + this.installedVersion, + this.uninstallCommand, + this.icon, + this.libraryDir}); + + final RegistryLocatorValue? installDir; + final RegistryLocatorValue? mainExe; + final RegistryLocatorValue? installedVersion; + final RegistryLocatorValue? uninstallCommand; + final RegistryLocatorValue? icon; + final RegistryLocatorValue? libraryDir; + + @override + Future locateInstallDir() { + return Future.value(lookupValue(installDir)); + } + + @override + Future locateMainExe() { + return Future.value(lookupValue(mainExe)); + } + + @override + Future locateInstalledVersion() { + return Future.value(lookupValue(installedVersion)); + } + + @override + Future locateIcon() { + return Future.value(lookupValue(icon)); + } + + @override + Future locateUninstallCommand() { + return Future.value(lookupValue(uninstallCommand)); + } + + @override + Future?> locateLibraryDirs() { + return Future.value(null); + } + + String? lookupValue(RegistryLocatorValue? registryValue) { + return (registryValue != null) ? registryValue.lookupValue() : null; + } +} + +class RegistryUninstallDataLocator extends RegistryLocator { + RegistryUninstallDataLocator({ + RegistryHive registryHive = RegistryHive.localMachine, + required String uninstallerKey, + String? installDirValue = 'InstallLocation', + String? installedVersionValue = 'DisplayVersion', + String? uninstallCommandValue = 'UninstallString', + String? iconValue = 'DisplayIcon', + RegistryLocatorValue? installDir, + RegistryLocatorValue? mainExe, + RegistryLocatorValue? installedVersion, + RegistryLocatorValue? uninstallCommand, + RegistryLocatorValue? icon, + }) : super( + installDir: installDir ?? + RegistryLocatorValue( + registryHive: registryHive, + registryKey: uninstallerKey, + registryValueName: installDirValue ?? '', + ), + mainExe: mainExe, + installedVersion: installedVersion ?? + RegistryLocatorValue( + registryHive: registryHive, + registryKey: uninstallerKey, + registryValueName: installedVersionValue ?? '', + ), + uninstallCommand: uninstallCommand ?? + RegistryLocatorValue( + registryHive: registryHive, + registryKey: uninstallerKey, + registryValueName: uninstallCommandValue ?? '', + ), + icon: icon ?? + RegistryLocatorValue( + registryHive: registryHive, + registryKey: uninstallerKey, + registryValueName: iconValue ?? '', + ), + ); +} + +class RegistryLocatorValue { + RegistryLocatorValue({ + this.registryHive = RegistryHive.localMachine, + required this.registryKey, + this.registryValueName = '', + this.stringSelector, + this.stripTrailingSlash = true, + }); + + final RegistryHive registryHive; + final String registryKey; + final String registryValueName; + + /// A regular expression to select a substring from the registry value. + final String? stringSelector; + final bool stripTrailingSlash; + + String? lookupValue() { + String? value; + + try { + final key = Registry.openPath(registryHive, path: registryKey); + value = key.getValueAsString(registryValueName); + } on WindowsException catch (_) { + value = null; + } + + if (value != null && stringSelector != null) { + final regex = RegExp(stringSelector!); + final match = regex.firstMatch(value); + value = (match != null) ? match.group(0) : null; + } + + if (value != null && + stripTrailingSlash && + (value[value.length - 1] == r'\' || value[value.length - 1] == '/')) { + value = value.substring(0, value.length - 1); + } + + return value; + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/application/platform_locator/xml_locator.dart b/launchpad_app/lib/src/features/game_platforms/application/platform_locator/xml_locator.dart new file mode 100644 index 00000000..4a6d73ab --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/application/platform_locator/xml_locator.dart @@ -0,0 +1,119 @@ +import 'dart:io'; + +import 'package:xml/xml.dart'; + +import 'platform_locator.dart'; + +class XmlLocator extends PlatformLocatorBase { + XmlLocator({ + required this.xmlFilePath, + this.installDir, + this.mainExe, + this.installedVersion, + this.uninstallCommand, + this.icon, + this.libraryDir, + }) { + var file = File(xmlFilePath); + + if (file.existsSync()) { + document = XmlDocument.parse(file.readAsStringSync()); + } + } + + final String xmlFilePath; + XmlDocument? document; + final XmlLocatorValue? installDir; + final XmlLocatorValue? mainExe; + final XmlLocatorValue? installedVersion; + final XmlLocatorValue? uninstallCommand; + final XmlLocatorValue? icon; + final XmlLocatorValue? libraryDir; + + @override + Future locateInstallDir() { + return lookupValue(installDir); + } + + @override + Future locateMainExe() { + return lookupValue(mainExe); + } + + @override + Future locateInstalledVersion() { + return lookupValue(installedVersion); + } + + @override + Future locateUninstallCommand() { + return lookupValue(uninstallCommand); + } + + @override + Future locateIcon() { + return lookupValue(icon); + } + + @override + Future?> locateLibraryDirs() async { + var value = await lookupValue(libraryDir); + + return (value != null) ? [value] : null; + } + + Future lookupValue(XmlLocatorValue? xmlValue) { + return (xmlValue != null && document != null) + ? xmlValue.lookupValue(document!.rootElement) + : Future.value(null); + } +} + +class XmlLocatorValue { + XmlLocatorValue(this.elementSelector); + + /// Select an element by path such as /settings/setting + final String elementSelector; + + Future lookupValue(XmlElement rootElement) async { + var xmlNode = rootElement; + String? selectAttribute; + + if (elementSelector != '/') { + var searchPath = elementSelector; + + if (searchPath.substring(0, 1) == '/') { + searchPath = searchPath.substring(1); + } + + for (final element in searchPath.split('/')) { + final match = RegExp( + r'(?[^\[@]+)(\[@(?[^=]+)=?"?(?[^\]"]*)?"?\])?(@(?.+))?') + .firstMatch(searchPath)!; + + final attribute = match.namedGroup('attribute'); + final value = match.namedGroup('value'); + selectAttribute = match.namedGroup('selectAttribute'); + + for (final node in xmlNode.findElements(element)) { + if (attribute != null && value != null) { + var attributeValue = node.getAttribute(attribute); + + if (attributeValue == null || attributeValue != value) { + continue; + } + } + + xmlNode = node; + break; + } + } + } + + var value = (selectAttribute != null && selectAttribute != "") + ? xmlNode.getAttribute(selectAttribute) + : xmlNode.text; + + return value; + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/data/game_platform_data.dart b/launchpad_app/lib/src/features/game_platforms/data/game_platform_data.dart new file mode 100644 index 00000000..6391b575 --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/data/game_platform_data.dart @@ -0,0 +1,20 @@ +import 'package:isar/isar.dart'; + +part 'game_platform_data.g.dart'; + +@collection +class GamePlatformData { + Id? id = Isar.autoIncrement; + + String? key; + + String? name; + + String? installDir; + + String? exeFile; + + String? iconPath; + + String? platformTypeId; +} diff --git a/launchpad_app/lib/src/features/game_platforms/data/game_platform_data.g.dart b/launchpad_app/lib/src/features/game_platforms/data/game_platform_data.g.dart new file mode 100644 index 00000000..fb8511a9 --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/data/game_platform_data.g.dart @@ -0,0 +1,1538 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_platform_data.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetGamePlatformDataCollection on Isar { + IsarCollection get gamePlatformDatas => this.collection(); +} + +const GamePlatformDataSchema = CollectionSchema( + name: r'GamePlatformData', + id: -8166720709079364800, + properties: { + r'exeFile': PropertySchema( + id: 0, + name: r'exeFile', + type: IsarType.string, + ), + r'iconPath': PropertySchema( + id: 1, + name: r'iconPath', + type: IsarType.string, + ), + r'installDir': PropertySchema( + id: 2, + name: r'installDir', + type: IsarType.string, + ), + r'key': PropertySchema( + id: 3, + name: r'key', + type: IsarType.string, + ), + r'name': PropertySchema( + id: 4, + name: r'name', + type: IsarType.string, + ), + r'platformTypeId': PropertySchema( + id: 5, + name: r'platformTypeId', + type: IsarType.string, + ) + }, + estimateSize: _gamePlatformDataEstimateSize, + serialize: _gamePlatformDataSerialize, + deserialize: _gamePlatformDataDeserialize, + deserializeProp: _gamePlatformDataDeserializeProp, + idName: r'id', + indexes: {}, + links: {}, + embeddedSchemas: {}, + getId: _gamePlatformDataGetId, + getLinks: _gamePlatformDataGetLinks, + attach: _gamePlatformDataAttach, + version: '3.0.5', +); + +int _gamePlatformDataEstimateSize( + GamePlatformData object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.exeFile; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.iconPath; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.installDir; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.key; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.name; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.platformTypeId; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _gamePlatformDataSerialize( + GamePlatformData object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.exeFile); + writer.writeString(offsets[1], object.iconPath); + writer.writeString(offsets[2], object.installDir); + writer.writeString(offsets[3], object.key); + writer.writeString(offsets[4], object.name); + writer.writeString(offsets[5], object.platformTypeId); +} + +GamePlatformData _gamePlatformDataDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = GamePlatformData(); + object.exeFile = reader.readStringOrNull(offsets[0]); + object.iconPath = reader.readStringOrNull(offsets[1]); + object.id = id; + object.installDir = reader.readStringOrNull(offsets[2]); + object.key = reader.readStringOrNull(offsets[3]); + object.name = reader.readStringOrNull(offsets[4]); + object.platformTypeId = reader.readStringOrNull(offsets[5]); + return object; +} + +P _gamePlatformDataDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + case 2: + return (reader.readStringOrNull(offset)) as P; + case 3: + return (reader.readStringOrNull(offset)) as P; + case 4: + return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _gamePlatformDataGetId(GamePlatformData object) { + return object.id ?? Isar.autoIncrement; +} + +List> _gamePlatformDataGetLinks(GamePlatformData object) { + return []; +} + +void _gamePlatformDataAttach( + IsarCollection col, Id id, GamePlatformData object) { + object.id = id; +} + +extension GamePlatformDataQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension GamePlatformDataQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder + idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder + idGreaterThan(Id id, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder + idLessThan(Id id, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension GamePlatformDataQueryFilter + on QueryBuilder { + QueryBuilder + exeFileIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'exeFile', + )); + }); + } + + QueryBuilder + exeFileIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'exeFile', + )); + }); + } + + QueryBuilder + exeFileEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'exeFile', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'exeFile', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeFileIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'exeFile', + value: '', + )); + }); + } + + QueryBuilder + exeFileIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'exeFile', + value: '', + )); + }); + } + + QueryBuilder + iconPathIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'iconPath', + )); + }); + } + + QueryBuilder + iconPathIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'iconPath', + )); + }); + } + + QueryBuilder + iconPathEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'iconPath', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'iconPath', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + iconPathIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'iconPath', + value: '', + )); + }); + } + + QueryBuilder + iconPathIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'iconPath', + value: '', + )); + }); + } + + QueryBuilder + idIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'id', + )); + }); + } + + QueryBuilder + idIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'id', + )); + }); + } + + QueryBuilder + idEqualTo(Id? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idGreaterThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idLessThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idBetween( + Id? lower, + Id? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + installDirIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'installDir', + )); + }); + } + + QueryBuilder + installDirIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'installDir', + )); + }); + } + + QueryBuilder + installDirEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'installDir', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'installDir', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + installDirIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'installDir', + value: '', + )); + }); + } + + QueryBuilder + installDirIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'installDir', + value: '', + )); + }); + } + + QueryBuilder + keyIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'key', + )); + }); + } + + QueryBuilder + keyIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'key', + )); + }); + } + + QueryBuilder + keyEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'key', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'key', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'key', + value: '', + )); + }); + } + + QueryBuilder + keyIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'key', + value: '', + )); + }); + } + + QueryBuilder + nameIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'name', + )); + }); + } + + QueryBuilder + nameIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'name', + )); + }); + } + + QueryBuilder + nameEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: '', + )); + }); + } + + QueryBuilder + nameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'name', + value: '', + )); + }); + } + + QueryBuilder + platformTypeIdIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'platformTypeId', + )); + }); + } + + QueryBuilder + platformTypeIdIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'platformTypeId', + )); + }); + } + + QueryBuilder + platformTypeIdEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformTypeId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'platformTypeId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'platformTypeId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'platformTypeId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'platformTypeId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'platformTypeId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'platformTypeId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'platformTypeId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformTypeIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformTypeId', + value: '', + )); + }); + } + + QueryBuilder + platformTypeIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'platformTypeId', + value: '', + )); + }); + } +} + +extension GamePlatformDataQueryObject + on QueryBuilder {} + +extension GamePlatformDataQueryLinks + on QueryBuilder {} + +extension GamePlatformDataQuerySortBy + on QueryBuilder { + QueryBuilder + sortByExeFile() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.asc); + }); + } + + QueryBuilder + sortByExeFileDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.desc); + }); + } + + QueryBuilder + sortByIconPath() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.asc); + }); + } + + QueryBuilder + sortByIconPathDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.desc); + }); + } + + QueryBuilder + sortByInstallDir() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.asc); + }); + } + + QueryBuilder + sortByInstallDirDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.desc); + }); + } + + QueryBuilder sortByKey() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.asc); + }); + } + + QueryBuilder + sortByKeyDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.desc); + }); + } + + QueryBuilder sortByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder + sortByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } + + QueryBuilder + sortByPlatformTypeId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformTypeId', Sort.asc); + }); + } + + QueryBuilder + sortByPlatformTypeIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformTypeId', Sort.desc); + }); + } +} + +extension GamePlatformDataQuerySortThenBy + on QueryBuilder { + QueryBuilder + thenByExeFile() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.asc); + }); + } + + QueryBuilder + thenByExeFileDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.desc); + }); + } + + QueryBuilder + thenByIconPath() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.asc); + }); + } + + QueryBuilder + thenByIconPathDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder + thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder + thenByInstallDir() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.asc); + }); + } + + QueryBuilder + thenByInstallDirDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.desc); + }); + } + + QueryBuilder thenByKey() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.asc); + }); + } + + QueryBuilder + thenByKeyDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.desc); + }); + } + + QueryBuilder thenByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder + thenByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } + + QueryBuilder + thenByPlatformTypeId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformTypeId', Sort.asc); + }); + } + + QueryBuilder + thenByPlatformTypeIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformTypeId', Sort.desc); + }); + } +} + +extension GamePlatformDataQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByExeFile( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'exeFile', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByIconPath({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'iconPath', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByInstallDir({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'installDir', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByKey( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'key', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByName( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'name', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByPlatformTypeId({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'platformTypeId', + caseSensitive: caseSensitive); + }); + } +} + +extension GamePlatformDataQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder exeFileProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'exeFile'); + }); + } + + QueryBuilder iconPathProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'iconPath'); + }); + } + + QueryBuilder + installDirProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'installDir'); + }); + } + + QueryBuilder keyProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'key'); + }); + } + + QueryBuilder nameProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'name'); + }); + } + + QueryBuilder + platformTypeIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'platformTypeId'); + }); + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/domain/game_platform.dart b/launchpad_app/lib/src/features/game_platforms/domain/game_platform.dart new file mode 100644 index 00000000..3a8d0aaa --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/domain/game_platform.dart @@ -0,0 +1,32 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/game_platform_types.dart'; + +part 'game_platform.freezed.dart'; + +part 'game_platform.g.dart'; + +@freezed +class GamePlatform with _$GamePlatform { + const GamePlatform._(); + + const factory GamePlatform({ + required String key, + required String name, + required String platformTypeId, + String? installDir, + String? exeFile, + String? iconPath, + }) = _GamePlatform; + + factory GamePlatform.fromJson(Map json) => + _$GamePlatformFromJson(json); + + Future getPlatformType(Ref ref) async { + var platformTypes = ref.watch(gamePlatformTypesProvider); + + return platformTypes.value?.values + .firstWhere((element) => element.key == platformTypeId); + } +} diff --git a/launchpad_app/lib/src/features/game_platforms/domain/game_platform.freezed.dart b/launchpad_app/lib/src/features/game_platforms/domain/game_platform.freezed.dart new file mode 100644 index 00000000..a84ec29e --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/domain/game_platform.freezed.dart @@ -0,0 +1,274 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'game_platform.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +GamePlatform _$GamePlatformFromJson(Map json) { + return _GamePlatform.fromJson(json); +} + +/// @nodoc +mixin _$GamePlatform { + String get key => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get platformTypeId => throw _privateConstructorUsedError; + String? get installDir => throw _privateConstructorUsedError; + String? get exeFile => throw _privateConstructorUsedError; + String? get iconPath => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $GamePlatformCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GamePlatformCopyWith<$Res> { + factory $GamePlatformCopyWith( + GamePlatform value, $Res Function(GamePlatform) then) = + _$GamePlatformCopyWithImpl<$Res, GamePlatform>; + @useResult + $Res call( + {String key, + String name, + String platformTypeId, + String? installDir, + String? exeFile, + String? iconPath}); +} + +/// @nodoc +class _$GamePlatformCopyWithImpl<$Res, $Val extends GamePlatform> + implements $GamePlatformCopyWith<$Res> { + _$GamePlatformCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? platformTypeId = null, + Object? installDir = freezed, + Object? exeFile = freezed, + Object? iconPath = freezed, + }) { + return _then(_value.copyWith( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + platformTypeId: null == platformTypeId + ? _value.platformTypeId + : platformTypeId // ignore: cast_nullable_to_non_nullable + as String, + installDir: freezed == installDir + ? _value.installDir + : installDir // ignore: cast_nullable_to_non_nullable + as String?, + exeFile: freezed == exeFile + ? _value.exeFile + : exeFile // ignore: cast_nullable_to_non_nullable + as String?, + iconPath: freezed == iconPath + ? _value.iconPath + : iconPath // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_GamePlatformCopyWith<$Res> + implements $GamePlatformCopyWith<$Res> { + factory _$$_GamePlatformCopyWith( + _$_GamePlatform value, $Res Function(_$_GamePlatform) then) = + __$$_GamePlatformCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String key, + String name, + String platformTypeId, + String? installDir, + String? exeFile, + String? iconPath}); +} + +/// @nodoc +class __$$_GamePlatformCopyWithImpl<$Res> + extends _$GamePlatformCopyWithImpl<$Res, _$_GamePlatform> + implements _$$_GamePlatformCopyWith<$Res> { + __$$_GamePlatformCopyWithImpl( + _$_GamePlatform _value, $Res Function(_$_GamePlatform) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? platformTypeId = null, + Object? installDir = freezed, + Object? exeFile = freezed, + Object? iconPath = freezed, + }) { + return _then(_$_GamePlatform( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + platformTypeId: null == platformTypeId + ? _value.platformTypeId + : platformTypeId // ignore: cast_nullable_to_non_nullable + as String, + installDir: freezed == installDir + ? _value.installDir + : installDir // ignore: cast_nullable_to_non_nullable + as String?, + exeFile: freezed == exeFile + ? _value.exeFile + : exeFile // ignore: cast_nullable_to_non_nullable + as String?, + iconPath: freezed == iconPath + ? _value.iconPath + : iconPath // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_GamePlatform extends _GamePlatform with DiagnosticableTreeMixin { + const _$_GamePlatform( + {required this.key, + required this.name, + required this.platformTypeId, + this.installDir, + this.exeFile, + this.iconPath}) + : super._(); + + factory _$_GamePlatform.fromJson(Map json) => + _$$_GamePlatformFromJson(json); + + @override + final String key; + @override + final String name; + @override + final String platformTypeId; + @override + final String? installDir; + @override + final String? exeFile; + @override + final String? iconPath; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'GamePlatform(key: $key, name: $name, platformTypeId: $platformTypeId, installDir: $installDir, exeFile: $exeFile, iconPath: $iconPath)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'GamePlatform')) + ..add(DiagnosticsProperty('key', key)) + ..add(DiagnosticsProperty('name', name)) + ..add(DiagnosticsProperty('platformTypeId', platformTypeId)) + ..add(DiagnosticsProperty('installDir', installDir)) + ..add(DiagnosticsProperty('exeFile', exeFile)) + ..add(DiagnosticsProperty('iconPath', iconPath)); + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_GamePlatform && + (identical(other.key, key) || other.key == key) && + (identical(other.name, name) || other.name == name) && + (identical(other.platformTypeId, platformTypeId) || + other.platformTypeId == platformTypeId) && + (identical(other.installDir, installDir) || + other.installDir == installDir) && + (identical(other.exeFile, exeFile) || other.exeFile == exeFile) && + (identical(other.iconPath, iconPath) || + other.iconPath == iconPath)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, key, name, platformTypeId, installDir, exeFile, iconPath); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_GamePlatformCopyWith<_$_GamePlatform> get copyWith => + __$$_GamePlatformCopyWithImpl<_$_GamePlatform>(this, _$identity); + + @override + Map toJson() { + return _$$_GamePlatformToJson( + this, + ); + } +} + +abstract class _GamePlatform extends GamePlatform { + const factory _GamePlatform( + {required final String key, + required final String name, + required final String platformTypeId, + final String? installDir, + final String? exeFile, + final String? iconPath}) = _$_GamePlatform; + const _GamePlatform._() : super._(); + + factory _GamePlatform.fromJson(Map json) = + _$_GamePlatform.fromJson; + + @override + String get key; + @override + String get name; + @override + String get platformTypeId; + @override + String? get installDir; + @override + String? get exeFile; + @override + String? get iconPath; + @override + @JsonKey(ignore: true) + _$$_GamePlatformCopyWith<_$_GamePlatform> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/launchpad_app/lib/src/features/game_platforms/domain/game_platform.g.dart b/launchpad_app/lib/src/features/game_platforms/domain/game_platform.g.dart new file mode 100644 index 00000000..da9f176d --- /dev/null +++ b/launchpad_app/lib/src/features/game_platforms/domain/game_platform.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_platform.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_GamePlatform _$$_GamePlatformFromJson(Map json) => + _$_GamePlatform( + key: json['key'] as String, + name: json['name'] as String, + platformTypeId: json['platformTypeId'] as String, + installDir: json['installDir'] as String?, + exeFile: json['exeFile'] as String?, + iconPath: json['iconPath'] as String?, + ); + +Map _$$_GamePlatformToJson(_$_GamePlatform instance) => + { + 'key': instance.key, + 'name': instance.name, + 'platformTypeId': instance.platformTypeId, + 'installDir': instance.installDir, + 'exeFile': instance.exeFile, + 'iconPath': instance.iconPath, + }; diff --git a/launchpad_app/lib/src/features/games/data/game_data.dart b/launchpad_app/lib/src/features/games/data/game_data.dart new file mode 100644 index 00000000..0d711978 --- /dev/null +++ b/launchpad_app/lib/src/features/games/data/game_data.dart @@ -0,0 +1,31 @@ +import 'package:isar/isar.dart'; + +import 'launch_config_data.dart'; +import 'launch_process_data.dart'; + +part 'game_data.g.dart'; + +@collection +class GameData { + Id? id = Isar.autoIncrement; + + String? key; + + String? name; + + String? platformId; + + String? platformRef; + + String? installDir; + + String? exeFile; + + String? iconPath; + + String? iconType; + + final launchConfig = IsarLink(); + + final launchProcesses = IsarLinks(); +} diff --git a/launchpad_app/lib/src/features/games/data/game_data.g.dart b/launchpad_app/lib/src/features/games/data/game_data.g.dart new file mode 100644 index 00000000..f7c60ef0 --- /dev/null +++ b/launchpad_app/lib/src/features/games/data/game_data.g.dart @@ -0,0 +1,1949 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_data.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetGameDataCollection on Isar { + IsarCollection get gameDatas => this.collection(); +} + +const GameDataSchema = CollectionSchema( + name: r'GameData', + id: -6579857273911918101, + properties: { + r'exeFile': PropertySchema( + id: 0, + name: r'exeFile', + type: IsarType.string, + ), + r'iconPath': PropertySchema( + id: 1, + name: r'iconPath', + type: IsarType.string, + ), + r'iconType': PropertySchema( + id: 2, + name: r'iconType', + type: IsarType.string, + ), + r'installDir': PropertySchema( + id: 3, + name: r'installDir', + type: IsarType.string, + ), + r'key': PropertySchema( + id: 4, + name: r'key', + type: IsarType.string, + ), + r'name': PropertySchema( + id: 5, + name: r'name', + type: IsarType.string, + ), + r'platformId': PropertySchema( + id: 6, + name: r'platformId', + type: IsarType.string, + ), + r'platformRef': PropertySchema( + id: 7, + name: r'platformRef', + type: IsarType.string, + ) + }, + estimateSize: _gameDataEstimateSize, + serialize: _gameDataSerialize, + deserialize: _gameDataDeserialize, + deserializeProp: _gameDataDeserializeProp, + idName: r'id', + indexes: {}, + links: { + r'launchConfig': LinkSchema( + id: 6684939833935828972, + name: r'launchConfig', + target: r'LaunchConfigData', + single: true, + ), + r'launchProcesses': LinkSchema( + id: -8678547302105873528, + name: r'launchProcesses', + target: r'LaunchProcessData', + single: false, + ) + }, + embeddedSchemas: {}, + getId: _gameDataGetId, + getLinks: _gameDataGetLinks, + attach: _gameDataAttach, + version: '3.0.5', +); + +int _gameDataEstimateSize( + GameData object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.exeFile; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.iconPath; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.iconType; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.installDir; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.key; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.name; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.platformId; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.platformRef; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _gameDataSerialize( + GameData object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.exeFile); + writer.writeString(offsets[1], object.iconPath); + writer.writeString(offsets[2], object.iconType); + writer.writeString(offsets[3], object.installDir); + writer.writeString(offsets[4], object.key); + writer.writeString(offsets[5], object.name); + writer.writeString(offsets[6], object.platformId); + writer.writeString(offsets[7], object.platformRef); +} + +GameData _gameDataDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = GameData(); + object.exeFile = reader.readStringOrNull(offsets[0]); + object.iconPath = reader.readStringOrNull(offsets[1]); + object.iconType = reader.readStringOrNull(offsets[2]); + object.id = id; + object.installDir = reader.readStringOrNull(offsets[3]); + object.key = reader.readStringOrNull(offsets[4]); + object.name = reader.readStringOrNull(offsets[5]); + object.platformId = reader.readStringOrNull(offsets[6]); + object.platformRef = reader.readStringOrNull(offsets[7]); + return object; +} + +P _gameDataDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + case 2: + return (reader.readStringOrNull(offset)) as P; + case 3: + return (reader.readStringOrNull(offset)) as P; + case 4: + return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readStringOrNull(offset)) as P; + case 6: + return (reader.readStringOrNull(offset)) as P; + case 7: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _gameDataGetId(GameData object) { + return object.id ?? Isar.autoIncrement; +} + +List> _gameDataGetLinks(GameData object) { + return [object.launchConfig, object.launchProcesses]; +} + +void _gameDataAttach(IsarCollection col, Id id, GameData object) { + object.id = id; + object.launchConfig.attach( + col, col.isar.collection(), r'launchConfig', id); + object.launchProcesses.attach( + col, col.isar.collection(), r'launchProcesses', id); +} + +extension GameDataQueryWhereSort on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension GameDataQueryWhere on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension GameDataQueryFilter + on QueryBuilder { + QueryBuilder exeFileIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'exeFile', + )); + }); + } + + QueryBuilder exeFileIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'exeFile', + )); + }); + } + + QueryBuilder exeFileEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'exeFile', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'exeFile', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'exeFile', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder exeFileIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'exeFile', + value: '', + )); + }); + } + + QueryBuilder exeFileIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'exeFile', + value: '', + )); + }); + } + + QueryBuilder iconPathIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'iconPath', + )); + }); + } + + QueryBuilder iconPathIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'iconPath', + )); + }); + } + + QueryBuilder iconPathEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'iconPath', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'iconPath', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'iconPath', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconPathIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'iconPath', + value: '', + )); + }); + } + + QueryBuilder iconPathIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'iconPath', + value: '', + )); + }); + } + + QueryBuilder iconTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'iconType', + )); + }); + } + + QueryBuilder iconTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'iconType', + )); + }); + } + + QueryBuilder iconTypeEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'iconType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'iconType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'iconType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'iconType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'iconType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'iconType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'iconType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'iconType', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder iconTypeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'iconType', + value: '', + )); + }); + } + + QueryBuilder iconTypeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'iconType', + value: '', + )); + }); + } + + QueryBuilder idIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'id', + )); + }); + } + + QueryBuilder idIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'id', + )); + }); + } + + QueryBuilder idEqualTo(Id? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id? lower, + Id? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder installDirIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'installDir', + )); + }); + } + + QueryBuilder + installDirIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'installDir', + )); + }); + } + + QueryBuilder installDirEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'installDir', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'installDir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'installDir', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder installDirIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'installDir', + value: '', + )); + }); + } + + QueryBuilder + installDirIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'installDir', + value: '', + )); + }); + } + + QueryBuilder keyIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'key', + )); + }); + } + + QueryBuilder keyIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'key', + )); + }); + } + + QueryBuilder keyEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'key', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'key', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder keyIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'key', + value: '', + )); + }); + } + + QueryBuilder keyIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'key', + value: '', + )); + }); + } + + QueryBuilder nameIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'name', + )); + }); + } + + QueryBuilder nameIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'name', + )); + }); + } + + QueryBuilder nameEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: '', + )); + }); + } + + QueryBuilder nameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'name', + value: '', + )); + }); + } + + QueryBuilder platformIdIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'platformId', + )); + }); + } + + QueryBuilder + platformIdIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'platformId', + )); + }); + } + + QueryBuilder platformIdEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'platformId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'platformId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformId', + value: '', + )); + }); + } + + QueryBuilder + platformIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'platformId', + value: '', + )); + }); + } + + QueryBuilder platformRefIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'platformRef', + )); + }); + } + + QueryBuilder + platformRefIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'platformRef', + )); + }); + } + + QueryBuilder platformRefEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformRef', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformRefGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'platformRef', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'platformRef', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'platformRef', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'platformRef', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'platformRef', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'platformRef', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'platformRef', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder platformRefIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformRef', + value: '', + )); + }); + } + + QueryBuilder + platformRefIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'platformRef', + value: '', + )); + }); + } +} + +extension GameDataQueryObject + on QueryBuilder {} + +extension GameDataQueryLinks + on QueryBuilder { + QueryBuilder launchConfig( + FilterQuery q) { + return QueryBuilder.apply(this, (query) { + return query.link(q, r'launchConfig'); + }); + } + + QueryBuilder launchConfigIsNull() { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'launchConfig', 0, true, 0, true); + }); + } + + QueryBuilder launchProcesses( + FilterQuery q) { + return QueryBuilder.apply(this, (query) { + return query.link(q, r'launchProcesses'); + }); + } + + QueryBuilder + launchProcessesLengthEqualTo(int length) { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'launchProcesses', length, true, length, true); + }); + } + + QueryBuilder + launchProcessesIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'launchProcesses', 0, true, 0, true); + }); + } + + QueryBuilder + launchProcessesIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'launchProcesses', 0, false, 999999, true); + }); + } + + QueryBuilder + launchProcessesLengthLessThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'launchProcesses', 0, true, length, include); + }); + } + + QueryBuilder + launchProcessesLengthGreaterThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.linkLength( + r'launchProcesses', length, include, 999999, true); + }); + } + + QueryBuilder + launchProcessesLengthBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.linkLength( + r'launchProcesses', lower, includeLower, upper, includeUpper); + }); + } +} + +extension GameDataQuerySortBy on QueryBuilder { + QueryBuilder sortByExeFile() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.asc); + }); + } + + QueryBuilder sortByExeFileDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.desc); + }); + } + + QueryBuilder sortByIconPath() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.asc); + }); + } + + QueryBuilder sortByIconPathDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.desc); + }); + } + + QueryBuilder sortByIconType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconType', Sort.asc); + }); + } + + QueryBuilder sortByIconTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconType', Sort.desc); + }); + } + + QueryBuilder sortByInstallDir() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.asc); + }); + } + + QueryBuilder sortByInstallDirDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.desc); + }); + } + + QueryBuilder sortByKey() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.asc); + }); + } + + QueryBuilder sortByKeyDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.desc); + }); + } + + QueryBuilder sortByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder sortByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } + + QueryBuilder sortByPlatformId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.asc); + }); + } + + QueryBuilder sortByPlatformIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.desc); + }); + } + + QueryBuilder sortByPlatformRef() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformRef', Sort.asc); + }); + } + + QueryBuilder sortByPlatformRefDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformRef', Sort.desc); + }); + } +} + +extension GameDataQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByExeFile() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.asc); + }); + } + + QueryBuilder thenByExeFileDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exeFile', Sort.desc); + }); + } + + QueryBuilder thenByIconPath() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.asc); + }); + } + + QueryBuilder thenByIconPathDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconPath', Sort.desc); + }); + } + + QueryBuilder thenByIconType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconType', Sort.asc); + }); + } + + QueryBuilder thenByIconTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'iconType', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByInstallDir() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.asc); + }); + } + + QueryBuilder thenByInstallDirDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'installDir', Sort.desc); + }); + } + + QueryBuilder thenByKey() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.asc); + }); + } + + QueryBuilder thenByKeyDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'key', Sort.desc); + }); + } + + QueryBuilder thenByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder thenByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } + + QueryBuilder thenByPlatformId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.asc); + }); + } + + QueryBuilder thenByPlatformIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.desc); + }); + } + + QueryBuilder thenByPlatformRef() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformRef', Sort.asc); + }); + } + + QueryBuilder thenByPlatformRefDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformRef', Sort.desc); + }); + } +} + +extension GameDataQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByExeFile( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'exeFile', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByIconPath( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'iconPath', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByIconType( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'iconType', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByInstallDir( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'installDir', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByKey( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'key', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByName( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'name', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByPlatformId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'platformId', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByPlatformRef( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'platformRef', caseSensitive: caseSensitive); + }); + } +} + +extension GameDataQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder exeFileProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'exeFile'); + }); + } + + QueryBuilder iconPathProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'iconPath'); + }); + } + + QueryBuilder iconTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'iconType'); + }); + } + + QueryBuilder installDirProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'installDir'); + }); + } + + QueryBuilder keyProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'key'); + }); + } + + QueryBuilder nameProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'name'); + }); + } + + QueryBuilder platformIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'platformId'); + }); + } + + QueryBuilder platformRefProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'platformRef'); + }); + } +} diff --git a/launchpad_app/lib/src/features/games/data/launch_config_data.dart b/launchpad_app/lib/src/features/games/data/launch_config_data.dart new file mode 100644 index 00000000..d71eeba6 --- /dev/null +++ b/launchpad_app/lib/src/features/games/data/launch_config_data.dart @@ -0,0 +1,35 @@ +import 'package:isar/isar.dart'; + +part 'launch_config_data.g.dart'; + +@collection +class LaunchConfigData { + Id? id = Isar.autoIncrement; + + String type = 'game'; + + String? gameId; + + String? gameKey; + + String? platformId; + + List? values; + + Map toConfigMap(List values) { + var configMap = {}; + + for (var value in values) { + configMap[value.key!] = value; + } + + return configMap; + } +} + +@embedded +class LaunchConfigValue { + String? key; + + String? value; +} diff --git a/launchpad_app/lib/src/features/games/data/launch_config_data.g.dart b/launchpad_app/lib/src/features/games/data/launch_config_data.g.dart new file mode 100644 index 00000000..e440e9a7 --- /dev/null +++ b/launchpad_app/lib/src/features/games/data/launch_config_data.g.dart @@ -0,0 +1,1656 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'launch_config_data.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetLaunchConfigDataCollection on Isar { + IsarCollection get launchConfigDatas => this.collection(); +} + +const LaunchConfigDataSchema = CollectionSchema( + name: r'LaunchConfigData', + id: -1166077371751590236, + properties: { + r'gameId': PropertySchema( + id: 0, + name: r'gameId', + type: IsarType.string, + ), + r'gameKey': PropertySchema( + id: 1, + name: r'gameKey', + type: IsarType.string, + ), + r'platformId': PropertySchema( + id: 2, + name: r'platformId', + type: IsarType.string, + ), + r'type': PropertySchema( + id: 3, + name: r'type', + type: IsarType.string, + ), + r'values': PropertySchema( + id: 4, + name: r'values', + type: IsarType.objectList, + target: r'LaunchConfigValue', + ) + }, + estimateSize: _launchConfigDataEstimateSize, + serialize: _launchConfigDataSerialize, + deserialize: _launchConfigDataDeserialize, + deserializeProp: _launchConfigDataDeserializeProp, + idName: r'id', + indexes: {}, + links: {}, + embeddedSchemas: {r'LaunchConfigValue': LaunchConfigValueSchema}, + getId: _launchConfigDataGetId, + getLinks: _launchConfigDataGetLinks, + attach: _launchConfigDataAttach, + version: '3.0.5', +); + +int _launchConfigDataEstimateSize( + LaunchConfigData object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.gameId; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.gameKey; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.platformId; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + bytesCount += 3 + object.type.length * 3; + { + final list = object.values; + if (list != null) { + bytesCount += 3 + list.length * 3; + { + final offsets = allOffsets[LaunchConfigValue]!; + for (var i = 0; i < list.length; i++) { + final value = list[i]; + bytesCount += + LaunchConfigValueSchema.estimateSize(value, offsets, allOffsets); + } + } + } + } + return bytesCount; +} + +void _launchConfigDataSerialize( + LaunchConfigData object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.gameId); + writer.writeString(offsets[1], object.gameKey); + writer.writeString(offsets[2], object.platformId); + writer.writeString(offsets[3], object.type); + writer.writeObjectList( + offsets[4], + allOffsets, + LaunchConfigValueSchema.serialize, + object.values, + ); +} + +LaunchConfigData _launchConfigDataDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LaunchConfigData(); + object.gameId = reader.readStringOrNull(offsets[0]); + object.gameKey = reader.readStringOrNull(offsets[1]); + object.id = id; + object.platformId = reader.readStringOrNull(offsets[2]); + object.type = reader.readString(offsets[3]); + object.values = reader.readObjectList( + offsets[4], + LaunchConfigValueSchema.deserialize, + allOffsets, + LaunchConfigValue(), + ); + return object; +} + +P _launchConfigDataDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + case 2: + return (reader.readStringOrNull(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + case 4: + return (reader.readObjectList( + offset, + LaunchConfigValueSchema.deserialize, + allOffsets, + LaunchConfigValue(), + )) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _launchConfigDataGetId(LaunchConfigData object) { + return object.id ?? Isar.autoIncrement; +} + +List> _launchConfigDataGetLinks(LaunchConfigData object) { + return []; +} + +void _launchConfigDataAttach( + IsarCollection col, Id id, LaunchConfigData object) { + object.id = id; +} + +extension LaunchConfigDataQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension LaunchConfigDataQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder + idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder + idGreaterThan(Id id, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder + idLessThan(Id id, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension LaunchConfigDataQueryFilter + on QueryBuilder { + QueryBuilder + gameIdIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'gameId', + )); + }); + } + + QueryBuilder + gameIdIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'gameId', + )); + }); + } + + QueryBuilder + gameIdEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'gameId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'gameId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'gameId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'gameId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'gameId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'gameId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'gameId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'gameId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'gameId', + value: '', + )); + }); + } + + QueryBuilder + gameIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'gameId', + value: '', + )); + }); + } + + QueryBuilder + gameKeyIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'gameKey', + )); + }); + } + + QueryBuilder + gameKeyIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'gameKey', + )); + }); + } + + QueryBuilder + gameKeyEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'gameKey', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'gameKey', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'gameKey', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'gameKey', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'gameKey', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'gameKey', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'gameKey', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'gameKey', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + gameKeyIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'gameKey', + value: '', + )); + }); + } + + QueryBuilder + gameKeyIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'gameKey', + value: '', + )); + }); + } + + QueryBuilder + idIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'id', + )); + }); + } + + QueryBuilder + idIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'id', + )); + }); + } + + QueryBuilder + idEqualTo(Id? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idGreaterThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idLessThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idBetween( + Id? lower, + Id? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + platformIdIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'platformId', + )); + }); + } + + QueryBuilder + platformIdIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'platformId', + )); + }); + } + + QueryBuilder + platformIdEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'platformId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'platformId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'platformId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + platformIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'platformId', + value: '', + )); + }); + } + + QueryBuilder + platformIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'platformId', + value: '', + )); + }); + } + + QueryBuilder + typeEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'type', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'type', + value: '', + )); + }); + } + + QueryBuilder + typeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'type', + value: '', + )); + }); + } + + QueryBuilder + valuesIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'values', + )); + }); + } + + QueryBuilder + valuesIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'values', + )); + }); + } + + QueryBuilder + valuesLengthEqualTo(int length) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'values', + length, + true, + length, + true, + ); + }); + } + + QueryBuilder + valuesIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'values', + 0, + true, + 0, + true, + ); + }); + } + + QueryBuilder + valuesIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'values', + 0, + false, + 999999, + true, + ); + }); + } + + QueryBuilder + valuesLengthLessThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'values', + 0, + true, + length, + include, + ); + }); + } + + QueryBuilder + valuesLengthGreaterThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'values', + length, + include, + 999999, + true, + ); + }); + } + + QueryBuilder + valuesLengthBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'values', + lower, + includeLower, + upper, + includeUpper, + ); + }); + } +} + +extension LaunchConfigDataQueryObject + on QueryBuilder { + QueryBuilder + valuesElement(FilterQuery q) { + return QueryBuilder.apply(this, (query) { + return query.object(q, r'values'); + }); + } +} + +extension LaunchConfigDataQueryLinks + on QueryBuilder {} + +extension LaunchConfigDataQuerySortBy + on QueryBuilder { + QueryBuilder + sortByGameId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameId', Sort.asc); + }); + } + + QueryBuilder + sortByGameIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameId', Sort.desc); + }); + } + + QueryBuilder + sortByGameKey() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameKey', Sort.asc); + }); + } + + QueryBuilder + sortByGameKeyDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameKey', Sort.desc); + }); + } + + QueryBuilder + sortByPlatformId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.asc); + }); + } + + QueryBuilder + sortByPlatformIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.desc); + }); + } + + QueryBuilder sortByType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.asc); + }); + } + + QueryBuilder + sortByTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.desc); + }); + } +} + +extension LaunchConfigDataQuerySortThenBy + on QueryBuilder { + QueryBuilder + thenByGameId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameId', Sort.asc); + }); + } + + QueryBuilder + thenByGameIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameId', Sort.desc); + }); + } + + QueryBuilder + thenByGameKey() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameKey', Sort.asc); + }); + } + + QueryBuilder + thenByGameKeyDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'gameKey', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder + thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder + thenByPlatformId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.asc); + }); + } + + QueryBuilder + thenByPlatformIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'platformId', Sort.desc); + }); + } + + QueryBuilder thenByType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.asc); + }); + } + + QueryBuilder + thenByTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.desc); + }); + } +} + +extension LaunchConfigDataQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByGameId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'gameId', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByGameKey( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'gameKey', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByPlatformId({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'platformId', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByType( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'type', caseSensitive: caseSensitive); + }); + } +} + +extension LaunchConfigDataQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder gameIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'gameId'); + }); + } + + QueryBuilder gameKeyProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'gameKey'); + }); + } + + QueryBuilder + platformIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'platformId'); + }); + } + + QueryBuilder typeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'type'); + }); + } + + QueryBuilder?, QQueryOperations> + valuesProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'values'); + }); + } +} + +// ************************************************************************** +// IsarEmbeddedGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +const LaunchConfigValueSchema = Schema( + name: r'LaunchConfigValue', + id: -3854823125406390924, + properties: { + r'key': PropertySchema( + id: 0, + name: r'key', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 1, + name: r'value', + type: IsarType.string, + ) + }, + estimateSize: _launchConfigValueEstimateSize, + serialize: _launchConfigValueSerialize, + deserialize: _launchConfigValueDeserialize, + deserializeProp: _launchConfigValueDeserializeProp, +); + +int _launchConfigValueEstimateSize( + LaunchConfigValue object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.key; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.value; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _launchConfigValueSerialize( + LaunchConfigValue object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.key); + writer.writeString(offsets[1], object.value); +} + +LaunchConfigValue _launchConfigValueDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LaunchConfigValue(); + object.key = reader.readStringOrNull(offsets[0]); + object.value = reader.readStringOrNull(offsets[1]); + return object; +} + +P _launchConfigValueDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +extension LaunchConfigValueQueryFilter + on QueryBuilder { + QueryBuilder + keyIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'key', + )); + }); + } + + QueryBuilder + keyIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'key', + )); + }); + } + + QueryBuilder + keyEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'key', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'key', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'key', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + keyIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'key', + value: '', + )); + }); + } + + QueryBuilder + keyIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'key', + value: '', + )); + }); + } + + QueryBuilder + valueIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'value', + )); + }); + } + + QueryBuilder + valueIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'value', + )); + }); + } + + QueryBuilder + valueEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'value', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'value', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + valueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'value', + value: '', + )); + }); + } +} + +extension LaunchConfigValueQueryObject + on QueryBuilder {} diff --git a/launchpad_app/lib/src/features/games/data/launch_process_data.dart b/launchpad_app/lib/src/features/games/data/launch_process_data.dart new file mode 100644 index 00000000..d11a06e2 --- /dev/null +++ b/launchpad_app/lib/src/features/games/data/launch_process_data.dart @@ -0,0 +1,29 @@ +import 'package:isar/isar.dart'; +import 'package:launchpad_app/src/features/games/data/game_data.dart'; + +part 'launch_process_data.g.dart'; + +@collection +class LaunchProcessData { + Id? id = Isar.autoIncrement; + + final game = IsarLink(); + + String? type; + + String? dir; + + String? exe; + + String? startCommand; + + String? startUri; + + String? startArgs; + + String? processId; + + String? processIdType; + + final childProcesses = IsarLinks(); +} diff --git a/launchpad_app/lib/src/features/games/data/launch_process_data.g.dart b/launchpad_app/lib/src/features/games/data/launch_process_data.g.dart new file mode 100644 index 00000000..2dde250b --- /dev/null +++ b/launchpad_app/lib/src/features/games/data/launch_process_data.g.dart @@ -0,0 +1,2054 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'launch_process_data.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetLaunchProcessDataCollection on Isar { + IsarCollection get launchProcessDatas => this.collection(); +} + +const LaunchProcessDataSchema = CollectionSchema( + name: r'LaunchProcessData', + id: -8203881271593144308, + properties: { + r'dir': PropertySchema( + id: 0, + name: r'dir', + type: IsarType.string, + ), + r'exe': PropertySchema( + id: 1, + name: r'exe', + type: IsarType.string, + ), + r'processId': PropertySchema( + id: 2, + name: r'processId', + type: IsarType.string, + ), + r'processIdType': PropertySchema( + id: 3, + name: r'processIdType', + type: IsarType.string, + ), + r'startArgs': PropertySchema( + id: 4, + name: r'startArgs', + type: IsarType.string, + ), + r'startCommand': PropertySchema( + id: 5, + name: r'startCommand', + type: IsarType.string, + ), + r'startUri': PropertySchema( + id: 6, + name: r'startUri', + type: IsarType.string, + ), + r'type': PropertySchema( + id: 7, + name: r'type', + type: IsarType.string, + ) + }, + estimateSize: _launchProcessDataEstimateSize, + serialize: _launchProcessDataSerialize, + deserialize: _launchProcessDataDeserialize, + deserializeProp: _launchProcessDataDeserializeProp, + idName: r'id', + indexes: {}, + links: { + r'game': LinkSchema( + id: 7299628713932688470, + name: r'game', + target: r'GameData', + single: true, + ), + r'childProcesses': LinkSchema( + id: 5523404633728566519, + name: r'childProcesses', + target: r'LaunchProcessData', + single: false, + ) + }, + embeddedSchemas: {}, + getId: _launchProcessDataGetId, + getLinks: _launchProcessDataGetLinks, + attach: _launchProcessDataAttach, + version: '3.0.5', +); + +int _launchProcessDataEstimateSize( + LaunchProcessData object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.dir; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.exe; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.processId; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.processIdType; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.startArgs; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.startCommand; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.startUri; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + { + final value = object.type; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _launchProcessDataSerialize( + LaunchProcessData object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.dir); + writer.writeString(offsets[1], object.exe); + writer.writeString(offsets[2], object.processId); + writer.writeString(offsets[3], object.processIdType); + writer.writeString(offsets[4], object.startArgs); + writer.writeString(offsets[5], object.startCommand); + writer.writeString(offsets[6], object.startUri); + writer.writeString(offsets[7], object.type); +} + +LaunchProcessData _launchProcessDataDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LaunchProcessData(); + object.dir = reader.readStringOrNull(offsets[0]); + object.exe = reader.readStringOrNull(offsets[1]); + object.id = id; + object.processId = reader.readStringOrNull(offsets[2]); + object.processIdType = reader.readStringOrNull(offsets[3]); + object.startArgs = reader.readStringOrNull(offsets[4]); + object.startCommand = reader.readStringOrNull(offsets[5]); + object.startUri = reader.readStringOrNull(offsets[6]); + object.type = reader.readStringOrNull(offsets[7]); + return object; +} + +P _launchProcessDataDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + case 2: + return (reader.readStringOrNull(offset)) as P; + case 3: + return (reader.readStringOrNull(offset)) as P; + case 4: + return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readStringOrNull(offset)) as P; + case 6: + return (reader.readStringOrNull(offset)) as P; + case 7: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _launchProcessDataGetId(LaunchProcessData object) { + return object.id ?? Isar.autoIncrement; +} + +List> _launchProcessDataGetLinks( + LaunchProcessData object) { + return [object.game, object.childProcesses]; +} + +void _launchProcessDataAttach( + IsarCollection col, Id id, LaunchProcessData object) { + object.id = id; + object.game.attach(col, col.isar.collection(), r'game', id); + object.childProcesses.attach( + col, col.isar.collection(), r'childProcesses', id); +} + +extension LaunchProcessDataQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension LaunchProcessDataQueryWhere + on QueryBuilder { + QueryBuilder + idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder + idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder + idGreaterThan(Id id, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder + idLessThan(Id id, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder + idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension LaunchProcessDataQueryFilter + on QueryBuilder { + QueryBuilder + dirIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'dir', + )); + }); + } + + QueryBuilder + dirIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'dir', + )); + }); + } + + QueryBuilder + dirEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'dir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'dir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'dir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'dir', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'dir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'dir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'dir', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'dir', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + dirIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'dir', + value: '', + )); + }); + } + + QueryBuilder + dirIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'dir', + value: '', + )); + }); + } + + QueryBuilder + exeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'exe', + )); + }); + } + + QueryBuilder + exeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'exe', + )); + }); + } + + QueryBuilder + exeEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'exe', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'exe', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'exe', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'exe', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'exe', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'exe', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'exe', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'exe', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + exeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'exe', + value: '', + )); + }); + } + + QueryBuilder + exeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'exe', + value: '', + )); + }); + } + + QueryBuilder + idIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'id', + )); + }); + } + + QueryBuilder + idIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'id', + )); + }); + } + + QueryBuilder + idEqualTo(Id? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idGreaterThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idLessThan( + Id? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idBetween( + Id? lower, + Id? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + processIdIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'processId', + )); + }); + } + + QueryBuilder + processIdIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'processId', + )); + }); + } + + QueryBuilder + processIdEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'processId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'processId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'processId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'processId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'processId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'processId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'processId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'processId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'processId', + value: '', + )); + }); + } + + QueryBuilder + processIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'processId', + value: '', + )); + }); + } + + QueryBuilder + processIdTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'processIdType', + )); + }); + } + + QueryBuilder + processIdTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'processIdType', + )); + }); + } + + QueryBuilder + processIdTypeEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'processIdType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'processIdType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'processIdType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'processIdType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'processIdType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'processIdType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'processIdType', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'processIdType', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + processIdTypeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'processIdType', + value: '', + )); + }); + } + + QueryBuilder + processIdTypeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'processIdType', + value: '', + )); + }); + } + + QueryBuilder + startArgsIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'startArgs', + )); + }); + } + + QueryBuilder + startArgsIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'startArgs', + )); + }); + } + + QueryBuilder + startArgsEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'startArgs', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'startArgs', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'startArgs', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'startArgs', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'startArgs', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'startArgs', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'startArgs', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'startArgs', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startArgsIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'startArgs', + value: '', + )); + }); + } + + QueryBuilder + startArgsIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'startArgs', + value: '', + )); + }); + } + + QueryBuilder + startCommandIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'startCommand', + )); + }); + } + + QueryBuilder + startCommandIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'startCommand', + )); + }); + } + + QueryBuilder + startCommandEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'startCommand', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'startCommand', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'startCommand', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'startCommand', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'startCommand', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'startCommand', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'startCommand', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'startCommand', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startCommandIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'startCommand', + value: '', + )); + }); + } + + QueryBuilder + startCommandIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'startCommand', + value: '', + )); + }); + } + + QueryBuilder + startUriIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'startUri', + )); + }); + } + + QueryBuilder + startUriIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'startUri', + )); + }); + } + + QueryBuilder + startUriEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'startUri', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'startUri', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'startUri', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'startUri', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'startUri', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'startUri', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'startUri', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'startUri', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + startUriIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'startUri', + value: '', + )); + }); + } + + QueryBuilder + startUriIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'startUri', + value: '', + )); + }); + } + + QueryBuilder + typeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'type', + )); + }); + } + + QueryBuilder + typeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'type', + )); + }); + } + + QueryBuilder + typeEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'type', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + typeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'type', + value: '', + )); + }); + } + + QueryBuilder + typeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'type', + value: '', + )); + }); + } +} + +extension LaunchProcessDataQueryObject + on QueryBuilder {} + +extension LaunchProcessDataQueryLinks + on QueryBuilder { + QueryBuilder + game(FilterQuery q) { + return QueryBuilder.apply(this, (query) { + return query.link(q, r'game'); + }); + } + + QueryBuilder + gameIsNull() { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'game', 0, true, 0, true); + }); + } + + QueryBuilder + childProcesses(FilterQuery q) { + return QueryBuilder.apply(this, (query) { + return query.link(q, r'childProcesses'); + }); + } + + QueryBuilder + childProcessesLengthEqualTo(int length) { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'childProcesses', length, true, length, true); + }); + } + + QueryBuilder + childProcessesIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'childProcesses', 0, true, 0, true); + }); + } + + QueryBuilder + childProcessesIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'childProcesses', 0, false, 999999, true); + }); + } + + QueryBuilder + childProcessesLengthLessThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'childProcesses', 0, true, length, include); + }); + } + + QueryBuilder + childProcessesLengthGreaterThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.linkLength(r'childProcesses', length, include, 999999, true); + }); + } + + QueryBuilder + childProcessesLengthBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.linkLength( + r'childProcesses', lower, includeLower, upper, includeUpper); + }); + } +} + +extension LaunchProcessDataQuerySortBy + on QueryBuilder { + QueryBuilder sortByDir() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'dir', Sort.asc); + }); + } + + QueryBuilder + sortByDirDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'dir', Sort.desc); + }); + } + + QueryBuilder sortByExe() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exe', Sort.asc); + }); + } + + QueryBuilder + sortByExeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exe', Sort.desc); + }); + } + + QueryBuilder + sortByProcessId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processId', Sort.asc); + }); + } + + QueryBuilder + sortByProcessIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processId', Sort.desc); + }); + } + + QueryBuilder + sortByProcessIdType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processIdType', Sort.asc); + }); + } + + QueryBuilder + sortByProcessIdTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processIdType', Sort.desc); + }); + } + + QueryBuilder + sortByStartArgs() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startArgs', Sort.asc); + }); + } + + QueryBuilder + sortByStartArgsDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startArgs', Sort.desc); + }); + } + + QueryBuilder + sortByStartCommand() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startCommand', Sort.asc); + }); + } + + QueryBuilder + sortByStartCommandDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startCommand', Sort.desc); + }); + } + + QueryBuilder + sortByStartUri() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startUri', Sort.asc); + }); + } + + QueryBuilder + sortByStartUriDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startUri', Sort.desc); + }); + } + + QueryBuilder + sortByType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.asc); + }); + } + + QueryBuilder + sortByTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.desc); + }); + } +} + +extension LaunchProcessDataQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByDir() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'dir', Sort.asc); + }); + } + + QueryBuilder + thenByDirDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'dir', Sort.desc); + }); + } + + QueryBuilder thenByExe() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exe', Sort.asc); + }); + } + + QueryBuilder + thenByExeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'exe', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder + thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder + thenByProcessId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processId', Sort.asc); + }); + } + + QueryBuilder + thenByProcessIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processId', Sort.desc); + }); + } + + QueryBuilder + thenByProcessIdType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processIdType', Sort.asc); + }); + } + + QueryBuilder + thenByProcessIdTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'processIdType', Sort.desc); + }); + } + + QueryBuilder + thenByStartArgs() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startArgs', Sort.asc); + }); + } + + QueryBuilder + thenByStartArgsDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startArgs', Sort.desc); + }); + } + + QueryBuilder + thenByStartCommand() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startCommand', Sort.asc); + }); + } + + QueryBuilder + thenByStartCommandDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startCommand', Sort.desc); + }); + } + + QueryBuilder + thenByStartUri() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startUri', Sort.asc); + }); + } + + QueryBuilder + thenByStartUriDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'startUri', Sort.desc); + }); + } + + QueryBuilder + thenByType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.asc); + }); + } + + QueryBuilder + thenByTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.desc); + }); + } +} + +extension LaunchProcessDataQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByDir( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'dir', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByExe( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'exe', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByProcessId({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'processId', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByProcessIdType({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'processIdType', + caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByStartArgs({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'startArgs', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByStartCommand({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'startCommand', caseSensitive: caseSensitive); + }); + } + + QueryBuilder + distinctByStartUri({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'startUri', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByType( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'type', caseSensitive: caseSensitive); + }); + } +} + +extension LaunchProcessDataQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder dirProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'dir'); + }); + } + + QueryBuilder exeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'exe'); + }); + } + + QueryBuilder + processIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'processId'); + }); + } + + QueryBuilder + processIdTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'processIdType'); + }); + } + + QueryBuilder + startArgsProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'startArgs'); + }); + } + + QueryBuilder + startCommandProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'startCommand'); + }); + } + + QueryBuilder + startUriProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'startUri'); + }); + } + + QueryBuilder typeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'type'); + }); + } +} diff --git a/launchpad_app/lib/src/features/games/domain/game.dart b/launchpad_app/lib/src/features/games/domain/game.dart new file mode 100644 index 00000000..ef2429b2 --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/game.dart @@ -0,0 +1,21 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'game.freezed.dart'; + +part 'game.g.dart'; + +@freezed +class Game with _$Game { + const Game._(); + + const factory Game({ + required String key, + required String name, + required String platformId, + required String installDir, + String? platformRef, + String? exeFile, + }) = _Game; + + factory Game.fromJson(Map json) => _$GameFromJson(json); +} diff --git a/launchpad_app/lib/src/features/games/domain/game.freezed.dart b/launchpad_app/lib/src/features/games/domain/game.freezed.dart new file mode 100644 index 00000000..83b627c2 --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/game.freezed.dart @@ -0,0 +1,252 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'game.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +Game _$GameFromJson(Map json) { + return _Game.fromJson(json); +} + +/// @nodoc +mixin _$Game { + String get key => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get platformId => throw _privateConstructorUsedError; + String get installDir => throw _privateConstructorUsedError; + String? get platformRef => throw _privateConstructorUsedError; + String? get exeFile => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $GameCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GameCopyWith<$Res> { + factory $GameCopyWith(Game value, $Res Function(Game) then) = + _$GameCopyWithImpl<$Res, Game>; + @useResult + $Res call( + {String key, + String name, + String platformId, + String installDir, + String? platformRef, + String? exeFile}); +} + +/// @nodoc +class _$GameCopyWithImpl<$Res, $Val extends Game> + implements $GameCopyWith<$Res> { + _$GameCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? platformId = null, + Object? installDir = null, + Object? platformRef = freezed, + Object? exeFile = freezed, + }) { + return _then(_value.copyWith( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + platformId: null == platformId + ? _value.platformId + : platformId // ignore: cast_nullable_to_non_nullable + as String, + installDir: null == installDir + ? _value.installDir + : installDir // ignore: cast_nullable_to_non_nullable + as String, + platformRef: freezed == platformRef + ? _value.platformRef + : platformRef // ignore: cast_nullable_to_non_nullable + as String?, + exeFile: freezed == exeFile + ? _value.exeFile + : exeFile // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_GameCopyWith<$Res> implements $GameCopyWith<$Res> { + factory _$$_GameCopyWith(_$_Game value, $Res Function(_$_Game) then) = + __$$_GameCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String key, + String name, + String platformId, + String installDir, + String? platformRef, + String? exeFile}); +} + +/// @nodoc +class __$$_GameCopyWithImpl<$Res> extends _$GameCopyWithImpl<$Res, _$_Game> + implements _$$_GameCopyWith<$Res> { + __$$_GameCopyWithImpl(_$_Game _value, $Res Function(_$_Game) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? platformId = null, + Object? installDir = null, + Object? platformRef = freezed, + Object? exeFile = freezed, + }) { + return _then(_$_Game( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + platformId: null == platformId + ? _value.platformId + : platformId // ignore: cast_nullable_to_non_nullable + as String, + installDir: null == installDir + ? _value.installDir + : installDir // ignore: cast_nullable_to_non_nullable + as String, + platformRef: freezed == platformRef + ? _value.platformRef + : platformRef // ignore: cast_nullable_to_non_nullable + as String?, + exeFile: freezed == exeFile + ? _value.exeFile + : exeFile // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_Game extends _Game { + const _$_Game( + {required this.key, + required this.name, + required this.platformId, + required this.installDir, + this.platformRef, + this.exeFile}) + : super._(); + + factory _$_Game.fromJson(Map json) => _$$_GameFromJson(json); + + @override + final String key; + @override + final String name; + @override + final String platformId; + @override + final String installDir; + @override + final String? platformRef; + @override + final String? exeFile; + + @override + String toString() { + return 'Game(key: $key, name: $name, platformId: $platformId, installDir: $installDir, platformRef: $platformRef, exeFile: $exeFile)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_Game && + (identical(other.key, key) || other.key == key) && + (identical(other.name, name) || other.name == name) && + (identical(other.platformId, platformId) || + other.platformId == platformId) && + (identical(other.installDir, installDir) || + other.installDir == installDir) && + (identical(other.platformRef, platformRef) || + other.platformRef == platformRef) && + (identical(other.exeFile, exeFile) || other.exeFile == exeFile)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, key, name, platformId, installDir, platformRef, exeFile); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_GameCopyWith<_$_Game> get copyWith => + __$$_GameCopyWithImpl<_$_Game>(this, _$identity); + + @override + Map toJson() { + return _$$_GameToJson( + this, + ); + } +} + +abstract class _Game extends Game { + const factory _Game( + {required final String key, + required final String name, + required final String platformId, + required final String installDir, + final String? platformRef, + final String? exeFile}) = _$_Game; + const _Game._() : super._(); + + factory _Game.fromJson(Map json) = _$_Game.fromJson; + + @override + String get key; + @override + String get name; + @override + String get platformId; + @override + String get installDir; + @override + String? get platformRef; + @override + String? get exeFile; + @override + @JsonKey(ignore: true) + _$$_GameCopyWith<_$_Game> get copyWith => throw _privateConstructorUsedError; +} diff --git a/launchpad_app/lib/src/features/games/domain/game.g.dart b/launchpad_app/lib/src/features/games/domain/game.g.dart new file mode 100644 index 00000000..a9ab7eff --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/game.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_Game _$$_GameFromJson(Map json) => _$_Game( + key: json['key'] as String, + name: json['name'] as String, + platformId: json['platformId'] as String, + installDir: json['installDir'] as String, + platformRef: json['platformRef'] as String?, + exeFile: json['exeFile'] as String?, + ); + +Map _$$_GameToJson(_$_Game instance) => { + 'key': instance.key, + 'name': instance.name, + 'platformId': instance.platformId, + 'installDir': instance.installDir, + 'platformRef': instance.platformRef, + 'exeFile': instance.exeFile, + }; diff --git a/launchpad_app/lib/src/features/games/domain/launch_config.dart b/launchpad_app/lib/src/features/games/domain/launch_config.dart new file mode 100644 index 00000000..5796b39f --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/launch_config.dart @@ -0,0 +1,23 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; + +import '../data/launch_config_data.dart'; + +part 'launch_config.freezed.dart'; +part 'launch_config.g.dart'; + +@freezed +class LaunchConfig with _$LaunchConfig { + const LaunchConfig._(); + + const factory LaunchConfig({ + required String type, + required String gameId, + required String gameKey, + required String platformId, + required Map values, + }) = _LaunchConfig; + + factory LaunchConfig.fromJson(Map json) => + _$LaunchConfigFromJson(json); +} diff --git a/launchpad_app/lib/src/features/games/domain/launch_config.freezed.dart b/launchpad_app/lib/src/features/games/domain/launch_config.freezed.dart new file mode 100644 index 00000000..83829e22 --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/launch_config.freezed.dart @@ -0,0 +1,257 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'launch_config.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +LaunchConfig _$LaunchConfigFromJson(Map json) { + return _LaunchConfig.fromJson(json); +} + +/// @nodoc +mixin _$LaunchConfig { + String get type => throw _privateConstructorUsedError; + String get gameId => throw _privateConstructorUsedError; + String get gameKey => throw _privateConstructorUsedError; + String get platformId => throw _privateConstructorUsedError; + Map get values => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $LaunchConfigCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LaunchConfigCopyWith<$Res> { + factory $LaunchConfigCopyWith( + LaunchConfig value, $Res Function(LaunchConfig) then) = + _$LaunchConfigCopyWithImpl<$Res, LaunchConfig>; + @useResult + $Res call( + {String type, + String gameId, + String gameKey, + String platformId, + Map values}); +} + +/// @nodoc +class _$LaunchConfigCopyWithImpl<$Res, $Val extends LaunchConfig> + implements $LaunchConfigCopyWith<$Res> { + _$LaunchConfigCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? type = null, + Object? gameId = null, + Object? gameKey = null, + Object? platformId = null, + Object? values = null, + }) { + return _then(_value.copyWith( + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String, + gameId: null == gameId + ? _value.gameId + : gameId // ignore: cast_nullable_to_non_nullable + as String, + gameKey: null == gameKey + ? _value.gameKey + : gameKey // ignore: cast_nullable_to_non_nullable + as String, + platformId: null == platformId + ? _value.platformId + : platformId // ignore: cast_nullable_to_non_nullable + as String, + values: null == values + ? _value.values + : values // ignore: cast_nullable_to_non_nullable + as Map, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_LaunchConfigCopyWith<$Res> + implements $LaunchConfigCopyWith<$Res> { + factory _$$_LaunchConfigCopyWith( + _$_LaunchConfig value, $Res Function(_$_LaunchConfig) then) = + __$$_LaunchConfigCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String type, + String gameId, + String gameKey, + String platformId, + Map values}); +} + +/// @nodoc +class __$$_LaunchConfigCopyWithImpl<$Res> + extends _$LaunchConfigCopyWithImpl<$Res, _$_LaunchConfig> + implements _$$_LaunchConfigCopyWith<$Res> { + __$$_LaunchConfigCopyWithImpl( + _$_LaunchConfig _value, $Res Function(_$_LaunchConfig) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? type = null, + Object? gameId = null, + Object? gameKey = null, + Object? platformId = null, + Object? values = null, + }) { + return _then(_$_LaunchConfig( + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String, + gameId: null == gameId + ? _value.gameId + : gameId // ignore: cast_nullable_to_non_nullable + as String, + gameKey: null == gameKey + ? _value.gameKey + : gameKey // ignore: cast_nullable_to_non_nullable + as String, + platformId: null == platformId + ? _value.platformId + : platformId // ignore: cast_nullable_to_non_nullable + as String, + values: null == values + ? _value._values + : values // ignore: cast_nullable_to_non_nullable + as Map, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_LaunchConfig extends _LaunchConfig with DiagnosticableTreeMixin { + const _$_LaunchConfig( + {required this.type, + required this.gameId, + required this.gameKey, + required this.platformId, + required final Map values}) + : _values = values, + super._(); + + factory _$_LaunchConfig.fromJson(Map json) => + _$$_LaunchConfigFromJson(json); + + @override + final String type; + @override + final String gameId; + @override + final String gameKey; + @override + final String platformId; + final Map _values; + @override + Map get values { + if (_values is EqualUnmodifiableMapView) return _values; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_values); + } + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'LaunchConfig(type: $type, gameId: $gameId, gameKey: $gameKey, platformId: $platformId, values: $values)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'LaunchConfig')) + ..add(DiagnosticsProperty('type', type)) + ..add(DiagnosticsProperty('gameId', gameId)) + ..add(DiagnosticsProperty('gameKey', gameKey)) + ..add(DiagnosticsProperty('platformId', platformId)) + ..add(DiagnosticsProperty('values', values)); + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_LaunchConfig && + (identical(other.type, type) || other.type == type) && + (identical(other.gameId, gameId) || other.gameId == gameId) && + (identical(other.gameKey, gameKey) || other.gameKey == gameKey) && + (identical(other.platformId, platformId) || + other.platformId == platformId) && + const DeepCollectionEquality().equals(other._values, _values)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, type, gameId, gameKey, + platformId, const DeepCollectionEquality().hash(_values)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_LaunchConfigCopyWith<_$_LaunchConfig> get copyWith => + __$$_LaunchConfigCopyWithImpl<_$_LaunchConfig>(this, _$identity); + + @override + Map toJson() { + return _$$_LaunchConfigToJson( + this, + ); + } +} + +abstract class _LaunchConfig extends LaunchConfig { + const factory _LaunchConfig( + {required final String type, + required final String gameId, + required final String gameKey, + required final String platformId, + required final Map values}) = _$_LaunchConfig; + const _LaunchConfig._() : super._(); + + factory _LaunchConfig.fromJson(Map json) = + _$_LaunchConfig.fromJson; + + @override + String get type; + @override + String get gameId; + @override + String get gameKey; + @override + String get platformId; + @override + Map get values; + @override + @JsonKey(ignore: true) + _$$_LaunchConfigCopyWith<_$_LaunchConfig> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/launchpad_app/lib/src/features/games/domain/launch_config.g.dart b/launchpad_app/lib/src/features/games/domain/launch_config.g.dart new file mode 100644 index 00000000..d96bf111 --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/launch_config.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'launch_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_LaunchConfig _$$_LaunchConfigFromJson(Map json) => + _$_LaunchConfig( + type: json['type'] as String, + gameId: json['gameId'] as String, + gameKey: json['gameKey'] as String, + platformId: json['platformId'] as String, + values: json['values'] as Map, + ); + +Map _$$_LaunchConfigToJson(_$_LaunchConfig instance) => + { + 'type': instance.type, + 'gameId': instance.gameId, + 'gameKey': instance.gameKey, + 'platformId': instance.platformId, + 'values': instance.values, + }; diff --git a/launchpad_app/lib/src/features/games/domain/launch_process.dart b/launchpad_app/lib/src/features/games/domain/launch_process.dart new file mode 100644 index 00000000..17115963 --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/launch_process.dart @@ -0,0 +1,20 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; +import 'package:launchpad_app/src/features/games/domain/launch_config.dart'; + +part 'launch_process.freezed.dart'; + +part 'launch_process.g.dart'; + +@freezed +class LaunchProcess with _$LaunchProcess { + const LaunchProcess._(); + + const factory LaunchProcess({ + required String processType, + required LaunchConfig launchConfig, + }) = _LaunchProcess; + + factory LaunchProcess.fromJson(Map json) => + _$LaunchProcessFromJson(json); +} diff --git a/launchpad_app/lib/src/features/games/domain/launch_process.freezed.dart b/launchpad_app/lib/src/features/games/domain/launch_process.freezed.dart new file mode 100644 index 00000000..766a3754 --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/launch_process.freezed.dart @@ -0,0 +1,196 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'launch_process.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +LaunchProcess _$LaunchProcessFromJson(Map json) { + return _LaunchProcess.fromJson(json); +} + +/// @nodoc +mixin _$LaunchProcess { + String get processType => throw _privateConstructorUsedError; + LaunchConfig get launchConfig => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $LaunchProcessCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LaunchProcessCopyWith<$Res> { + factory $LaunchProcessCopyWith( + LaunchProcess value, $Res Function(LaunchProcess) then) = + _$LaunchProcessCopyWithImpl<$Res, LaunchProcess>; + @useResult + $Res call({String processType, LaunchConfig launchConfig}); + + $LaunchConfigCopyWith<$Res> get launchConfig; +} + +/// @nodoc +class _$LaunchProcessCopyWithImpl<$Res, $Val extends LaunchProcess> + implements $LaunchProcessCopyWith<$Res> { + _$LaunchProcessCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? processType = null, + Object? launchConfig = null, + }) { + return _then(_value.copyWith( + processType: null == processType + ? _value.processType + : processType // ignore: cast_nullable_to_non_nullable + as String, + launchConfig: null == launchConfig + ? _value.launchConfig + : launchConfig // ignore: cast_nullable_to_non_nullable + as LaunchConfig, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $LaunchConfigCopyWith<$Res> get launchConfig { + return $LaunchConfigCopyWith<$Res>(_value.launchConfig, (value) { + return _then(_value.copyWith(launchConfig: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$_LaunchProcessCopyWith<$Res> + implements $LaunchProcessCopyWith<$Res> { + factory _$$_LaunchProcessCopyWith( + _$_LaunchProcess value, $Res Function(_$_LaunchProcess) then) = + __$$_LaunchProcessCopyWithImpl<$Res>; + @override + @useResult + $Res call({String processType, LaunchConfig launchConfig}); + + @override + $LaunchConfigCopyWith<$Res> get launchConfig; +} + +/// @nodoc +class __$$_LaunchProcessCopyWithImpl<$Res> + extends _$LaunchProcessCopyWithImpl<$Res, _$_LaunchProcess> + implements _$$_LaunchProcessCopyWith<$Res> { + __$$_LaunchProcessCopyWithImpl( + _$_LaunchProcess _value, $Res Function(_$_LaunchProcess) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? processType = null, + Object? launchConfig = null, + }) { + return _then(_$_LaunchProcess( + processType: null == processType + ? _value.processType + : processType // ignore: cast_nullable_to_non_nullable + as String, + launchConfig: null == launchConfig + ? _value.launchConfig + : launchConfig // ignore: cast_nullable_to_non_nullable + as LaunchConfig, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_LaunchProcess extends _LaunchProcess with DiagnosticableTreeMixin { + const _$_LaunchProcess( + {required this.processType, required this.launchConfig}) + : super._(); + + factory _$_LaunchProcess.fromJson(Map json) => + _$$_LaunchProcessFromJson(json); + + @override + final String processType; + @override + final LaunchConfig launchConfig; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'LaunchProcess(processType: $processType, launchConfig: $launchConfig)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'LaunchProcess')) + ..add(DiagnosticsProperty('processType', processType)) + ..add(DiagnosticsProperty('launchConfig', launchConfig)); + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_LaunchProcess && + (identical(other.processType, processType) || + other.processType == processType) && + (identical(other.launchConfig, launchConfig) || + other.launchConfig == launchConfig)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, processType, launchConfig); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_LaunchProcessCopyWith<_$_LaunchProcess> get copyWith => + __$$_LaunchProcessCopyWithImpl<_$_LaunchProcess>(this, _$identity); + + @override + Map toJson() { + return _$$_LaunchProcessToJson( + this, + ); + } +} + +abstract class _LaunchProcess extends LaunchProcess { + const factory _LaunchProcess( + {required final String processType, + required final LaunchConfig launchConfig}) = _$_LaunchProcess; + const _LaunchProcess._() : super._(); + + factory _LaunchProcess.fromJson(Map json) = + _$_LaunchProcess.fromJson; + + @override + String get processType; + @override + LaunchConfig get launchConfig; + @override + @JsonKey(ignore: true) + _$$_LaunchProcessCopyWith<_$_LaunchProcess> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/launchpad_app/lib/src/features/games/domain/launch_process.g.dart b/launchpad_app/lib/src/features/games/domain/launch_process.g.dart new file mode 100644 index 00000000..a336cf5f --- /dev/null +++ b/launchpad_app/lib/src/features/games/domain/launch_process.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'launch_process.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_LaunchProcess _$$_LaunchProcessFromJson(Map json) => + _$_LaunchProcess( + processType: json['processType'] as String, + launchConfig: + LaunchConfig.fromJson(json['launchConfig'] as Map), + ); + +Map _$$_LaunchProcessToJson(_$_LaunchProcess instance) => + { + 'processType': instance.processType, + 'launchConfig': instance.launchConfig, + }; diff --git a/launchpad_app/lib/src/features/games/presentation/all_games.dart b/launchpad_app/lib/src/features/games/presentation/all_games.dart new file mode 100644 index 00000000..eae6793a --- /dev/null +++ b/launchpad_app/lib/src/features/games/presentation/all_games.dart @@ -0,0 +1,47 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/games/domain/game.dart'; +import 'package:launchpad_app/src/features/games/presentation/game_listing.dart'; + +import '../../../common_widgets/page.dart'; + +class AllGamesPage extends StatefulHookConsumerWidget { + const AllGamesPage({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _AllGamesState(); +} + +class _AllGamesState extends ConsumerState with PageMixin { + bool selected = true; + + @override + void initState() { + super.initState(); + } + + @override + build(BuildContext context) { + assert(debugCheckHasFluentTheme(context)); + final List games = [ + for (var i = 0; i < 100; i++) + const Game( + key: 'Overwatch', + name: 'Overwatch 2', + installDir: 'C:\\Program Files (x86)\\Overwatch', + platformId: 'blizzard', + exeFile: 'Overwatch.exe', + platformRef: 'pro', + ), + ]; + + return ScrollablePage( + header: const PageHeader( + title: Text("All Games"), + ), + children: [ + GameListing(games), + ], + ); + } +} diff --git a/launchpad_app/lib/src/features/games/presentation/game_listing.dart b/launchpad_app/lib/src/features/games/presentation/game_listing.dart new file mode 100644 index 00000000..9088f1de --- /dev/null +++ b/launchpad_app/lib/src/features/games/presentation/game_listing.dart @@ -0,0 +1,166 @@ +import 'dart:io'; + +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/game_platform_types.dart'; +import 'package:launchpad_app/src/features/games/domain/game.dart'; +import 'package:launchpad_app/src/utils/theme_provider.dart'; + +class GameListing extends StatefulHookConsumerWidget { + const GameListing( + this.games, { + Key? key, + }) : super(key: key); + + final List games; + + @override + ConsumerState createState() => _GameListing(); +} + +class _GameListing extends ConsumerState { + bool selected = true; + + @override + void initState() { + super.initState(); + } + + @override + build(BuildContext context) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + var spacing = 16.0; + var spacingWidth = spacing * 1.5; + var minWidth = 75.0; + var maxWidth = 200.0; + var minItemsPerRow = 3; + var maxContainerWidth = (constraints.maxWidth + spacingWidth - 1); + var itemsPerRow = maxContainerWidth ~/ (maxWidth + spacingWidth); + var itemWidth = + (maxContainerWidth - (itemsPerRow * spacingWidth)) / (itemsPerRow); + var textSize = 16.0; + var minTextSize = 13.0; + + if (itemWidth > maxWidth) { + itemsPerRow = itemsPerRow + 1; + itemWidth = + (maxContainerWidth - (itemsPerRow * spacingWidth)) / itemsPerRow; + } + + if (itemsPerRow < minItemsPerRow) { + itemsPerRow = minItemsPerRow; + itemWidth = + (maxContainerWidth - (itemsPerRow * spacingWidth)) / itemsPerRow; + } + + if (itemWidth > maxWidth) { + itemWidth = maxWidth; + itemsPerRow = maxContainerWidth ~/ (itemWidth + spacingWidth); + } + + var multiplier = itemWidth / maxWidth; + textSize = textSize * multiplier; + + if (textSize < minTextSize) { + textSize = minTextSize; + } + + return Wrap( + spacing: spacingWidth, + runSpacing: spacing, + children: [ + for (var game in widget.games) + PackshotGameListingItem(game, itemWidth, textSize: textSize), + ], + ); + }); + } +} + +class PackshotGameListingItem extends HookConsumerWidget { + const PackshotGameListingItem( + this.game, + this.width, { + Key? key, + this.textSize = 16, + }) : super(key: key); + + final Game game; + final double width; + final double textSize; + + @override + Widget build(BuildContext context, WidgetRef ref) { + var theme = ref.watch(appThemeProvider); + var platformTypes = ref.watch(gamePlatformTypesProvider); + + var platformType = (platformTypes.hasValue && platformTypes.value != null) + ? platformTypes.value![game.platformId] + : null; + + var platformIcon = + (platformType != null) ? platformType.locateIcon() : Future.value(null); + + var platformName = + (platformType != null) ? platformType.name : game.platformId; + + return FutureBuilder( + future: platformIcon, + builder: (context, snapshot) { + return Container( + width: width, + child: AspectRatio( + aspectRatio: 9 / 14, + child: Container( + color: theme.color.darker, + child: Stack( + children: [ + Positioned.fill( + child: Image.file( + File( + 'E:/Dev/Apps/Launchpad/Launchpad - Assets/Games/Overwatch/background_art.png'), + fit: BoxFit.cover, + ), + ), + Positioned.fill( + child: Container( + color: Colors.black.withOpacity(0.3), + ), + ), + Positioned.fill( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + game.name, + style: TextStyle( + fontSize: textSize, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + color: Colors.black.withOpacity(0.8), + offset: const Offset(1, 1), + blurRadius: 2, + ), + ], + ), + ), + const SizedBox(height: 8), + if (snapshot.hasData && snapshot.data != null) + snapshot.data!, + ], + ), + ), + ), + ], + ), + ), + ), + ); + }); + } +} diff --git a/launchpad_app/lib/src/features/main_window/data/persisted_state_storage.dart b/launchpad_app/lib/src/features/main_window/data/persisted_state_storage.dart new file mode 100644 index 00000000..c0df1fbc --- /dev/null +++ b/launchpad_app/lib/src/features/main_window/data/persisted_state_storage.dart @@ -0,0 +1,22 @@ +import 'package:hive/hive.dart'; +import 'package:state_persistence/state_persistence.dart'; + +class HiveStateStorage extends PersistedStateStorage { + var box = Hive.openBox("persisted_state"); + + @override + Future clear() { + return box.then((openBox) => openBox.put("state", {})); + } + + @override + Future?> load() { + return box.then( + (openBox) => openBox.get("state", defaultValue: {})); + } + + @override + Future save(Map? data) { + return box.then((openBox) => openBox.put("state", data)); + } +} diff --git a/launchpad_app/lib/src/features/main_window/presentation/home_container.dart b/launchpad_app/lib/src/features/main_window/presentation/home_container.dart new file mode 100644 index 00000000..0f90b3b9 --- /dev/null +++ b/launchpad_app/lib/src/features/main_window/presentation/home_container.dart @@ -0,0 +1,410 @@ +import 'dart:convert'; + +import 'package:fluent_ui/fluent_ui.dart' hide Page; +import 'package:flutter/foundation.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:http/http.dart' as http; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/gen/assets.gen.dart'; +import 'package:launchpad_app/src/features/dashboard/presentation/dashboard.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/game_platform_types.dart'; +import 'package:launchpad_app/src/features/game_platforms/application/game_platforms.dart'; +import 'package:launchpad_app/src/features/games/presentation/all_games.dart'; +import 'package:launchpad_app/src/features/main_window/presentation/main_drop_target.dart'; +import 'package:launchpad_app/src/utils/globals.dart'; +import 'package:launchpad_app/src/utils/theme_provider.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path/path.dart' as p; +import 'package:platform_info/platform_info.dart'; +import 'package:protocol_handler/protocol_handler.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:updat/updat_window_manager.dart'; +import 'package:url_launcher/link.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../../settings/presentation/settings.dart'; + +import '../../../utils/theme.dart'; + +part 'home_container.g.dart'; + +@riverpod +Future> paneItems(PaneItemsRef ref) async { + var platforms = ref.watch(gamePlatformsProvider); + var platformTypes = ref.watch(gamePlatformTypesProvider); + + Future> getPlatformItems() async { + var list = []; + + if (platforms.hasValue && platformTypes.hasValue) { + for (var platform in platforms.value!) { + var platformType = platformTypes.value![platform.platformTypeId]; + var icon = + (await platformType?.locateIcon()) ?? const Icon(FluentIcons.game); + + list.add(PaneItem( + icon: icon, + title: Text(platform.name), + body: const Text('Show manage list for platform entities.'), + )); + } + } + + return list; + } + + return [ + PaneItem( + icon: const Icon(FluentIcons.home), + title: const Text('Dashboard'), + body: const DashboardPage(), + ), + PaneItemHeader(header: const Text("Games")), + // TODO Add links to the most recent 5 games edited, launched, or built + PaneItem( + icon: const Icon(FluentIcons.game), + title: const Text('All Games'), + body: const AllGamesPage(), + ), + PaneItem( + icon: const Icon(FluentIcons.add), + title: const Text('Add Game'), + body: const Text('To be replaced with a page widget.'), + ), + PaneItem( + icon: const Icon(FluentIcons.search), + title: const Text('Find Installed Games'), + body: const Text('To be replaced with a page widget.'), + ), + PaneItemHeader(header: const Text("Game Platforms")), + ...(await getPlatformItems()), + PaneItem( + icon: const Icon(FluentIcons.library), + title: const Text('All Platforms'), + body: const Text('Show manage list for platform entities.'), + ), + // PaneItemHeader(header: const Icon(FluentIcons.archive)), + // PaneItem( + // icon: const Icon(FluentIcons.lifesaver), + // title: const Text('All Backups'), + // body: const Text('.'), + // ), + // PaneItem( + // icon: const Icon(FluentIcons.archive), + // title: const Text('Add Backup'), + // body: const Text('.'), + // ), + // PaneItemHeader(header: const Text("Web Services")), + // // TODO Add links to all enabled web services here + // PaneItem( + // icon: const Icon(FluentIcons.globe), + // title: const Text('All Web Services'), + // body: const Text('Show manage list for platform entities.'), + // ), + // PaneItem( + // icon: const Icon(FluentIcons.o_d_link), + // title: const Text('Add Web Service'), + // body: const Text('.'), + // ), + // PaneItemHeader(header: const Icon(FluentIcons.puzzle)), + // PaneItem( + // icon: const Icon(FluentIcons.puzzle), + // title: const Text('Modules'), + // body: const Text('.'), + // ), + // PaneItem( + // icon: const Icon(FluentIcons.download), + // title: const Text('Extend Launchpad'), + // body: const Text('.'), + // ), + ]; +} + +class HomeContainer extends StatefulHookConsumerWidget { + const HomeContainer({Key? key}) : super(key: key); + + @override + createState() => _HomeContainerState(); +} + +class _HomeContainerState extends ConsumerState + with WindowListener, ProtocolListener { + var value = false; + + var index = 0; + + final viewKey = GlobalKey(); + + final searchKey = GlobalKey(); + final searchFocusNode = FocusNode(); + final searchController = TextEditingController(); + + @override + dispose() { + protocolHandler.removeListener(this); + windowManager.removeListener(this); + searchController.dispose(); + searchFocusNode.dispose(); + super.dispose(); + } + + @override + initState() { + protocolHandler.addListener(this); + windowManager.addListener(this); + WidgetsFlutterBinding.ensureInitialized(); + + super.initState(); + } + + Future _getPackageInfo() { + return PackageInfo.fromPlatform(); + } + + @override + void onProtocolUrlReceived(String url) { + // TODO Handle protocol response + } + + @override + build(BuildContext context) { + final appTheme = ref.watch(appThemeProvider); + final originalItems = ref.watch(paneItemsProvider); + final footerItems = [ + PaneItemSeparator(), + PaneItem( + icon: const Icon(FluentIcons.settings), + title: const Text('Settings'), + body: Settings(), + ), + PaneItem( + icon: const Icon(FluentIcons.help), + title: const Text('Help'), + body: const Text('To be replaced with a page widget.'), + ), + _LinkPaneItemAction( + icon: const Icon(FluentIcons.open_source), + title: const Text('Source code'), + link: 'https://github.com/VolantisDev/Launchpad/tree/flutter', + body: const SizedBox.shrink(), + ), + ]; + + return FutureBuilder( + future: _getPackageInfo(), + builder: (context, snapshot) { + if (snapshot.hasError) { + return const Text("Failed to initialize."); + } else if (!snapshot.hasData) { + return const ProgressRing(); + } else { + final data = snapshot.data!; + + return UpdatWindowManager( + appName: appTitle, + currentVersion: data.version, + getLatestVersion: () async { + final data = await http.get(Uri.parse( + "https://api.github.com/repos/VolantisDev/Launchpad/releases/latest", + )); + + return jsonDecode(data.body)["tag_name"]; + }, + getBinaryUrl: (version) async { + var ext = Platform.instance.operatingSystem.name == 'windows' + ? 'exe' + : 'dmg'; + + return "https://github.com/VolantisDev/Launchpad/releases/download/$version/Launchpad-${Platform.instance.operatingSystem}-$version.$ext"; + }, + getChangelog: (_, __) async { + final data = await http.get(Uri.parse( + "https://api.github.com/repos/VolantisDev/Launchpad/releases/latest", + )); + return jsonDecode(data.body)["body"]; + }, + child: MainDropTarget( + child: NavigationView( + key: viewKey, + appBar: NavigationAppBar( + automaticallyImplyLeading: false, + title: () { + if (kIsWeb) { + return const Align( + alignment: AlignmentDirectional.centerStart, + child: Text(appTitle), + ); + } + return const DragToMoveArea( + child: Align( + alignment: AlignmentDirectional.centerStart, + child: Text(appTitle), + ), + ); + }(), + actions: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsetsDirectional.only(end: 8.0), + child: ToggleSwitch( + content: const Text('Dark Mode'), + checked: + FluentTheme.of(context).brightness.isDark, + onChanged: (v) { + appTheme.mode = + v ? ThemeMode.dark : ThemeMode.light; + }, + ), + ), + if (!kIsWeb) const WindowButtons(), + ]), + ), + pane: NavigationPane( + selected: index, + onChanged: (i) { + setState(() => index = i); + }, + header: SizedBox( + height: kOneLineTileHeight + 5, + child: SvgPicture.asset( + FluentTheme.of(context).brightness.isDark + ? Assets.graphics.logoWide.path + : Assets.graphics.light.logoWide.path, + semanticsLabel: 'Launchpad', + ), + ), + displayMode: appTheme.displayMode, + indicator: () { + switch (appTheme.indicator) { + case NavigationIndicators.end: + return const EndNavigationIndicator(); + case NavigationIndicators.sticky: + default: + return const StickyNavigationIndicator(); + } + }(), + items: originalItems.value ?? [], + autoSuggestBox: AutoSuggestBox( + key: searchKey, + focusNode: searchFocusNode, + controller: searchController, + items: (originalItems.value ?? []) + .whereType() + .map((item) { + assert(item.title is Text); + final text = (item.title as Text).data!; + + return AutoSuggestBoxItem( + label: text, + value: text, + onSelected: () async { + final itemIndex = NavigationPane( + items: originalItems.value ?? [], + ).effectiveIndexOf(item); + + setState(() => index = itemIndex); + await Future.delayed( + const Duration(milliseconds: 17)); + searchController.clear(); + }, + ); + }).toList(), + placeholder: 'Search', + ), + autoSuggestBoxReplacement: const Icon(FluentIcons.search), + footerItems: footerItems, + ), + onOpenSearch: () { + searchFocusNode.requestFocus(); + }, + ))); + } + }); + } + + @override + onWindowClose() async { + var isPreventClose = await windowManager.isPreventClose(); + + if (isPreventClose) { + showDialog( + context: context, + builder: (_) { + return ContentDialog( + title: const Text('Confirm close'), + content: const Text('Are you sure you want to close this window?'), + actions: [ + FilledButton( + child: const Text('Yes'), + onPressed: () { + Navigator.pop(context); + windowManager.destroy(); + }, + ), + Button( + child: const Text('No'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + }, + ); + } + } +} + +class WindowButtons extends StatelessWidget { + const WindowButtons({Key? key}) : super(key: key); + + @override + build(BuildContext context) { + final theme = FluentTheme.of(context); + + return SizedBox( + width: 138, + height: 50, + child: WindowCaption( + brightness: theme.brightness, + backgroundColor: Colors.transparent, + ), + ); + } +} + +class _LinkPaneItemAction extends PaneItem { + _LinkPaneItemAction({ + required super.icon, + required this.link, + required super.body, + super.title, + }); + + final String link; + + @override + build( + BuildContext context, + bool selected, + VoidCallback? onPressed, { + PaneDisplayMode? displayMode, + var showTextOnTop = true, + bool? autofocus, + int? itemIndex, + }) { + return Link( + uri: Uri.parse(link), + builder: (context, followLink) => super.build( + context, + selected, + followLink, + displayMode: displayMode, + showTextOnTop: showTextOnTop, + itemIndex: itemIndex, + autofocus: autofocus, + ), + ); + } +} diff --git a/launchpad_app/lib/src/features/main_window/presentation/home_container.g.dart b/launchpad_app/lib/src/features/main_window/presentation/home_container.g.dart new file mode 100644 index 00000000..d3a3f281 --- /dev/null +++ b/launchpad_app/lib/src/features/main_window/presentation/home_container.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'home_container.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $paneItemsHash() => r'b5363764b8c1c9e4542fb3cda15440fb37f21ecb'; + +/// See also [paneItems]. +final paneItemsProvider = AutoDisposeFutureProvider>( + paneItems, + name: r'paneItemsProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : $paneItemsHash, +); +typedef PaneItemsRef = AutoDisposeFutureProviderRef>; diff --git a/launchpad_app/lib/src/features/main_window/presentation/main_drop_target.dart b/launchpad_app/lib/src/features/main_window/presentation/main_drop_target.dart new file mode 100644 index 00000000..f6345fa2 --- /dev/null +++ b/launchpad_app/lib/src/features/main_window/presentation/main_drop_target.dart @@ -0,0 +1,50 @@ +import 'package:desktop_drop/desktop_drop.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +class MainDropTarget extends StatefulHookConsumerWidget { + const MainDropTarget({Key? key, required this.child}) : super(key: key); + + final Widget child; + + @override + createState() => _MainDropTargetState(); +} + +class _MainDropTargetState extends ConsumerState { + bool _dragging = false; + + @override + Widget build(BuildContext context) { + return DropTarget( + onDragDone: (detail) { + if (detail.files.isNotEmpty) { + // TODO Start game detection against the provided files + } + }, + onDragEntered: (detail) { + setState(() { + _dragging = true; + }); + }, + onDragExited: (detail) { + setState(() { + _dragging = false; + }); + }, + child: Stack(children: [ + widget.child, + Visibility( + visible: _dragging, + child: Container( + width: double.infinity, + height: double.infinity, + color: Theme.of(context).colorScheme.surface.withOpacity(0.75), + child: const Center( + child: Text("Drop files or folders anywhere to add games")), + ), + ), + ]), + ); + } +} diff --git a/launchpad_app/lib/src/features/settings/presentation/sample_data_options.dart b/launchpad_app/lib/src/features/settings/presentation/sample_data_options.dart new file mode 100644 index 00000000..5f4901c7 --- /dev/null +++ b/launchpad_app/lib/src/features/settings/presentation/sample_data_options.dart @@ -0,0 +1,101 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/games/data/game_data.dart'; +import 'package:launchpad_app/src/features/games/data/launch_config_data.dart'; +import 'package:launchpad_app/src/utils/isar_instance.dart'; + +class SampleDataOptions extends StatefulHookConsumerWidget { + const SampleDataOptions({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _SampleDataOptions(); +} + +class _SampleDataOptions extends ConsumerState { + @override + build(BuildContext context) { + return Visibility( + visible: kDebugMode, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const Text( + 'This is a debug build. You can import test data or clear all ' + 'data using these options. Clearing data will clear all data, ' + 'not just test data.'), + const SizedBox(height: 10.0), + Row( + children: [ + FilledButton( + onPressed: () => importTestData(), + child: const Text("Import test data"), + ), + const SizedBox(width: 10.0), + Button( + onPressed: () => clearData(), + child: const Text('Clear all data'), + ) + ], + ), + const SizedBox(height: 22.0), + ])); + } + + Future importTestData() async { + var instance = await isarInstance; + + instance.collection().importJson([ + { + 'type': 'game', + 'gameId': 1, + 'gameKey': 'Overwatch', + 'platformId': 'blizzard', + 'values': [ + { + 'key': 'SampleKey', + 'value': 'SampleValue', + } + ] + }, + { + 'type': 'game', + 'gameId': 1, + 'gameKey': 'Overwatch', + 'platformId': 'blizzard', + 'values': [ + { + 'key': 'SampleKey', + 'value': 'SampleValue', + } + ] + }, + { + 'type': 'game', + 'gameId': 1, + 'gameKey': 'Overwatch', + 'platformId': 'blizzard', + 'values': [ + { + 'key': 'SampleKey', + 'value': 'SampleValue', + } + ] + } + ]); + + instance.collection().importJson([ + { + 'id': 1, + 'key': 'Overwatch', + 'name': 'Overwatch 2', + 'platformId': 'blizzard', + 'platformRef': 'pro' + } + ]); + } + + Future clearData() async { + var instance = await isarInstance; + + instance.collection().clear(); + } +} diff --git a/launchpad_app/lib/src/features/settings/presentation/settings.dart b/launchpad_app/lib/src/features/settings/presentation/settings.dart new file mode 100644 index 00000000..7fadb11c --- /dev/null +++ b/launchpad_app/lib/src/features/settings/presentation/settings.dart @@ -0,0 +1,293 @@ +// ignore_for_file: constant_identifier_names + +import 'package:flutter/foundation.dart'; + +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter_acrylic/flutter_acrylic.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:launchpad_app/src/features/settings/presentation/sample_data_options.dart'; +import 'package:launchpad_app/src/utils/theme_provider.dart'; + +import '../../../utils/theme.dart'; +import '../../../common_widgets/page.dart'; + +const accentColorNames = [ + 'System', + 'Yellow', + 'Orange', + 'Red', + 'Magenta', + 'Purple', + 'Blue', + 'Teal', + 'Green', +]; + +bool get kIsWindowEffectsSupported { + return !kIsWeb && + [ + TargetPlatform.windows, + TargetPlatform.linux, + TargetPlatform.macOS, + ].contains(defaultTargetPlatform); +} + +const _LinuxWindowEffects = [ + WindowEffect.disabled, + WindowEffect.transparent, +]; + +const _WindowsWindowEffects = [ + WindowEffect.disabled, + WindowEffect.solid, + WindowEffect.transparent, + WindowEffect.aero, + WindowEffect.acrylic, + WindowEffect.mica, + WindowEffect.tabbed, +]; + +const _MacosWindowEffects = [ + WindowEffect.disabled, + WindowEffect.titlebar, + WindowEffect.selection, + WindowEffect.menu, + WindowEffect.popover, + WindowEffect.sidebar, + WindowEffect.headerView, + WindowEffect.sheet, + WindowEffect.windowBackground, + WindowEffect.hudWindow, + WindowEffect.fullScreenUI, + WindowEffect.toolTip, + WindowEffect.contentBackground, + WindowEffect.underWindowBackground, + WindowEffect.underPageBackground, +]; + +List get currentWindowEffects { + if (kIsWeb) return []; + + if (defaultTargetPlatform == TargetPlatform.windows) { + return _WindowsWindowEffects; + } else if (defaultTargetPlatform == TargetPlatform.linux) { + return _LinuxWindowEffects; + } else if (defaultTargetPlatform == TargetPlatform.macOS) { + return _MacosWindowEffects; + } + + return []; +} + +class Settings extends ScrollablePage { + Settings({super.key}); + + @override + Widget buildHeader(BuildContext context, WidgetRef ref) { + return const PageHeader(title: Text('Settings')); + } + + @override + List buildScrollable(BuildContext context, WidgetRef ref) { + assert(debugCheckHasMediaQuery(context)); + final appTheme = ref.watch(appThemeProvider); + const spacer = SizedBox(height: 10.0); + const biggerSpacer = SizedBox(height: 40.0); + + const supportedLocales = FluentLocalizations.supportedLocales; + final currentLocale = + appTheme.locale ?? Localizations.maybeLocaleOf(context); + + return [ + const SampleDataOptions(), + Text('Theme mode', style: FluentTheme.of(context).typography.subtitle), + spacer, + ...List.generate(ThemeMode.values.length, (index) { + final mode = ThemeMode.values[index]; + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: appTheme.mode == mode, + onChanged: (value) { + if (value) { + appTheme.mode = mode; + + if (kIsWindowEffectsSupported) { + // some window effects require on [dark] to look good. + // appTheme.setEffect(WindowEffect.disabled, context); + appTheme.setEffect(appTheme.windowEffect, context); + } + } + }, + content: Text('$mode'.replaceAll('ThemeMode.', '')), + ), + ); + }), + biggerSpacer, + Text( + 'Navigation Pane Display Mode', + style: FluentTheme.of(context).typography.subtitle, + ), + spacer, + ...List.generate(PaneDisplayMode.values.length, (index) { + final mode = PaneDisplayMode.values[index]; + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: appTheme.displayMode == mode, + onChanged: (value) { + if (value) appTheme.displayMode = mode; + }, + content: Text( + mode.toString().replaceAll('PaneDisplayMode.', ''), + ), + ), + ); + }), + biggerSpacer, + Text('Navigation Indicator', + style: FluentTheme.of(context).typography.subtitle), + spacer, + ...List.generate(NavigationIndicators.values.length, (index) { + final mode = NavigationIndicators.values[index]; + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: appTheme.indicator == mode, + onChanged: (value) { + if (value) appTheme.indicator = mode; + }, + content: Text( + mode.toString().replaceAll('NavigationIndicators.', ''), + ), + ), + ); + }), + biggerSpacer, + Text('Accent Color', style: FluentTheme.of(context).typography.subtitle), + spacer, + Wrap(children: [ + Tooltip( + message: accentColorNames[0], + child: _buildColorBlock(appTheme, systemAccentColor), + ), + ...List.generate(Colors.accentColors.length, (index) { + final color = Colors.accentColors[index]; + return Tooltip( + message: accentColorNames[index + 1], + child: _buildColorBlock(appTheme, color), + ); + }), + ]), + if (kIsWindowEffectsSupported) ...[ + biggerSpacer, + Text( + 'Window Transparency (${defaultTargetPlatform.toString().replaceAll('TargetPlatform.', '')})', + style: FluentTheme.of(context).typography.subtitle, + ), + spacer, + ...List.generate(currentWindowEffects.length, (index) { + final mode = currentWindowEffects[index]; + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: appTheme.windowEffect == mode, + onChanged: (value) { + if (value) { + appTheme.windowEffect = mode; + appTheme.setEffect(mode, context); + } + }, + content: Text( + mode.toString().replaceAll('WindowEffect.', ''), + ), + ), + ); + }), + ], + biggerSpacer, + Text('Text Direction', + style: FluentTheme.of(context).typography.subtitle), + spacer, + ...List.generate(TextDirection.values.length, (index) { + final direction = TextDirection.values[index]; + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: appTheme.textDirection == direction, + onChanged: (value) { + if (value) { + appTheme.textDirection = direction; + } + }, + content: Text( + '$direction' + .replaceAll('TextDirection.', '') + .replaceAll('rtl', 'Right to left') + .replaceAll('ltr', 'Left to right'), + ), + ), + ); + }).reversed, + Text('Locale', style: FluentTheme.of(context).typography.subtitle), + spacer, + Wrap( + spacing: 15.0, + runSpacing: 10.0, + children: List.generate( + supportedLocales.length, + (index) { + final locale = supportedLocales[index]; + + return Padding( + padding: const EdgeInsetsDirectional.only(bottom: 8.0), + child: RadioButton( + checked: currentLocale == locale, + onChanged: (value) { + if (value) { + appTheme.locale = locale; + } + }, + content: Text('$locale'), + ), + ); + }, + ), + ), + ]; + } + + Widget _buildColorBlock(AppTheme appTheme, AccentColor color) { + return Padding( + padding: const EdgeInsets.all(2.0), + child: Button( + onPressed: () { + appTheme.color = color; + }, + style: ButtonStyle( + padding: ButtonState.all(EdgeInsets.zero), + backgroundColor: ButtonState.resolveWith((states) { + if (states.isPressing) { + return color.light; + } else if (states.isHovering) { + return color.lighter; + } + return color; + }), + ), + child: Container( + height: 40, + width: 40, + alignment: AlignmentDirectional.center, + child: appTheme.color == color + ? Icon( + FluentIcons.check_mark, + color: color.basedOnLuminance(), + size: 22.0, + ) + : null, + ), + ), + ); + } +} diff --git a/launchpad_app/lib/src/features/web_services/application/web_service_adapter_types.dart b/launchpad_app/lib/src/features/web_services/application/web_service_adapter_types.dart new file mode 100644 index 00000000..edf46ee7 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/application/web_service_adapter_types.dart @@ -0,0 +1,40 @@ +// An Adapter takes data from a web service and converts it to a domain object. + +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'web_service_adapter_types.g.dart'; + +class WebServiceAdapterType { + const WebServiceAdapterType({ + required this.key, + required this.name, + this.description = '', + }); + + final String key; + final String name; + final String description; +} + +@Riverpod(keepAlive: true) +Future> webServiceAdapterTypes( + WebServiceAdapterTypesRef ref) async { + return { + "game": const WebServiceAdapterType( + key: "game", + name: "Game", + description: "Data representing a game.", + ), + "game_asset": const WebServiceAdapterType( + key: "game_asset", + name: "Game Asset", + description: + "Data representing an asset for a game, such as an image or video.", + ), + "game_platform": const WebServiceAdapterType( + key: "game_platform", + name: "Game Platform", + description: "Data representing a platform for game discovery.", + ), + }; +} diff --git a/launchpad_app/lib/src/features/web_services/application/web_service_adapter_types.g.dart b/launchpad_app/lib/src/features/web_services/application/web_service_adapter_types.g.dart new file mode 100644 index 00000000..16f79b1e --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/application/web_service_adapter_types.g.dart @@ -0,0 +1,45 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'web_service_adapter_types.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $webServiceAdapterTypesHash() => + r'9d12d9e927307e919d92e0e58354ba66e6d6de34'; + +/// See also [webServiceAdapterTypes]. +final webServiceAdapterTypesProvider = + FutureProvider>( + webServiceAdapterTypes, + name: r'webServiceAdapterTypesProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : $webServiceAdapterTypesHash, +); +typedef WebServiceAdapterTypesRef + = FutureProviderRef>; diff --git a/launchpad_app/lib/src/features/web_services/application/web_service_adapters.dart b/launchpad_app/lib/src/features/web_services/application/web_service_adapters.dart new file mode 100644 index 00000000..6da440bc --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/application/web_service_adapters.dart @@ -0,0 +1,2 @@ +// An Adapter takes data from a web service and converts it to a domain object. + diff --git a/launchpad_app/lib/src/features/web_services/application/web_service_authenticators.dart b/launchpad_app/lib/src/features/web_services/application/web_service_authenticators.dart new file mode 100644 index 00000000..da99bc32 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/application/web_service_authenticators.dart @@ -0,0 +1,51 @@ +import 'package:fluent_ui/fluent_ui.dart'; + +class WebServiceAuthenticator { + WebServiceAuthenticator({ + required this.name, + required this.description, + required this.fields, + }); + + final String name; + final String description; + final List fields; + + Future> authenticate( + Map credentials) async { + return {}; + } +} + +class AuthField { + AuthField({ + required this.key, + required this.name, + this.description = '', + this.placeholder = '', + this.value = '', + this.expands = false, + this.obscureText = false, + }); + + final String key; + final String name; + final String description; + final String placeholder; + final bool expands; + final bool obscureText; + String value; + Widget? widget; + + Future getWidget() async { + widget ??= TextBox( + header: name, + placeholder: placeholder, + expands: expands, + initialValue: value, + obscureText: obscureText, + ); + + return widget!; + } +} diff --git a/launchpad_app/lib/src/features/web_services/application/web_service_providers.dart b/launchpad_app/lib/src/features/web_services/application/web_service_providers.dart new file mode 100644 index 00000000..ebcadaf5 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/application/web_service_providers.dart @@ -0,0 +1,40 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/widgets.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'web_service_providers.g.dart'; + +class WebServiceProvider { + WebServiceProvider({ + required this.key, + required this.name, + this.url, + this.icon, + this.color, + this.accentColor, + this.logoPath, + this.description, + }); + + final String key; + final String name; + final String? url; + final String? icon; + final String? color; + final String? accentColor; + final String? logoPath; + final String? description; +} + +@Riverpod(keepAlive: true) +Future> webServiceProviders( + WebServiceProvidersRef ref) async { + return { + "launchpad.games": WebServiceProvider( + key: "launchpad.games", + name: "Launchpad.games", + description: + "The official Launchpad website, which offers cloud syncing and full API access.", + ), + }; +} diff --git a/launchpad_app/lib/src/features/web_services/application/web_service_providers.g.dart b/launchpad_app/lib/src/features/web_services/application/web_service_providers.g.dart new file mode 100644 index 00000000..7d660762 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/application/web_service_providers.g.dart @@ -0,0 +1,45 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'web_service_providers.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $webServiceProvidersHash() => + r'7ec95b65c93b54f384f55e19129a74c353449033'; + +/// See also [webServiceProviders]. +final webServiceProvidersProvider = + FutureProvider>( + webServiceProviders, + name: r'webServiceProvidersProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : $webServiceProvidersHash, +); +typedef WebServiceProvidersRef + = FutureProviderRef>; diff --git a/launchpad_app/lib/src/features/web_services/domain/account.dart b/launchpad_app/lib/src/features/web_services/domain/account.dart new file mode 100644 index 00000000..1b3c8c26 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/domain/account.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'account.freezed.dart'; + +part 'account.g.dart'; + +@freezed +class Account with _$Account { + const Account._(); + + const factory Account({ + required String key, + required String name, + required String providerId, + }) = _Account; + + factory Account.fromJson(Map json) => + _$AccountFromJson(json); +} diff --git a/launchpad_app/lib/src/features/web_services/domain/account.freezed.dart b/launchpad_app/lib/src/features/web_services/domain/account.freezed.dart new file mode 100644 index 00000000..e803f2a9 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/domain/account.freezed.dart @@ -0,0 +1,185 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'account.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +Account _$AccountFromJson(Map json) { + return _Account.fromJson(json); +} + +/// @nodoc +mixin _$Account { + String get key => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get providerId => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $AccountCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AccountCopyWith<$Res> { + factory $AccountCopyWith(Account value, $Res Function(Account) then) = + _$AccountCopyWithImpl<$Res, Account>; + @useResult + $Res call({String key, String name, String providerId}); +} + +/// @nodoc +class _$AccountCopyWithImpl<$Res, $Val extends Account> + implements $AccountCopyWith<$Res> { + _$AccountCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? providerId = null, + }) { + return _then(_value.copyWith( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + providerId: null == providerId + ? _value.providerId + : providerId // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_AccountCopyWith<$Res> implements $AccountCopyWith<$Res> { + factory _$$_AccountCopyWith( + _$_Account value, $Res Function(_$_Account) then) = + __$$_AccountCopyWithImpl<$Res>; + @override + @useResult + $Res call({String key, String name, String providerId}); +} + +/// @nodoc +class __$$_AccountCopyWithImpl<$Res> + extends _$AccountCopyWithImpl<$Res, _$_Account> + implements _$$_AccountCopyWith<$Res> { + __$$_AccountCopyWithImpl(_$_Account _value, $Res Function(_$_Account) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? providerId = null, + }) { + return _then(_$_Account( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + providerId: null == providerId + ? _value.providerId + : providerId // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_Account extends _Account { + const _$_Account( + {required this.key, required this.name, required this.providerId}) + : super._(); + + factory _$_Account.fromJson(Map json) => + _$$_AccountFromJson(json); + + @override + final String key; + @override + final String name; + @override + final String providerId; + + @override + String toString() { + return 'Account(key: $key, name: $name, providerId: $providerId)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_Account && + (identical(other.key, key) || other.key == key) && + (identical(other.name, name) || other.name == name) && + (identical(other.providerId, providerId) || + other.providerId == providerId)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, key, name, providerId); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_AccountCopyWith<_$_Account> get copyWith => + __$$_AccountCopyWithImpl<_$_Account>(this, _$identity); + + @override + Map toJson() { + return _$$_AccountToJson( + this, + ); + } +} + +abstract class _Account extends Account { + const factory _Account( + {required final String key, + required final String name, + required final String providerId}) = _$_Account; + const _Account._() : super._(); + + factory _Account.fromJson(Map json) = _$_Account.fromJson; + + @override + String get key; + @override + String get name; + @override + String get providerId; + @override + @JsonKey(ignore: true) + _$$_AccountCopyWith<_$_Account> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/launchpad_app/lib/src/features/web_services/domain/account.g.dart b/launchpad_app/lib/src/features/web_services/domain/account.g.dart new file mode 100644 index 00000000..4e5a5339 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/domain/account.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'account.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_Account _$$_AccountFromJson(Map json) => _$_Account( + key: json['key'] as String, + name: json['name'] as String, + providerId: json['providerId'] as String, + ); + +Map _$$_AccountToJson(_$_Account instance) => + { + 'key': instance.key, + 'name': instance.name, + 'providerId': instance.providerId, + }; diff --git a/launchpad_app/lib/src/features/web_services/domain/provider.dart b/launchpad_app/lib/src/features/web_services/domain/provider.dart new file mode 100644 index 00000000..fcb23043 --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/domain/provider.dart @@ -0,0 +1,23 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'provider.freezed.dart'; + +part 'provider.g.dart'; + +@freezed +class Provider with _$Provider { + const Provider._(); + + const factory Provider({ + required String key, + required String name, + required String type, + required String url, + required String icon, + required String color, + required String description, + }) = _Provider; + + factory Provider.fromJson(Map json) => + _$ProviderFromJson(json); +} diff --git a/launchpad_app/lib/src/features/web_services/domain/provider.freezed.dart b/launchpad_app/lib/src/features/web_services/domain/provider.freezed.dart new file mode 100644 index 00000000..8ac4816e --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/domain/provider.freezed.dart @@ -0,0 +1,276 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'provider.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +Provider _$ProviderFromJson(Map json) { + return _Provider.fromJson(json); +} + +/// @nodoc +mixin _$Provider { + String get key => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get type => throw _privateConstructorUsedError; + String get url => throw _privateConstructorUsedError; + String get icon => throw _privateConstructorUsedError; + String get color => throw _privateConstructorUsedError; + String get description => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ProviderCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ProviderCopyWith<$Res> { + factory $ProviderCopyWith(Provider value, $Res Function(Provider) then) = + _$ProviderCopyWithImpl<$Res, Provider>; + @useResult + $Res call( + {String key, + String name, + String type, + String url, + String icon, + String color, + String description}); +} + +/// @nodoc +class _$ProviderCopyWithImpl<$Res, $Val extends Provider> + implements $ProviderCopyWith<$Res> { + _$ProviderCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? type = null, + Object? url = null, + Object? icon = null, + Object? color = null, + Object? description = null, + }) { + return _then(_value.copyWith( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String, + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + icon: null == icon + ? _value.icon + : icon // ignore: cast_nullable_to_non_nullable + as String, + color: null == color + ? _value.color + : color // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_ProviderCopyWith<$Res> implements $ProviderCopyWith<$Res> { + factory _$$_ProviderCopyWith( + _$_Provider value, $Res Function(_$_Provider) then) = + __$$_ProviderCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String key, + String name, + String type, + String url, + String icon, + String color, + String description}); +} + +/// @nodoc +class __$$_ProviderCopyWithImpl<$Res> + extends _$ProviderCopyWithImpl<$Res, _$_Provider> + implements _$$_ProviderCopyWith<$Res> { + __$$_ProviderCopyWithImpl( + _$_Provider _value, $Res Function(_$_Provider) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? key = null, + Object? name = null, + Object? type = null, + Object? url = null, + Object? icon = null, + Object? color = null, + Object? description = null, + }) { + return _then(_$_Provider( + key: null == key + ? _value.key + : key // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String, + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + icon: null == icon + ? _value.icon + : icon // ignore: cast_nullable_to_non_nullable + as String, + color: null == color + ? _value.color + : color // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_Provider extends _Provider { + const _$_Provider( + {required this.key, + required this.name, + required this.type, + required this.url, + required this.icon, + required this.color, + required this.description}) + : super._(); + + factory _$_Provider.fromJson(Map json) => + _$$_ProviderFromJson(json); + + @override + final String key; + @override + final String name; + @override + final String type; + @override + final String url; + @override + final String icon; + @override + final String color; + @override + final String description; + + @override + String toString() { + return 'Provider(key: $key, name: $name, type: $type, url: $url, icon: $icon, color: $color, description: $description)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_Provider && + (identical(other.key, key) || other.key == key) && + (identical(other.name, name) || other.name == name) && + (identical(other.type, type) || other.type == type) && + (identical(other.url, url) || other.url == url) && + (identical(other.icon, icon) || other.icon == icon) && + (identical(other.color, color) || other.color == color) && + (identical(other.description, description) || + other.description == description)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, key, name, type, url, icon, color, description); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_ProviderCopyWith<_$_Provider> get copyWith => + __$$_ProviderCopyWithImpl<_$_Provider>(this, _$identity); + + @override + Map toJson() { + return _$$_ProviderToJson( + this, + ); + } +} + +abstract class _Provider extends Provider { + const factory _Provider( + {required final String key, + required final String name, + required final String type, + required final String url, + required final String icon, + required final String color, + required final String description}) = _$_Provider; + const _Provider._() : super._(); + + factory _Provider.fromJson(Map json) = _$_Provider.fromJson; + + @override + String get key; + @override + String get name; + @override + String get type; + @override + String get url; + @override + String get icon; + @override + String get color; + @override + String get description; + @override + @JsonKey(ignore: true) + _$$_ProviderCopyWith<_$_Provider> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/launchpad_app/lib/src/features/web_services/domain/provider.g.dart b/launchpad_app/lib/src/features/web_services/domain/provider.g.dart new file mode 100644 index 00000000..22071d4d --- /dev/null +++ b/launchpad_app/lib/src/features/web_services/domain/provider.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'provider.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_Provider _$$_ProviderFromJson(Map json) => _$_Provider( + key: json['key'] as String, + name: json['name'] as String, + type: json['type'] as String, + url: json['url'] as String, + icon: json['icon'] as String, + color: json['color'] as String, + description: json['description'] as String, + ); + +Map _$$_ProviderToJson(_$_Provider instance) => + { + 'key': instance.key, + 'name': instance.name, + 'type': instance.type, + 'url': instance.url, + 'icon': instance.icon, + 'color': instance.color, + 'description': instance.description, + }; diff --git a/launchpad_app/lib/src/routing/router.dart b/launchpad_app/lib/src/routing/router.dart new file mode 100644 index 00000000..25c012fd --- /dev/null +++ b/launchpad_app/lib/src/routing/router.dart @@ -0,0 +1,14 @@ +import 'package:catcher/catcher.dart'; +import 'package:go_router/go_router.dart'; +import 'package:launchpad_app/src/routing/routes.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'router.g.dart'; + +@Riverpod(keepAlive: true) +GoRouter router(RouterRef ref) { + return GoRouter( + routes: $appRoutes, + navigatorKey: Catcher.navigatorKey, + ); +} diff --git a/launchpad_app/lib/src/routing/router.g.dart b/launchpad_app/lib/src/routing/router.g.dart new file mode 100644 index 00000000..8c71a6f1 --- /dev/null +++ b/launchpad_app/lib/src/routing/router.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'router.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $routerHash() => r'10918b6a65308b2967b190b9f58c33389e825cf9'; + +/// See also [router]. +final routerProvider = Provider( + router, + name: r'routerProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : $routerHash, +); +typedef RouterRef = ProviderRef; diff --git a/launchpad_app/lib/src/routing/routes.dart b/launchpad_app/lib/src/routing/routes.dart new file mode 100644 index 00000000..d2fafd82 --- /dev/null +++ b/launchpad_app/lib/src/routing/routes.dart @@ -0,0 +1,25 @@ +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:launchpad_app/src/features/main_window/presentation/home_container.dart'; + +part 'routes.g.dart'; + +@TypedGoRoute( + path: '/', + routes: >[], +) +class HomeRoute extends GoRouteData { + const HomeRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) => + const HomeContainer(); +} + +// @TypedGoRoute(path: '/login') +// class LoginRoute extends GoRouteData { +// const LoginRoute(); + +// @override +// Widget build(BuildContext context) => const LoginPage(); +// } diff --git a/launchpad_app/lib/src/routing/routes.g.dart b/launchpad_app/lib/src/routing/routes.g.dart new file mode 100644 index 00000000..2945cb4b --- /dev/null +++ b/launchpad_app/lib/src/routing/routes.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'routes.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $homeRoute, + ]; + +GoRoute get $homeRoute => GoRouteData.$route( + path: '/', + factory: $HomeRouteExtension._fromState, + ); + +extension $HomeRouteExtension on HomeRoute { + static HomeRoute _fromState(GoRouterState state) => const HomeRoute(); + + String get location => GoRouteData.$location( + '/', + ); + + void go(BuildContext context) => context.go(location, extra: this); + + void push(BuildContext context) => context.push(location, extra: this); +} diff --git a/launchpad_app/lib/src/utils/globals.dart b/launchpad_app/lib/src/utils/globals.dart new file mode 100644 index 00000000..24d16896 --- /dev/null +++ b/launchpad_app/lib/src/utils/globals.dart @@ -0,0 +1,3 @@ +library launchpad_app.globals; + +const appTitle = 'Launchpad - Game Launching Multitool'; diff --git a/launchpad_app/lib/src/utils/heading.dart b/launchpad_app/lib/src/utils/heading.dart new file mode 100644 index 00000000..51e473fc --- /dev/null +++ b/launchpad_app/lib/src/utils/heading.dart @@ -0,0 +1,19 @@ +import 'dart:math'; + +final appHeadings = [ + 'The game launching multitool', + "Let's launch!", + 'We can launch anything', + "Tame your library", + "Organize the chaos", + "All games treated equal", + "Surface your forgotten gems", + "If you can play it, you can launch it", + "Game wrangler extraordinaire", + "The wizard of your game library", + "Come with me if you want to launch", + "Getting you into the fray", + "Let's launch this thing" +]; + +final appHeading = appHeadings[Random().nextInt(appHeadings.length)]; diff --git a/launchpad_app/lib/src/utils/isar_instance.dart b/launchpad_app/lib/src/utils/isar_instance.dart new file mode 100644 index 00000000..20bb18c0 --- /dev/null +++ b/launchpad_app/lib/src/utils/isar_instance.dart @@ -0,0 +1,21 @@ +import 'package:launchpad_app/src/features/games/data/game_data.dart'; +import 'package:launchpad_app/src/features/games/data/launch_process_data.dart'; +import 'package:launchpad_app/src/features/games/data/launch_config_data.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:isar/isar.dart'; + +import '../features/game_platforms/data/game_platform_data.dart'; + +part 'isar_instance.g.dart'; + +final isarInstance = Isar.open([ + GamePlatformDataSchema, + GameDataSchema, + LaunchProcessDataSchema, + LaunchConfigDataSchema, +]); + +@Riverpod(keepAlive: true) +Future isarInstanceProvider(FutureProviderRef ref) { + return isarInstance; +} diff --git a/launchpad_app/lib/src/utils/isar_instance.g.dart b/launchpad_app/lib/src/utils/isar_instance.g.dart new file mode 100644 index 00000000..c6063281 --- /dev/null +++ b/launchpad_app/lib/src/utils/isar_instance.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'isar_instance.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// ignore_for_file: avoid_private_typedef_functions, non_constant_identifier_names, subtype_of_sealed_class, invalid_use_of_internal_member, unused_element, constant_identifier_names, unnecessary_raw_strings, library_private_types_in_public_api + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +String $isarInstanceProviderHash() => + r'2caae97ebfaa8ab3dd722301e568b6df0ff9223a'; + +/// See also [isarInstanceProvider]. +final isarInstanceProviderProvider = FutureProvider( + isarInstanceProvider, + name: r'isarInstanceProviderProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : $isarInstanceProviderHash, +); +typedef IsarInstanceProviderRef = FutureProviderRef; diff --git a/launchpad_app/lib/src/utils/theme.dart b/launchpad_app/lib/src/utils/theme.dart new file mode 100644 index 00000000..63110fe5 --- /dev/null +++ b/launchpad_app/lib/src/utils/theme.dart @@ -0,0 +1,87 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; +import 'package:system_theme/system_theme.dart'; +import 'package:flutter_acrylic/flutter_acrylic.dart'; + +enum NavigationIndicators { sticky, end } + +class AppTheme extends ChangeNotifier { + var _color = systemAccentColor; + AccentColor get color => _color; + set color(AccentColor color) { + _color = color; + notifyListeners(); + } + + var _mode = ThemeMode.system; + ThemeMode get mode => _mode; + set mode(ThemeMode mode) { + _mode = mode; + notifyListeners(); + } + + var _displayMode = PaneDisplayMode.auto; + PaneDisplayMode get displayMode => _displayMode; + set displayMode(PaneDisplayMode displayMode) { + _displayMode = displayMode; + notifyListeners(); + } + + var _indicator = NavigationIndicators.sticky; + NavigationIndicators get indicator => _indicator; + set indicator(NavigationIndicators indicator) { + _indicator = indicator; + notifyListeners(); + } + + var _windowEffect = WindowEffect.disabled; + WindowEffect get windowEffect => _windowEffect; + set windowEffect(WindowEffect windowEffect) { + _windowEffect = windowEffect; + notifyListeners(); + } + + void setEffect(WindowEffect effect, BuildContext context) { + Window.setEffect( + effect: effect, + color: [ + WindowEffect.solid, + WindowEffect.acrylic, + ].contains(effect) + ? FluentTheme.of(context).micaBackgroundColor.withOpacity(0.05) + : Colors.transparent, + dark: FluentTheme.of(context).brightness.isDark, + ); + } + + var _textDirection = TextDirection.ltr; + TextDirection get textDirection => _textDirection; + set textDirection(TextDirection direction) { + _textDirection = direction; + notifyListeners(); + } + + Locale? _locale; + Locale? get locale => _locale; + set locale(Locale? locale) { + _locale = locale; + notifyListeners(); + } +} + +AccentColor get systemAccentColor { + if ((defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.android) && + !kIsWeb) { + return AccentColor.swatch({ + 'darkest': SystemTheme.accentColor.darkest, + 'darker': SystemTheme.accentColor.darker, + 'dark': SystemTheme.accentColor.dark, + 'normal': SystemTheme.accentColor.accent, + 'light': SystemTheme.accentColor.light, + 'lighter': SystemTheme.accentColor.lighter, + 'lightest': SystemTheme.accentColor.lightest, + }); + } + return Colors.blue; +} diff --git a/launchpad_app/lib/src/utils/theme_provider.dart b/launchpad_app/lib/src/utils/theme_provider.dart new file mode 100644 index 00000000..2c1a9a22 --- /dev/null +++ b/launchpad_app/lib/src/utils/theme_provider.dart @@ -0,0 +1,5 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import 'theme.dart'; + +final appThemeProvider = ChangeNotifierProvider((ref) => AppTheme()); diff --git a/launchpad_app/lib/swaggers/launchpad_api.yaml b/launchpad_app/lib/swaggers/launchpad_api.yaml new file mode 100644 index 00000000..1c816088 --- /dev/null +++ b/launchpad_app/lib/swaggers/launchpad_api.yaml @@ -0,0 +1,468 @@ +openapi: '3.0.2' +info: + title: Launchpad API + version: '1.0' +servers: + - url: https://api.launchpad.games/v1 +paths: + /hello: + get: + description: Simply test that you can get a valid response from the API + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + example: Hey there! + /status: + get: + description: Get the status of your connection to the API + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/Status" + "/release-info": + get: + description: Get information about the latest release of Launchpad + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/ReleaseInfo" + /platforms: + get: + description: Retrieve a list of available game platforms + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/Listing" + /platforms/{platformId}: + get: + description: Get the details of a specific game platform + parameters: + - name: platformId + in: path + required: true + description: The id of the platform + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/PlatformDocument" + /launcher-types: + get: + description: Retrieve a list of available launcher types + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/Listing" + /launcher-types/{launcherTypeId}: + get: + description: Get the details of a specific launcher type + parameters: + - name: launcherTypeId + in: path + required: true + description: The id of the launcher type + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/LauncherTypeDocument" + "/game-types": + get: + description: Retrieve a list of available game types + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/Listing" + /game-types/{gameTypeId}: + get: + description: Get the details of a specific game type + parameters: + - name: gameTypeId + in: path + required: true + description: The id of the game type + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/GameTypeDocument" + /games: + get: + description: Gets a listing of all games available + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: string + key: + type: string + platform: + type: string + /game-keys: + get: + description: Gets a list of all unique game keys available + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + type: string + /games/{gameId}: + get: + description: Retrieves the details of a specific game ID + parameters: + - name: gameId + in: path + required: true + description: The id of the game + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/GameDocument" + /lookup/{gameKey}/{platformId}: + get: + description: Look up the ID of a game based on its name and platform. + parameters: + - name: gameKey + in: path + required: true + description: The key of the game + schema: + type: string + - name: platformId + in: path + required: true + description: The id of the platform + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + id: + type: string + /submit-error: + post: + description: Submit an error to be fixed + parameters: + - name: message + in: query + required: true + description: The error message + schema: + type: string + - name: what + in: query + required: true + description: What caused the error + schema: + type: string + - name: file + in: query + required: true + description: What file the error originated in + schema: + type: string + - name: line + in: query + required: true + description: The line within the file + schema: + type: integer + - name: extra + in: query + required: false + description: Extra information about the error + schema: + type: string + - name: stack + in: query + required: false + description: The stack trace if available + schema: + type: string + - name: email + in: query + required: false + description: The email address of the submitter if provided + schema: + type: string + - name: details + in: query + required: false + description: Additional details by the submitter if provided + schema: + type: string + - name: version + in: query + required: false + description: The version of Launchpad that generated the error + schema: + type: string + responses: + '200': + description: OK + /submit-feedback: + post: + description: Submit feedback to the developers + parameters: + - name: feedback + in: query + required: true + description: The feedback that was submitted + schema: + type: string + - name: email + in: query + required: false + description: The email address of the submitter if provided + schema: + type: string + - name: version + in: query + required: false + description: The version of Launchpad + schema: + type: string + responses: + '200': + description: OK + +components: + schemas: + Status: + required: + - authenticated + - email + properties: + authenticated: + type: boolean + email: + type: string + example: user@example.com + ReleaseInfo: + required: + - installer + - releasePage + - version + properties: + tag: + type: string + example: stable + version: + type: string + example: "6.2.0" + installer: + type: string + example: "https://github.com/VolantisDev/Launchpad/releases/download/6.2.0/Launchpad-6.2.0.exe" + releasePage: + type: string + example: "https://github.com/VolantisDev/Launchpad/releases/tag/6.2.0" + timestamp: + type: integer + example: 1616027680 + Listing: + type: array + items: + type: string + example: Key1 + PlatformDocument: + required: + - id + - data + properties: + id: + type: string + example: Steam + data: + $ref: "#/components/schemas/Platform" + Platform: + required: + - key + - name + - launcherType + - gameType + - class + - enabled + properties: + key: + type: string + example: Steam + name: + type: string + example: Steam + launcherType: + type: string + example: Default + gameType: + type: string + example: Default + class: + type: string + example: SteamPlatform + enabled: + type: boolean + registry: + type: object + properties: + uninstallCmd: + $ref: "#/components/schemas/RegistryValue" + installDir: + $ref: "#/components/schemas/RegistryValue" + version: + $ref: "#/components/schemas/RegistryValue" + links: + type: object + properties: + install: + type: string + website: + type: string + LauncherTypeDocument: + required: + - id + - data + properties: + id: + type: string + example: Steam + data: + $ref: "#/components/schemas/LauncherType" + LauncherType: + required: + - key + - name + - defaults + - enabled + properties: + key: + type: string + example: Steam + name: + type: string + example: Steam + defaults: + type: object + enabled: + type: boolean + GameTypeDocument: + required: + - id + - data + properties: + id: + type: string + example: Steam + data: + $ref: "#/components/schemas/GameType" + GameType: + required: + - key + - name + - defaults + - enabled + properties: + key: + type: string + example: Steam + name: + type: string + example: Steam + defaults: + type: object + enabled: + type: boolean + RegistryValue: + required: + - view + - key + - value + properties: + view: + type: integer + example: 64 + description: Whether to use the 32-bit or 64-bit registry interface + key: + type: string + example: "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MySoftware" + value: + type: string + example: "ValueName" + GameDocument: + required: + - id + - data + properties: + id: + type: string + data: + $ref: '#/components/schemas/Game' + Game: + required: + - key + - platform + - defaults + properties: + key: + type: string + example: Steam + platform: + type: string + example: Steam + defaults: + type: object + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key diff --git a/launchpad_app/linux/.gitignore b/launchpad_app/linux/.gitignore new file mode 100644 index 00000000..d3896c98 --- /dev/null +++ b/launchpad_app/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/launchpad_app/linux/CMakeLists.txt b/launchpad_app/linux/CMakeLists.txt new file mode 100644 index 00000000..1e117838 --- /dev/null +++ b/launchpad_app/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "launchpad_app") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.volantisdev.launchpad_app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/launchpad_app/linux/flutter/CMakeLists.txt b/launchpad_app/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/launchpad_app/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/launchpad_app/linux/flutter/generated_plugin_registrant.cc b/launchpad_app/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..a1ff1a15 --- /dev/null +++ b/launchpad_app/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,67 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) catcher_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CatcherPlugin"); + catcher_plugin_register_with_registrar(catcher_registrar); + g_autoptr(FlPluginRegistrar) clipboard_watcher_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ClipboardWatcherPlugin"); + clipboard_watcher_plugin_register_with_registrar(clipboard_watcher_registrar); + g_autoptr(FlPluginRegistrar) contextual_menu_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ContextualMenuPlugin"); + contextual_menu_plugin_register_with_registrar(contextual_menu_registrar); + g_autoptr(FlPluginRegistrar) desktop_drop_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); + desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); + g_autoptr(FlPluginRegistrar) desktop_webview_auth_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewAuthPlugin"); + desktop_webview_auth_plugin_register_with_registrar(desktop_webview_auth_registrar); + g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin"); + flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar); + g_autoptr(FlPluginRegistrar) hotkey_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerPlugin"); + hotkey_manager_plugin_register_with_registrar(hotkey_manager_registrar); + g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin"); + isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar); + g_autoptr(FlPluginRegistrar) local_notifier_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "LocalNotifierPlugin"); + local_notifier_plugin_register_with_registrar(local_notifier_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); + screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); + g_autoptr(FlPluginRegistrar) screen_text_extractor_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenTextExtractorPlugin"); + screen_text_extractor_plugin_register_with_registrar(screen_text_extractor_registrar); + g_autoptr(FlPluginRegistrar) tray_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); + tray_manager_plugin_register_with_registrar(tray_manager_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); +} diff --git a/launchpad_app/linux/flutter/generated_plugin_registrant.h b/launchpad_app/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/launchpad_app/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/launchpad_app/linux/flutter/generated_plugins.cmake b/launchpad_app/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..479ebebd --- /dev/null +++ b/launchpad_app/linux/flutter/generated_plugins.cmake @@ -0,0 +1,37 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + catcher + clipboard_watcher + contextual_menu + desktop_drop + desktop_webview_auth + flutter_acrylic + hotkey_manager + isar_flutter_libs + local_notifier + screen_retriever + screen_text_extractor + tray_manager + url_launcher_linux + window_manager +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/launchpad_app/linux/main.cc b/launchpad_app/linux/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/launchpad_app/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/launchpad_app/linux/my_application.cc b/launchpad_app/linux/my_application.cc new file mode 100644 index 00000000..db04ead4 --- /dev/null +++ b/launchpad_app/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "launchpad_app"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "launchpad_app"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/launchpad_app/linux/my_application.h b/launchpad_app/linux/my_application.h new file mode 100644 index 00000000..72271d5e --- /dev/null +++ b/launchpad_app/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/launchpad_app/macos/.gitignore b/launchpad_app/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/launchpad_app/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/launchpad_app/macos/Flutter/Flutter-Debug.xcconfig b/launchpad_app/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..c2efd0b6 --- /dev/null +++ b/launchpad_app/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/launchpad_app/macos/Flutter/Flutter-Release.xcconfig b/launchpad_app/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..c2efd0b6 --- /dev/null +++ b/launchpad_app/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/launchpad_app/macos/Flutter/GeneratedPluginRegistrant.swift b/launchpad_app/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..b496b03d --- /dev/null +++ b/launchpad_app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,58 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import app_links +import catcher +import clipboard_watcher +import contextual_menu +import desktop_drop +import desktop_webview_auth +import device_info_plus +import firebase_auth +import firebase_core +import flutter_acrylic +import hotkey_manager +import isar_flutter_libs +import local_notifier +import network_info_plus +import package_info_plus +import path_provider_macos +import protocol_handler +import screen_capturer +import screen_retriever +import screen_text_extractor +import shared_preferences_macos +import tray_manager +import url_launcher_macos +import window_manager + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) + CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin")) + ClipboardWatcherPlugin.register(with: registry.registrar(forPlugin: "ClipboardWatcherPlugin")) + ContextualMenuPlugin.register(with: registry.registrar(forPlugin: "ContextualMenuPlugin")) + DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) + DesktopWebviewAuthPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewAuthPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FlutterAcrylicPlugin.register(with: registry.registrar(forPlugin: "FlutterAcrylicPlugin")) + HotkeyManagerPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerPlugin")) + IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin")) + NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + ProtocolHandlerPlugin.register(with: registry.registrar(forPlugin: "ProtocolHandlerPlugin")) + ScreenCapturerPlugin.register(with: registry.registrar(forPlugin: "ScreenCapturerPlugin")) + ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) + ScreenTextExtractorPlugin.register(with: registry.registrar(forPlugin: "ScreenTextExtractorPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) +} diff --git a/launchpad_app/macos/Runner.xcodeproj/project.pbxproj b/launchpad_app/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..13d1d8fc --- /dev/null +++ b/launchpad_app/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* launchpad_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "launchpad_app.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* launchpad_app.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* launchpad_app.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/launchpad_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/launchpad_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/launchpad_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/launchpad_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/launchpad_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..251b75b7 --- /dev/null +++ b/launchpad_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/macos/Runner.xcworkspace/contents.xcworkspacedata b/launchpad_app/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/launchpad_app/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/launchpad_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/launchpad_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/launchpad_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/launchpad_app/macos/Runner/AppDelegate.swift b/launchpad_app/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/launchpad_app/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..96d3fee1 --- /dev/null +++ b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "info": { + "version": 1, + "author": "xcode" + }, + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ] +} \ No newline at end of file diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000..887c4539 Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..bc3c1136 Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000..5320fe70 Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000..1bc00be3 Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000..e66763d8 Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000..e41fd860 Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000..40581b7a Binary files /dev/null and b/launchpad_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/launchpad_app/macos/Runner/Base.lproj/MainMenu.xib b/launchpad_app/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..80e867a4 --- /dev/null +++ b/launchpad_app/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launchpad_app/macos/Runner/Configs/AppInfo.xcconfig b/launchpad_app/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..95d0bc7d --- /dev/null +++ b/launchpad_app/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = launchpad_app + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.volantisdev.launchpadApp + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 Volantis Development. All rights reserved. diff --git a/launchpad_app/macos/Runner/Configs/Debug.xcconfig b/launchpad_app/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/launchpad_app/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/launchpad_app/macos/Runner/Configs/Release.xcconfig b/launchpad_app/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/launchpad_app/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/launchpad_app/macos/Runner/Configs/Warnings.xcconfig b/launchpad_app/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/launchpad_app/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/launchpad_app/macos/Runner/DebugProfile.entitlements b/launchpad_app/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/launchpad_app/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/launchpad_app/macos/Runner/Info.plist b/launchpad_app/macos/Runner/Info.plist new file mode 100644 index 00000000..5ed7adce --- /dev/null +++ b/launchpad_app/macos/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + + CFBundleURLSchemes + + launchpad + + + + NSPrincipalClass + NSApplication + + diff --git a/launchpad_app/macos/Runner/MainFlutterWindow.swift b/launchpad_app/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..2722837e --- /dev/null +++ b/launchpad_app/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/launchpad_app/macos/Runner/Release.entitlements b/launchpad_app/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/launchpad_app/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/launchpad_app/pubspec.lock b/launchpad_app/pubspec.lock new file mode 100644 index 00000000..246650f8 --- /dev/null +++ b/launchpad_app/pubspec.lock @@ -0,0 +1,1951 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _discoveryapis_commons: + dependency: transitive + description: + name: _discoveryapis_commons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "50.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.2" + ansicolor: + dependency: transitive + description: + name: ansicolor + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + app_links: + dependency: "direct main" + description: + name: app_links + url: "https://pub.dartlang.org" + source: hosted + version: "3.4.1" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + archive: + dependency: "direct main" + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.5" + args: + dependency: "direct main" + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: "direct main" + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + badges: + dependency: "direct main" + description: + name: badges + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.7" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.4.2" + catcher: + dependency: "direct main" + description: + name: catcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" + characters: + dependency: "direct main" + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + chopper: + dependency: "direct main" + description: + name: chopper + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.0" + chopper_generator: + dependency: "direct dev" + description: + name: chopper_generator + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.0" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" + clipboard: + dependency: "direct main" + description: + name: clipboard + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + clipboard_watcher: + dependency: "direct main" + description: + name: clipboard_watcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "5.7.7" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.10" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + collection: + dependency: "direct main" + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + color: + dependency: transitive + description: + name: color + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + console: + dependency: transitive + description: + name: console + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + contextual_menu: + dependency: "direct main" + description: + name: contextual_menu + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + counter_button: + dependency: "direct main" + description: + name: counter_button + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3+2" + crypto: + dependency: "direct main" + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.2" + dart_code_metrics: + dependency: "direct main" + description: + name: dart_code_metrics + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.0" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.4" + dartx: + dependency: "direct main" + description: + name: dartx + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + dbus: + dependency: transitive + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.8" + decimal: + dependency: transitive + description: + name: decimal + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.2" + desktop_drop: + dependency: "direct main" + description: + name: desktop_drop + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + desktop_webview_auth: + dependency: transitive + description: + name: desktop_webview_auth + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.11" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "7.0.1" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + dio: + dependency: transitive + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.6" + dropdown_search: + dependency: "direct main" + description: + name: dropdown_search + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.5" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + easy_logger: + dependency: transitive + description: + name: easy_logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + email_validator: + dependency: "direct main" + description: + name: email_validator + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.17" + expandable_menu: + dependency: "direct main" + description: + name: expandable_menu + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.5" + expressions: + dependency: "direct main" + description: + name: expressions + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.4" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + faker: + dependency: "direct dev" + description: + name: faker + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + feedback: + dependency: "direct main" + description: + name: feedback + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file: + dependency: "direct main" + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" + file_picker: + dependency: "direct main" + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.4" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.0" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.2+2" + filesize: + dependency: "direct main" + description: + name: filesize + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + filter_list: + dependency: "direct main" + description: + name: filter_list + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + url: "https://pub.dartlang.org" + source: hosted + version: "3.11.2" + firebase_auth_dart: + dependency: transitive + description: + name: firebase_auth_dart + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + firebase_auth_desktop: + dependency: "direct main" + description: + name: firebase_auth_desktop + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "6.10.1" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.1" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + url: "https://pub.dartlang.org" + source: hosted + version: "1.24.0" + firebase_core_dart: + dependency: transitive + description: + name: firebase_core_dart + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + firebase_core_desktop: + dependency: "direct main" + description: + name: firebase_core_desktop + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + firebase_core_platform_interface: + dependency: "direct overridden" + description: + name: firebase_core_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.1" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.3" + firebaseapis: + dependency: transitive + description: + name: firebaseapis + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + fixnum: + dependency: "direct main" + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + floating_action_bubble: + dependency: "direct main" + description: + name: floating_action_bubble + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.4" + fluent_ui: + dependency: "direct main" + description: + name: fluent_ui + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_acrylic: + dependency: "direct main" + description: + name: flutter_acrylic + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+2" + flutter_form_builder: + dependency: "direct main" + description: + name: flutter_form_builder + url: "https://pub.dartlang.org" + source: hosted + version: "7.7.0" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0+1" + flutter_gen_runner: + dependency: "direct dev" + description: + name: flutter_gen_runner + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0+1" + flutter_hooks: + dependency: "direct main" + description: + name: flutter_hooks + url: "https://pub.dartlang.org" + source: hosted + version: "0.18.5+1" + flutter_improved_scrolling: + dependency: "direct main" + description: + name: flutter_improved_scrolling + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.3" + flutter_intro: + dependency: "direct main" + description: + name: flutter_intro + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.0" + flutter_layout_grid: + dependency: "direct main" + description: + name: flutter_layout_grid + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_mailer: + dependency: transitive + description: + name: flutter_mailer + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_markdown: + dependency: "direct main" + description: + name: flutter_markdown + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.10+6" + flutter_native_splash: + dependency: "direct dev" + description: + name: flutter_native_splash + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.16" + flutter_phoenix: + dependency: "direct main" + description: + name: flutter_phoenix + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + flutter_riverpod: + dependency: transitive + description: + name: flutter_riverpod + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + flutter_screenutil: + dependency: "direct main" + description: + name: flutter_screenutil + url: "https://pub.dartlang.org" + source: hosted + version: "5.6.0" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + flutter_staggered_grid_view: + dependency: "direct main" + description: + name: flutter_staggered_grid_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + flutter_syntax_view: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: "8b0fb4944fc36f691e40fdc944d08b7bc16973ea" + url: "https://github.com/YehudaKremer/flutter_syntax_view.git" + source: git + version: "4.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.2" + freezed: + dependency: "direct dev" + description: + name: freezed + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.2" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + get_it: + dependency: transitive + description: + name: get_it + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.0" + github: + dependency: "direct main" + description: + name: github + url: "https://pub.dartlang.org" + source: hosted + version: "9.7.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + go_router: + dependency: "direct main" + description: + name: go_router + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.0" + go_router_builder: + dependency: "direct dev" + description: + name: go_router_builder + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.15" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + googleapis: + dependency: "direct main" + description: + name: googleapis + url: "https://pub.dartlang.org" + source: hosted + version: "9.2.0" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + hive: + dependency: "direct main" + description: + name: hive + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + hooks_riverpod: + dependency: "direct main" + description: + name: hooks_riverpod + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + hotkey_manager: + dependency: "direct main" + description: + name: hotkey_manager + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.7" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.1" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.2" + humanizer: + dependency: "direct main" + description: + name: humanizer + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + image: + dependency: "direct main" + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.6" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+4" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.10" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.6+3" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.6.2" + image_picker_windows: + dependency: "direct main" + description: + name: image_picker_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+3" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + isar: + dependency: "direct main" + description: + name: isar + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + isar_flutter_libs: + dependency: "direct main" + description: + name: isar_flutter_libs + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + isar_generator: + dependency: "direct dev" + description: + name: isar_generator + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.7.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "6.5.4" + jwt_decoder: + dependency: "direct main" + description: + name: jwt_decoder + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + launch_at_startup: + dependency: "direct main" + description: + name: launch_at_startup + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + launchpad_dotnet: + dependency: "direct main" + description: + path: "../launchpad_dotnet" + relative: true + source: path + version: "0.0.1" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + local_auth: + dependency: "direct main" + description: + name: local_auth + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.17" + local_auth_ios: + dependency: transitive + description: + name: local_auth_ios + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.12" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + local_notifier: + dependency: "direct main" + description: + name: local_notifier + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + logger: + dependency: "direct main" + description: + name: logger + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + mailer: + dependency: transitive + description: + name: mailer + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.0" + markdown: + dependency: transitive + description: + name: markdown + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + menu_base: + dependency: transitive + description: + name: menu_base + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mime: + dependency: "direct main" + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + mockito: + dependency: "direct dev" + description: + name: mockito + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.2" + msix: + dependency: "direct main" + description: + name: msix + url: "https://pub.dartlang.org" + source: hosted + version: "3.7.0" + mutex: + dependency: transitive + description: + name: mutex + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + network_info_plus: + dependency: "direct main" + description: + name: network_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + network_info_plus_platform_interface: + dependency: transitive + description: + name: network_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.3" + nil: + dependency: "direct main" + description: + name: nil + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + nm: + dependency: transitive + description: + name: nm + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" + number_display: + dependency: "direct main" + description: + name: number_display + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + once: + dependency: "direct main" + description: + name: once + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + path: + dependency: "direct main" + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.22" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: "direct main" + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + path_to_regexp: + dependency: transitive + description: + name: path_to_regexp + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + pigeon: + dependency: "direct dev" + description: + name: pigeon + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.4" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + platform_info: + dependency: "direct main" + description: + name: platform_info + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + pointycastle: + dependency: transitive + description: + name: pointycastle + url: "https://pub.dartlang.org" + source: hosted + version: "3.6.2" + pool: + dependency: "direct main" + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + protobuf: + dependency: "direct main" + description: + name: protobuf + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + protocol_handler: + dependency: "direct main" + description: + name: protocol_handler + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + pub_updater: + dependency: transitive + description: + name: pub_updater + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + rational: + dependency: transitive + description: + name: rational + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" + recase: + dependency: "direct main" + description: + name: recase + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + riverpod: + dependency: transitive + description: + name: riverpod + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.7" + screen_capturer: + dependency: "direct main" + description: + name: screen_capturer + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + screen_retriever: + dependency: "direct main" + description: + name: screen_retriever + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" + screen_text_extractor: + dependency: "direct main" + description: + name: screen_text_extractor + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + scroll_pos: + dependency: transitive + description: + name: scroll_pos + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + scroll_to_index: + dependency: "direct main" + description: + name: scroll_to_index + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + sentry: + dependency: transitive + description: + name: sentry + url: "https://pub.dartlang.org" + source: hosted + version: "6.18.1" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.14" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + shimmer: + dependency: "direct main" + description: + name: shimmer + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shortid: + dependency: transitive + description: + name: shortid + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.6" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.3" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: "direct main" + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + state_notifier: + dependency: transitive + description: + name: state_notifier + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.2+1" + state_persistence: + dependency: "direct main" + description: + name: state_persistence + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + stock: + dependency: "direct main" + description: + name: stock + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + storagebox: + dependency: transitive + description: + name: storagebox + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+3" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + string_validator: + dependency: "direct main" + description: + name: string_validator + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + swagger_dart_code_generator: + dependency: "direct dev" + description: + name: swagger_dart_code_generator + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + synchronized: + dependency: "direct main" + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+3" + system_theme: + dependency: "direct main" + description: + name: system_theme + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + system_theme_web: + dependency: transitive + description: + name: system_theme_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + time: + dependency: transitive + description: + name: time + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + timeago: + dependency: "direct main" + description: + name: timeago + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + tray_manager: + dependency: "direct main" + description: + name: tray_manager + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + undo: + dependency: "direct main" + description: + name: undo + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + updat: + dependency: "direct main" + description: + name: updat + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.7" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.22" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_strategy: + dependency: "direct main" + description: + name: url_strategy + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + usage: + dependency: "direct main" + description: + name: usage + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + uuid: + dependency: "direct main" + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.7" + vdf: + dependency: "direct main" + description: + name: vdf + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + version: + dependency: "direct main" + description: + name: version + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + watcher: + dependency: "direct main" + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + win32: + dependency: "direct main" + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + win32_registry: + dependency: "direct main" + description: + name: win32_registry + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + window_manager: + dependency: "direct main" + description: + name: window_manager + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.8" + windows_single_instance: + dependency: "direct main" + description: + name: windows_single_instance + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + windows_taskbar: + dependency: "direct main" + description: + name: windows_taskbar + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+2" + xml: + dependency: "direct main" + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + xxh3: + dependency: transitive + description: + name: xxh3 + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + yaml: + dependency: "direct main" + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.18.6 <3.0.0" + flutter: ">=3.3.0" diff --git a/launchpad_app/pubspec.yaml b/launchpad_app/pubspec.yaml new file mode 100644 index 00000000..9476fccd --- /dev/null +++ b/launchpad_app/pubspec.yaml @@ -0,0 +1,194 @@ +name: launchpad_app +description: Showcase App for Fluent UI package for Flutter +publish_to: none +version: 10.0.0 + +environment: + sdk: '>=2.17.0 <3.0.0' + +dependencies: + app_links: ^3.4.1 + archive: ^3.3.5 + args: ^2.3.1 + async: ^2.9.0 + auto_size_text: ^3.0.0 + badges: ^2.0.3 + catcher: ^0.7.0 + characters: ^1.2.1 + chopper: ^6.0.0 + clipboard: ^0.1.3 + clipboard_watcher: ^0.1.2 + collection: ^1.16.0 + contextual_menu: ^0.1.2 + counter_button: ^1.0.1 + crypto: ^3.0.2 + dart_code_metrics: ^5.2.1 + dartx: ^1.1.0 + desktop_drop: ^0.4.0 + device_info_plus: ^7.0.1 + dropdown_search: ^5.0.5 + easy_localization: ^3.0.1 + email_validator: ^2.0.1 + expandable_menu: ^0.0.5 + expressions: ^0.2.4 + feedback: ^2.5.0 + file: ^6.1.4 + file_picker: ^5.2.4 + filesize: ^2.0.1 + firebase_auth: ^3.11.2 + firebase_auth_desktop: ^1.0.2 + firebase_core: ^1.24.0 + firebase_core_desktop: ^1.0.2 + filter_list: ^1.0.2 + fixnum: ^1.0.1 + floating_action_bubble: ^1.1.4 + fluent_ui: ^4.1.2 + flutter: + sdk: flutter + flutter_acrylic: ^1.0.0+2 + flutter_form_builder: ^7.7.0 + flutter_hooks: ^0.18.5+1 + flutter_improved_scrolling: ^0.0.3 + flutter_intro: ^3.0.2 + flutter_layout_grid: ^2.0.1 + flutter_markdown: ^0.6.10+5 + flutter_phoenix: ^1.1.0 + flutter_screenutil: ^5.6.0 + flutter_spinkit: ^5.1.0 + flutter_staggered_grid_view: ^0.6.2 + flutter_svg: ^1.1.6 + flutter_syntax_view: + git: + url: https://github.com/YehudaKremer/flutter_syntax_view.git + freezed_annotation: ^2.2.0 + github: ^9.7.0 + go_router: ^6.0.0 + google_fonts: ^3.0.1 + googleapis: ^9.2.0 + hive: ^2.2.3 + hooks_riverpod: ^2.1.1 + hotkey_manager: ^0.1.7 + http: ^0.13.5 + humanizer: ^2.0.0 + image: ^3.2.2 + image_picker: ^0.8.6 + image_picker_windows: ^0.1.0+3 + intl: ^0.17.0 + isar: ^3.0.5 + isar_flutter_libs: ^3.0.5 + json_annotation: ^4.7.0 + launch_at_startup: ^0.2.0 + launchpad_dotnet: + path: ../launchpad_dotnet + local_auth: ^2.1.2 + local_notifier: ^0.1.5 + logger: ^1.1.0 + mime: ^1.0.3 + msix: ^3.7.0 + network_info_plus: ^3.0.1 + nil: ^1.1.1 + number_display: ^3.0.0 + once: ^1.5.0 + package_info_plus: ^3.0.2 + path: ^1.8.2 + path_provider: ^2.0.11 + path_provider_windows: ^2.1.3 + percent_indicator: ^4.2.2 + platform_info: ^3.2.0 + pool: ^1.5.1 + protobuf: ^2.1.0 + protocol_handler: ^0.1.4 + recase: ^4.1.0 + riverpod_annotation: ^1.0.6 + screen_capturer: ^0.1.3 + screen_retriever: ^0.1.4 + screen_text_extractor: ^0.1.2 + scroll_to_index: ^3.0.1 + shimmer: ^2.0.0 + stack_trace: ^1.10.0 + state_persistence: ^0.1.0 + stock: ^1.0.1 + string_validator: ^0.3.0 + synchronized: ^3.0.0+3 + system_theme: ^2.0.0 + timeago: ^3.3.0 + tray_manager: ^0.2.0 + undo: ^1.4.0 + updat: ^1.2.0 + usage: ^4.1.0 + url_launcher: ^6.1.5 + url_strategy: ^0.2.0 + uuid: ^3.0.7 + vdf: ^1.0.2 + version: ^3.0.2 + watcher: ^1.0.2 + win32: ^3.1.3 + win32_registry: ^1.0.2 + window_manager: ^0.2.7 + windows_single_instance: ^0.1.5 + windows_taskbar: ^1.1.1 + xml: ^6.1.0 + yaml: ^3.1.1 + jwt_decoder: ^2.0.1 + +dependency_overrides: + firebase_core_platform_interface: 4.5.1 + +dev_dependencies: + build_runner: ^2.3.3 + chopper_generator: ^6.0.0 + faker: ^2.1.0 + flutter_gen_runner: ^5.1.0+1 + flutter_launcher_icons: ^0.11.0 + flutter_lints: ^2.0.1 + flutter_native_splash: ^2.2.16 + flutter_test: + sdk: flutter + freezed: ^2.3.2 + go_router_builder: ^1.0.15 + isar_generator: ^3.0.5 + json_serializable: ^6.5.4 + mockito: ^5.3.2 + pigeon: ^0.2.4 + riverpod_generator: ^1.0.6 + swagger_dart_code_generator: ^2.9.0 + +flutter: + uses-material-design: true + assets: + - assets/graphics/logo_wide.svg + - assets/graphics/light/logo_wide.svg + - assets/graphics/splash.png + - assets/graphics/light/splash.png + - assets/graphics/icon.png + - assets/graphics/platform_icon_bethesda.png + - assets/graphics/light/platform_icon_bethesda.png + - assets/graphics/platform_icon_blizzard.png + - assets/graphics/platform_icon_ea.png + - assets/graphics/platform_icon_epic.png + - assets/graphics/platform_icon_origin.png + - assets/graphics/platform_icon_riot.png + - assets/graphics/platform_icon_steam.png + - assets/graphics/platform_icon_uplay.png + +flutter_gen: + integrations: + flutter_svg: true + +flutter_icons: + android: + generate: false + ios: + generate: false + image_path: "assets/graphics/icon.png" + web: + generate: true + windows: + generate: true + icon_size: 256 + macos: + generate: true + +flutter_native_splash: + color: "#202020" + image: assets/graphics/splash.png \ No newline at end of file diff --git a/launchpad_app/test/widget_test.dart b/launchpad_app/test/widget_test.dart new file mode 100644 index 00000000..1ea4e9dc --- /dev/null +++ b/launchpad_app/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:launchpad_app/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/launchpad_app/web/favicon.png b/launchpad_app/web/favicon.png new file mode 100644 index 00000000..5320fe70 Binary files /dev/null and b/launchpad_app/web/favicon.png differ diff --git a/launchpad_app/web/icons/Icon-192.png b/launchpad_app/web/icons/Icon-192.png new file mode 100644 index 00000000..8e4277fe Binary files /dev/null and b/launchpad_app/web/icons/Icon-192.png differ diff --git a/launchpad_app/web/icons/Icon-512.png b/launchpad_app/web/icons/Icon-512.png new file mode 100644 index 00000000..e41fd860 Binary files /dev/null and b/launchpad_app/web/icons/Icon-512.png differ diff --git a/launchpad_app/web/icons/Icon-maskable-192.png b/launchpad_app/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000..8e4277fe Binary files /dev/null and b/launchpad_app/web/icons/Icon-maskable-192.png differ diff --git a/launchpad_app/web/icons/Icon-maskable-512.png b/launchpad_app/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000..e41fd860 Binary files /dev/null and b/launchpad_app/web/icons/Icon-maskable-512.png differ diff --git a/launchpad_app/web/index.html b/launchpad_app/web/index.html new file mode 100644 index 00000000..074b1d25 --- /dev/null +++ b/launchpad_app/web/index.html @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + launchpad_app + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launchpad_app/web/manifest.json b/launchpad_app/web/manifest.json new file mode 100644 index 00000000..ac8f5489 --- /dev/null +++ b/launchpad_app/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "launchpad_app", + "short_name": "launchpad_app", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} \ No newline at end of file diff --git a/launchpad_app/web/splash/img/dark-1x.png b/launchpad_app/web/splash/img/dark-1x.png new file mode 100644 index 00000000..16b9e0b0 Binary files /dev/null and b/launchpad_app/web/splash/img/dark-1x.png differ diff --git a/launchpad_app/web/splash/img/dark-2x.png b/launchpad_app/web/splash/img/dark-2x.png new file mode 100644 index 00000000..02a3a563 Binary files /dev/null and b/launchpad_app/web/splash/img/dark-2x.png differ diff --git a/launchpad_app/web/splash/img/dark-3x.png b/launchpad_app/web/splash/img/dark-3x.png new file mode 100644 index 00000000..417a818f Binary files /dev/null and b/launchpad_app/web/splash/img/dark-3x.png differ diff --git a/launchpad_app/web/splash/img/dark-4x.png b/launchpad_app/web/splash/img/dark-4x.png new file mode 100644 index 00000000..9983337e Binary files /dev/null and b/launchpad_app/web/splash/img/dark-4x.png differ diff --git a/launchpad_app/web/splash/img/light-1x.png b/launchpad_app/web/splash/img/light-1x.png new file mode 100644 index 00000000..16b9e0b0 Binary files /dev/null and b/launchpad_app/web/splash/img/light-1x.png differ diff --git a/launchpad_app/web/splash/img/light-2x.png b/launchpad_app/web/splash/img/light-2x.png new file mode 100644 index 00000000..02a3a563 Binary files /dev/null and b/launchpad_app/web/splash/img/light-2x.png differ diff --git a/launchpad_app/web/splash/img/light-3x.png b/launchpad_app/web/splash/img/light-3x.png new file mode 100644 index 00000000..417a818f Binary files /dev/null and b/launchpad_app/web/splash/img/light-3x.png differ diff --git a/launchpad_app/web/splash/img/light-4x.png b/launchpad_app/web/splash/img/light-4x.png new file mode 100644 index 00000000..9983337e Binary files /dev/null and b/launchpad_app/web/splash/img/light-4x.png differ diff --git a/launchpad_app/web/splash/splash.js b/launchpad_app/web/splash/splash.js new file mode 100644 index 00000000..3b6ed11f --- /dev/null +++ b/launchpad_app/web/splash/splash.js @@ -0,0 +1,5 @@ +function removeSplashFromWeb() { + document.getElementById("splash")?.remove(); + document.getElementById("splash-branding")?.remove(); + document.body.style.background = "transparent"; +} diff --git a/launchpad_app/web/splash/style.css b/launchpad_app/web/splash/style.css new file mode 100644 index 00000000..a3d1fa4b --- /dev/null +++ b/launchpad_app/web/splash/style.css @@ -0,0 +1,56 @@ +html { + height: 100% +} + +body { + margin: 0; + min-height: 100%; + background-color: #202020; + background-size: 100% 100%; +} + +.center { + margin: 0; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +.contain { + display:block; + width:100%; height:100%; + object-fit: contain; +} + +.stretch { + display:block; + width:100%; height:100%; +} + +.cover { + display:block; + width:100%; height:100%; + object-fit: cover; +} + +.bottom { + position: absolute; + bottom: 0; + left: 50%; + -ms-transform: translate(-50%, 0); + transform: translate(-50%, 0); +} + +.bottomLeft { + position: absolute; + bottom: 0; + left: 0; +} + +.bottomRight { + position: absolute; + bottom: 0; + right: 0; +} diff --git a/launchpad_app/windows/.gitignore b/launchpad_app/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/launchpad_app/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/launchpad_app/windows/CMakeLists.txt b/launchpad_app/windows/CMakeLists.txt new file mode 100644 index 00000000..fbf84ffb --- /dev/null +++ b/launchpad_app/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(launchpad_app LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "launchpad_app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/launchpad_app/windows/flutter/CMakeLists.txt b/launchpad_app/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/launchpad_app/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/launchpad_app/windows/flutter/generated_plugin_registrant.cc b/launchpad_app/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..8b1aba24 --- /dev/null +++ b/launchpad_app/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,83 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + CatcherPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CatcherPlugin")); + ClipboardWatcherPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ClipboardWatcherPlugin")); + ContextualMenuPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ContextualMenuPlugin")); + DesktopDropPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopDropPlugin")); + DesktopWebviewAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopWebviewAuthPlugin")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); + FlutterAcrylicPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterAcrylicPlugin")); + HotkeyManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("HotkeyManagerPlugin")); + IsarFlutterLibsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); + LaunchpadDotnetPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LaunchpadDotnetPluginCApi")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); + LocalNotifierPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalNotifierPlugin")); + NetworkInfoPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("NetworkInfoPlusWindowsPlugin")); + ProtocolHandlerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ProtocolHandlerPlugin")); + ScreenCapturerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenCapturerPlugin")); + ScreenRetrieverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); + ScreenTextExtractorPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenTextExtractorPlugin")); + SystemThemePluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SystemThemePlugin")); + TrayManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("TrayManagerPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); + WindowsSingleInstancePluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowsSingleInstancePlugin")); + WindowsTaskbarPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowsTaskbarPlugin")); +} diff --git a/launchpad_app/windows/flutter/generated_plugin_registrant.h b/launchpad_app/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/launchpad_app/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/launchpad_app/windows/flutter/generated_plugins.cmake b/launchpad_app/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..e9e704fa --- /dev/null +++ b/launchpad_app/windows/flutter/generated_plugins.cmake @@ -0,0 +1,47 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + app_links + catcher + clipboard_watcher + contextual_menu + desktop_drop + desktop_webview_auth + file_selector_windows + flutter_acrylic + hotkey_manager + isar_flutter_libs + launchpad_dotnet + local_auth_windows + local_notifier + network_info_plus + protocol_handler + screen_capturer + screen_retriever + screen_text_extractor + system_theme + tray_manager + url_launcher_windows + window_manager + windows_single_instance + windows_taskbar +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/launchpad_app/windows/packaging/exe/make_config.yaml b/launchpad_app/windows/packaging/exe/make_config.yaml new file mode 100644 index 00000000..6dfe9d8a --- /dev/null +++ b/launchpad_app/windows/packaging/exe/make_config.yaml @@ -0,0 +1,8 @@ +app_id: 03C1FD4A-E37E-4F24-8068-CC6921A2CAB1 +publisher: VolantisDev +publisher_url: https://github.com/VolantisDev/Launchpad +display_name: Launchpad - Game Launching Multitool +create_desktop_icon: true +install_dir_name: Launchpad +locales: + - en diff --git a/launchpad_app/windows/runner/CMakeLists.txt b/launchpad_app/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..17411a8a --- /dev/null +++ b/launchpad_app/windows/runner/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/launchpad_app/windows/runner/Runner.rc b/launchpad_app/windows/runner/Runner.rc new file mode 100644 index 00000000..8903b609 --- /dev/null +++ b/launchpad_app/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Volantis Development" "\0" + VALUE "FileDescription", "launchpad_app" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "launchpad_app" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 Volantis Development. All rights reserved." "\0" + VALUE "OriginalFilename", "launchpad_app.exe" "\0" + VALUE "ProductName", "launchpad_app" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/launchpad_app/windows/runner/flutter_window.cpp b/launchpad_app/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/launchpad_app/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/launchpad_app/windows/runner/flutter_window.h b/launchpad_app/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/launchpad_app/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/launchpad_app/windows/runner/main.cpp b/launchpad_app/windows/runner/main.cpp new file mode 100644 index 00000000..b4f9a674 --- /dev/null +++ b/launchpad_app/windows/runner/main.cpp @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +#include + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", L"protocol_handler_example"); + if (hwnd != NULL) { + DispatchToProtocolHandler(hwnd); + + ::ShowWindow(hwnd, SW_NORMAL); + ::SetForegroundWindow(hwnd); + return EXIT_FAILURE; + } + + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"launchpad_app", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/launchpad_app/windows/runner/resource.h b/launchpad_app/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/launchpad_app/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/launchpad_app/windows/runner/resources/app_icon.ico b/launchpad_app/windows/runner/resources/app_icon.ico new file mode 100644 index 00000000..133986d6 Binary files /dev/null and b/launchpad_app/windows/runner/resources/app_icon.ico differ diff --git a/launchpad_app/windows/runner/runner.exe.manifest b/launchpad_app/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/launchpad_app/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/launchpad_app/windows/runner/utils.cpp b/launchpad_app/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/launchpad_app/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/launchpad_app/windows/runner/utils.h b/launchpad_app/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/launchpad_app/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/launchpad_app/windows/runner/win32_window.cpp b/launchpad_app/windows/runner/win32_window.cpp new file mode 100644 index 00000000..3ce2ec5e --- /dev/null +++ b/launchpad_app/windows/runner/win32_window.cpp @@ -0,0 +1,286 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +#include "app_links/app_links_plugin_c_api.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + if (SendAppLinkToInstance(title)) { + return false; + } + + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +bool Win32Window::SendAppLinkToInstance(const std::wstring& title) { + // Find our exact window + HWND hwnd = ::FindWindow(kWindowClassName, title.c_str()); + + if (hwnd) { + // Dispatch new link to current window + SendAppLink(hwnd); + + // (Optional) Restore our window to front in same state + WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) }; + GetWindowPlacement(hwnd, &place); + + switch(place.showCmd) { + case SW_SHOWMAXIMIZED: + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + break; + case SW_SHOWMINIMIZED: + ShowWindow(hwnd, SW_RESTORE); + break; + default: + ShowWindow(hwnd, SW_NORMAL); + break; + } + + SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); + SetForegroundWindow(hwnd); + // END Restore + + // Window has been found, don't create another one. + return true; + } + + return false; +} \ No newline at end of file diff --git a/launchpad_app/windows/runner/win32_window.h b/launchpad_app/windows/runner/win32_window.h new file mode 100644 index 00000000..9cfea10a --- /dev/null +++ b/launchpad_app/windows/runner/win32_window.h @@ -0,0 +1,103 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; + + // Dispatches link if any. + // This method enables our app to be with a single instance too. + // This is mandatory if you want to catch further links in same app. + bool SendAppLinkToInstance(const std::wstring& title); +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/launchpad_dotnet/.gitignore b/launchpad_dotnet/.gitignore new file mode 100644 index 00000000..b9d64f90 --- /dev/null +++ b/launchpad_dotnet/.gitignore @@ -0,0 +1,57 @@ +/bin +/obj + +assets/bin/debug/* +!assets/bin/debug/LaunchpadDotNet.dll +!assets/bin/debug/LaunchpadDotNet.pdb +!assets/bin/debug/LaunchpadDotNet.deps.json + +assets/bin/release/* +!assets/bin/release/LaunchpadDotNet.dll +!assets/bin/release/LaunchpadDotNet.pdb +!assets/bin/release/LaunchpadDotNet.deps.json + +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +.env.local +.env.*.local diff --git a/launchpad_dotnet/.metadata b/launchpad_dotnet/.metadata new file mode 100644 index 00000000..69541b86 --- /dev/null +++ b/launchpad_dotnet/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 135454af32477f815a7525073027a3ff9eff1bfd + channel: stable + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: windows + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/launchpad_dotnet/CHANGELOG.md b/launchpad_dotnet/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/launchpad_dotnet/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/launchpad_dotnet/LICENSE b/launchpad_dotnet/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/launchpad_dotnet/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/launchpad_dotnet/LICENSE.txt b/launchpad_dotnet/LICENSE.txt new file mode 100644 index 00000000..a9fb11a3 --- /dev/null +++ b/launchpad_dotnet/LICENSE.txt @@ -0,0 +1,28 @@ +Parts of launchpad_dotnet were copied from https://github.com/bertjohnson/extracticon. +The license for the extracticon library is below. + +### License ### + +ExtractIcon (https://github.com/bertjohnson/extracticon) + +Licensed according to the MIT License (http://mit-license.org/). + +Copyright © Bert Johnson (https://bertjohnson.com/) of Allcloud Inc. (https://allcloud.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 the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/launchpad_dotnet/LaunchpadDotNet.csproj b/launchpad_dotnet/LaunchpadDotNet.csproj new file mode 100644 index 00000000..fde714ef --- /dev/null +++ b/launchpad_dotnet/LaunchpadDotNet.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + true + true + + + + + + + + + diff --git a/launchpad_dotnet/LaunchpadFiles.cs b/launchpad_dotnet/LaunchpadFiles.cs new file mode 100644 index 00000000..afc8c2c7 --- /dev/null +++ b/launchpad_dotnet/LaunchpadFiles.cs @@ -0,0 +1,57 @@ +using System.Runtime.InteropServices; +using TsudaKageyu; + + +namespace LaunchpadDotNet +{ + + public class LaunchpadFiles + { + [UnmanagedCallersOnly(EntryPoint = "extract_icon")] + public static int ExtractIcon(IntPtr inputPathPtr, IntPtr outputPathPtr, bool overwrite = false) + { + string? inputPath = Marshal.PtrToStringAnsi(inputPathPtr); + string? outputPath = Marshal.PtrToStringAnsi(outputPathPtr); + + if (inputPath == null || outputPath == null) + { + return -1; + } + + try + { + if (!File.Exists(inputPath)) + { + return -1; + } + + if (File.Exists(outputPath)) + { + if (!overwrite) + { + return -1; + } + + File.Delete(outputPath); + } + + IconExtractor extractor = new IconExtractor(inputPath); + + if (extractor.Count == 0) { + return -1; + } + + using (var fs = File.OpenWrite(outputPath)) + { + extractor.Save(0, fs); + } + } + catch + { + return -1; + } + + return 1; + } + } +} diff --git a/launchpad_dotnet/README.md b/launchpad_dotnet/README.md new file mode 100644 index 00000000..18924c5f --- /dev/null +++ b/launchpad_dotnet/README.md @@ -0,0 +1,15 @@ +# launchpad_dotnet + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/launchpad_dotnet/analysis_options.yaml b/launchpad_dotnet/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/launchpad_dotnet/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.deps.json b/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.deps.json new file mode 100644 index 00000000..343baced --- /dev/null +++ b/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.deps.json @@ -0,0 +1,1107 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v6.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v6.0": {}, + ".NETCoreApp,Version=v6.0/win-x64": { + "LaunchpadDotNet/1.0.0": { + "dependencies": { + "Microsoft.DotNet.ILCompiler": "7.0.0", + "WinCopies.IconExtractor": "1.0.3-rc", + "runtimepack.Microsoft.NETCore.App.Runtime.win-x64": "6.0.5" + }, + "runtime": { + "LaunchpadDotNet.dll": {} + } + }, + "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/6.0.5": { + "runtime": { + "Microsoft.CSharp.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "Microsoft.VisualBasic.Core.dll": { + "assemblyVersion": "11.0.0.0", + "fileVersion": "11.100.522.21309" + }, + "Microsoft.VisualBasic.dll": { + "assemblyVersion": "10.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "Microsoft.Win32.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "Microsoft.Win32.Registry.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.AppContext.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Buffers.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.Concurrent.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.Immutable.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.NonGeneric.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.Specialized.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.Annotations.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.DataAnnotations.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.EventBasedAsync.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.TypeConverter.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Configuration.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Console.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Core.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Data.Common.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Data.DataSetExtensions.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Data.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Contracts.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Debug.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.DiagnosticSource.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.FileVersionInfo.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Process.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.StackTrace.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.TextWriterTraceListener.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Tools.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.TraceSource.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Tracing.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Drawing.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Drawing.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Dynamic.Runtime.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Formats.Asn1.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Globalization.Calendars.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Globalization.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Globalization.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.Brotli.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.FileSystem.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.ZipFile.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.AccessControl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.DriveInfo.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.Watcher.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.IsolatedStorage.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.MemoryMappedFiles.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Pipes.AccessControl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Pipes.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.UnmanagedMemoryStream.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.Expressions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.Parallel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.Queryable.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Memory.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Http.Json.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Http.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.HttpListener.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Mail.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.NameResolution.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.NetworkInformation.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Ping.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Quic.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Requests.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Security.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.ServicePoint.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Sockets.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebClient.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebHeaderCollection.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebProxy.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebSockets.Client.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebSockets.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Numerics.Vectors.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Numerics.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ObjectModel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.CoreLib.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.DataContractSerialization.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.Uri.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.Xml.Linq.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.Xml.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.DispatchProxy.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Emit.ILGeneration.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Emit.Lightweight.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Emit.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Metadata.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.TypeExtensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Resources.Reader.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Resources.ResourceManager.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Resources.Writer.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.CompilerServices.Unsafe.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.CompilerServices.VisualC.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Handles.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.InteropServices.RuntimeInformation.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.InteropServices.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Intrinsics.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Loader.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Numerics.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Formatters.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Json.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Xml.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.AccessControl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Claims.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Algorithms.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Cng.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Csp.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Encoding.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.OpenSsl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.X509Certificates.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Principal.Windows.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Principal.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.SecureString.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ServiceModel.Web.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ServiceProcess.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encoding.CodePages.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encoding.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encoding.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encodings.Web.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Json.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.RegularExpressions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Channels.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Overlapped.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.Dataflow.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.Parallel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Thread.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.ThreadPool.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Timer.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Transactions.Local.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Transactions.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ValueTuple.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Web.HttpUtility.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Web.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Windows.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.Linq.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.ReaderWriter.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.Serialization.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XDocument.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XPath.XDocument.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XPath.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XmlDocument.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XmlSerializer.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "mscorlib.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "netstandard.dll": { + "assemblyVersion": "2.1.0.0", + "fileVersion": "6.0.522.21309" + } + }, + "native": { + "Microsoft.DiaSymReader.Native.amd64.dll": { + "fileVersion": "14.28.29715.1" + }, + "System.IO.Compression.Native.dll": { + "fileVersion": "42.42.42.42424" + }, + "api-ms-win-core-console-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-console-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-datetime-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-debug-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-errorhandling-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-fibers-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-file-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-file-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-file-l2-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-handle-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-heap-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-interlocked-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-libraryloader-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-localization-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-memory-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-namedpipe-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-processenvironment-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-processthreads-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-processthreads-l1-1-1.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-profile-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-rtlsupport-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-string-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-synch-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-synch-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-sysinfo-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-timezone-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-util-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-conio-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-convert-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-environment-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-filesystem-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-heap-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-locale-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-math-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-multibyte-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-private-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-process-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-runtime-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-stdio-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-string-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-time-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-utility-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "clretwrc.dll": { + "fileVersion": "6.0.522.21309" + }, + "clrjit.dll": { + "fileVersion": "6.0.522.21309" + }, + "coreclr.dll": { + "fileVersion": "6.0.522.21309" + }, + "createdump.exe": { + "fileVersion": "6.0.522.21309" + }, + "dbgshim.dll": { + "fileVersion": "6.0.522.21309" + }, + "hostfxr.dll": { + "fileVersion": "6.0.522.21309" + }, + "hostpolicy.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscordaccore.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscordaccore_amd64_amd64_6.0.522.21309.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscordbi.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscorrc.dll": { + "fileVersion": "6.0.522.21309" + }, + "msquic.dll": { + "fileVersion": "1.7.0.0" + }, + "ucrtbase.dll": { + "fileVersion": "10.0.22000.194" + } + } + }, + "Microsoft.DotNet.ILCompiler/7.0.0": { + "dependencies": { + "runtime.win-x64.Microsoft.DotNet.ILCompiler": "7.0.0" + } + }, + "Microsoft.NETCore.Platforms/3.0.0": {}, + "Microsoft.Win32.SystemEvents/4.6.0": { + "dependencies": { + "Microsoft.NETCore.Platforms": "3.0.0" + } + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler/7.0.0": {}, + "System.Drawing.Common/4.6.0": { + "dependencies": { + "Microsoft.NETCore.Platforms": "3.0.0", + "Microsoft.Win32.SystemEvents": "4.6.0" + } + }, + "System.Reflection.Emit.ILGeneration/4.6.0": {}, + "System.Reflection.Emit.Lightweight/4.6.0": {}, + "WinCopies.IconExtractor/1.0.3-rc": { + "dependencies": { + "System.Drawing.Common": "4.6.0", + "System.Reflection.Emit.ILGeneration": "4.6.0", + "System.Reflection.Emit.Lightweight": "4.6.0", + "WinCopies.WindowsAPICodePack.Win32Native": "2.0.0-preview6" + } + }, + "WinCopies.Util/2.2.0-preview3": {}, + "WinCopies.WindowsAPICodePack.Win32Native/2.0.0-preview6": { + "dependencies": { + "WinCopies.Util": "2.2.0-preview3", + "WinCopies.WindowsAPICodePack.Win32Native.Consts": "2.0.0-preview6" + } + }, + "WinCopies.WindowsAPICodePack.Win32Native.Consts/2.0.0-preview6": {} + } + }, + "libraries": { + "LaunchpadDotNet/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/6.0.5": { + "type": "runtimepack", + "serviceable": false, + "sha512": "" + }, + "Microsoft.DotNet.ILCompiler/7.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-3utooKHhRKxgSLdViNbiKqeqczdxCr5TdXp3pe1zmOLNnfSfkMVubjBtm4wH6ZnmXkU3zGzbyE1P/HgffoiJWQ==", + "path": "microsoft.dotnet.ilcompiler/7.0.0", + "hashPath": "microsoft.dotnet.ilcompiler.7.0.0.nupkg.sha512" + }, + "Microsoft.NETCore.Platforms/3.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==", + "path": "microsoft.netcore.platforms/3.0.0", + "hashPath": "microsoft.netcore.platforms.3.0.0.nupkg.sha512" + }, + "Microsoft.Win32.SystemEvents/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Edg+pFW5C8WJb680Za2kTV8TqUi6Ahl/WldRVoOVJ23UQLpDHFspa+umgFjkWZw24ETsU99Cg+ErZz683M4chg==", + "path": "microsoft.win32.systemevents/4.6.0", + "hashPath": "microsoft.win32.systemevents.4.6.0.nupkg.sha512" + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler/7.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-tHV8gqgLn9THsC3a/D6zyrWwihBB53mm/L1J3DSklvAXvmRCxpWp2ULS1i8SJ48s5eXw3qbpoFOIfEqhXqldDg==", + "path": "runtime.win-x64.microsoft.dotnet.ilcompiler/7.0.0", + "hashPath": "runtime.win-x64.microsoft.dotnet.ilcompiler.7.0.0.nupkg.sha512" + }, + "System.Drawing.Common/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-2A3spjjoPZnvpVh/sDTzd+0H8ZqTdr+hH/6obB8MMfG81EJ85PmxCKDBxhBVQiA25PliKAZ1sKogDcq9mSnFEA==", + "path": "system.drawing.common/4.6.0", + "hashPath": "system.drawing.common.4.6.0.nupkg.sha512" + }, + "System.Reflection.Emit.ILGeneration/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-z3r/uEhMj+1wUNUa/EA2bWxEL9M8bgFt/22xCOQivES28ex3zWxE+2j0gQF/mdRwzlr0jir64HVlwV5J4zTOfw==", + "path": "system.reflection.emit.ilgeneration/4.6.0", + "hashPath": "system.reflection.emit.ilgeneration.4.6.0.nupkg.sha512" + }, + "System.Reflection.Emit.Lightweight/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-j/V5HVvxvBQ7uubYD0PptQW2KGsi1Pc2kZ9yfwLixv3ADdjL/4M78KyC5e+ymW612DY8ZE4PFoZmWpoNmN2mqg==", + "path": "system.reflection.emit.lightweight/4.6.0", + "hashPath": "system.reflection.emit.lightweight.4.6.0.nupkg.sha512" + }, + "WinCopies.IconExtractor/1.0.3-rc": { + "type": "package", + "serviceable": true, + "sha512": "sha512-TkFNV/dpztwqZZTwUNCmCkOVMSLx+iIyXhlW5HydbfkJubiY5gsPRtB/yoe/2DGau/KYIEI9TWcfeHwwbbaTfg==", + "path": "wincopies.iconextractor/1.0.3-rc", + "hashPath": "wincopies.iconextractor.1.0.3-rc.nupkg.sha512" + }, + "WinCopies.Util/2.2.0-preview3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-6kx5OWUZ/7x2BdPqmGSov92D7Y8E/wyBIs/IDqJlw5K6XHL2CA/g8U3/SWeWhtzjmxEJH4tRH2uTHZggBjzExA==", + "path": "wincopies.util/2.2.0-preview3", + "hashPath": "wincopies.util.2.2.0-preview3.nupkg.sha512" + }, + "WinCopies.WindowsAPICodePack.Win32Native/2.0.0-preview6": { + "type": "package", + "serviceable": true, + "sha512": "sha512-fDZFPhb6qF14seIMgEh0KXwos+eu4bLMPnJs55pkLINHwy/idWmvKARDsqAhXEKvaQtsfkZZVzOKTVBHwXb6SA==", + "path": "wincopies.windowsapicodepack.win32native/2.0.0-preview6", + "hashPath": "wincopies.windowsapicodepack.win32native.2.0.0-preview6.nupkg.sha512" + }, + "WinCopies.WindowsAPICodePack.Win32Native.Consts/2.0.0-preview6": { + "type": "package", + "serviceable": true, + "sha512": "sha512-eYl6TQJ0S9iYJ5l7IDXeiRJne0hjInCRMj041nhZTBWvcnrE51NMQvtDDeStP5Si2azz3AANkdktepzRrH+nqQ==", + "path": "wincopies.windowsapicodepack.win32native.consts/2.0.0-preview6", + "hashPath": "wincopies.windowsapicodepack.win32native.consts.2.0.0-preview6.nupkg.sha512" + } + }, + "runtimes": { + "win-x64": [ + "win", + "any", + "base" + ], + "win-x64-aot": [ + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win10-x64": [ + "win10", + "win81-x64", + "win81", + "win8-x64", + "win8", + "win7-x64", + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win10-x64-aot": [ + "win10-aot", + "win10-x64", + "win10", + "win81-x64-aot", + "win81-aot", + "win81-x64", + "win81", + "win8-x64-aot", + "win8-aot", + "win8-x64", + "win8", + "win7-x64-aot", + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win7-x64": [ + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win7-x64-aot": [ + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win8-x64": [ + "win8", + "win7-x64", + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win8-x64-aot": [ + "win8-aot", + "win8-x64", + "win8", + "win7-x64-aot", + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win81-x64": [ + "win81", + "win8-x64", + "win8", + "win7-x64", + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win81-x64-aot": [ + "win81-aot", + "win81-x64", + "win81", + "win8-x64-aot", + "win8-aot", + "win8-x64", + "win8", + "win7-x64-aot", + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ] + } +} \ No newline at end of file diff --git a/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.dll b/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.dll new file mode 100644 index 00000000..d087a8bd Binary files /dev/null and b/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.dll differ diff --git a/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.pdb b/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.pdb new file mode 100644 index 00000000..cc05f29b Binary files /dev/null and b/launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.pdb differ diff --git a/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.deps.json b/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.deps.json new file mode 100644 index 00000000..343baced --- /dev/null +++ b/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.deps.json @@ -0,0 +1,1107 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v6.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v6.0": {}, + ".NETCoreApp,Version=v6.0/win-x64": { + "LaunchpadDotNet/1.0.0": { + "dependencies": { + "Microsoft.DotNet.ILCompiler": "7.0.0", + "WinCopies.IconExtractor": "1.0.3-rc", + "runtimepack.Microsoft.NETCore.App.Runtime.win-x64": "6.0.5" + }, + "runtime": { + "LaunchpadDotNet.dll": {} + } + }, + "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/6.0.5": { + "runtime": { + "Microsoft.CSharp.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "Microsoft.VisualBasic.Core.dll": { + "assemblyVersion": "11.0.0.0", + "fileVersion": "11.100.522.21309" + }, + "Microsoft.VisualBasic.dll": { + "assemblyVersion": "10.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "Microsoft.Win32.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "Microsoft.Win32.Registry.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.AppContext.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Buffers.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.Concurrent.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.Immutable.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.NonGeneric.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.Specialized.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Collections.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.Annotations.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.DataAnnotations.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.EventBasedAsync.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.TypeConverter.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ComponentModel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Configuration.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Console.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Core.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Data.Common.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Data.DataSetExtensions.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Data.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Contracts.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Debug.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.DiagnosticSource.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.FileVersionInfo.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Process.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.StackTrace.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.TextWriterTraceListener.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Tools.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.TraceSource.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Diagnostics.Tracing.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Drawing.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Drawing.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Dynamic.Runtime.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Formats.Asn1.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Globalization.Calendars.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Globalization.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Globalization.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.Brotli.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.FileSystem.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.ZipFile.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Compression.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.AccessControl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.DriveInfo.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.Watcher.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.FileSystem.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.IsolatedStorage.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.MemoryMappedFiles.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Pipes.AccessControl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.Pipes.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.UnmanagedMemoryStream.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.IO.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.Expressions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.Parallel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.Queryable.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Linq.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Memory.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Http.Json.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Http.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.HttpListener.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Mail.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.NameResolution.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.NetworkInformation.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Ping.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Quic.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Requests.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Security.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.ServicePoint.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.Sockets.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebClient.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebHeaderCollection.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebProxy.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebSockets.Client.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.WebSockets.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Net.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Numerics.Vectors.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Numerics.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ObjectModel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.CoreLib.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.DataContractSerialization.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.Uri.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.Xml.Linq.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Private.Xml.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.DispatchProxy.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Emit.ILGeneration.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Emit.Lightweight.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Emit.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Metadata.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.TypeExtensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Reflection.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Resources.Reader.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Resources.ResourceManager.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Resources.Writer.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.CompilerServices.Unsafe.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.CompilerServices.VisualC.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Handles.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.InteropServices.RuntimeInformation.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.InteropServices.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Intrinsics.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Loader.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Numerics.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Formatters.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Json.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.Xml.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.Serialization.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Runtime.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.AccessControl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Claims.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Algorithms.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Cng.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Csp.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Encoding.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.OpenSsl.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Cryptography.X509Certificates.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Principal.Windows.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.Principal.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.SecureString.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Security.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ServiceModel.Web.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ServiceProcess.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encoding.CodePages.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encoding.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encoding.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Encodings.Web.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.Json.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Text.RegularExpressions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Channels.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Overlapped.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.Dataflow.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.Extensions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.Parallel.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Tasks.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Thread.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.ThreadPool.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.Timer.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Threading.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Transactions.Local.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Transactions.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.ValueTuple.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Web.HttpUtility.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Web.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Windows.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.Linq.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.ReaderWriter.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.Serialization.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XDocument.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XPath.XDocument.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XPath.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XmlDocument.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.XmlSerializer.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.Xml.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "System.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "mscorlib.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "6.0.522.21309" + }, + "netstandard.dll": { + "assemblyVersion": "2.1.0.0", + "fileVersion": "6.0.522.21309" + } + }, + "native": { + "Microsoft.DiaSymReader.Native.amd64.dll": { + "fileVersion": "14.28.29715.1" + }, + "System.IO.Compression.Native.dll": { + "fileVersion": "42.42.42.42424" + }, + "api-ms-win-core-console-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-console-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-datetime-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-debug-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-errorhandling-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-fibers-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-file-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-file-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-file-l2-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-handle-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-heap-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-interlocked-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-libraryloader-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-localization-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-memory-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-namedpipe-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-processenvironment-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-processthreads-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-processthreads-l1-1-1.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-profile-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-rtlsupport-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-string-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-synch-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-synch-l1-2-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-sysinfo-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-timezone-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-core-util-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-conio-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-convert-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-environment-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-filesystem-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-heap-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-locale-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-math-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-multibyte-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-private-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-process-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-runtime-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-stdio-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-string-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-time-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "api-ms-win-crt-utility-l1-1-0.dll": { + "fileVersion": "10.0.22000.194" + }, + "clretwrc.dll": { + "fileVersion": "6.0.522.21309" + }, + "clrjit.dll": { + "fileVersion": "6.0.522.21309" + }, + "coreclr.dll": { + "fileVersion": "6.0.522.21309" + }, + "createdump.exe": { + "fileVersion": "6.0.522.21309" + }, + "dbgshim.dll": { + "fileVersion": "6.0.522.21309" + }, + "hostfxr.dll": { + "fileVersion": "6.0.522.21309" + }, + "hostpolicy.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscordaccore.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscordaccore_amd64_amd64_6.0.522.21309.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscordbi.dll": { + "fileVersion": "6.0.522.21309" + }, + "mscorrc.dll": { + "fileVersion": "6.0.522.21309" + }, + "msquic.dll": { + "fileVersion": "1.7.0.0" + }, + "ucrtbase.dll": { + "fileVersion": "10.0.22000.194" + } + } + }, + "Microsoft.DotNet.ILCompiler/7.0.0": { + "dependencies": { + "runtime.win-x64.Microsoft.DotNet.ILCompiler": "7.0.0" + } + }, + "Microsoft.NETCore.Platforms/3.0.0": {}, + "Microsoft.Win32.SystemEvents/4.6.0": { + "dependencies": { + "Microsoft.NETCore.Platforms": "3.0.0" + } + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler/7.0.0": {}, + "System.Drawing.Common/4.6.0": { + "dependencies": { + "Microsoft.NETCore.Platforms": "3.0.0", + "Microsoft.Win32.SystemEvents": "4.6.0" + } + }, + "System.Reflection.Emit.ILGeneration/4.6.0": {}, + "System.Reflection.Emit.Lightweight/4.6.0": {}, + "WinCopies.IconExtractor/1.0.3-rc": { + "dependencies": { + "System.Drawing.Common": "4.6.0", + "System.Reflection.Emit.ILGeneration": "4.6.0", + "System.Reflection.Emit.Lightweight": "4.6.0", + "WinCopies.WindowsAPICodePack.Win32Native": "2.0.0-preview6" + } + }, + "WinCopies.Util/2.2.0-preview3": {}, + "WinCopies.WindowsAPICodePack.Win32Native/2.0.0-preview6": { + "dependencies": { + "WinCopies.Util": "2.2.0-preview3", + "WinCopies.WindowsAPICodePack.Win32Native.Consts": "2.0.0-preview6" + } + }, + "WinCopies.WindowsAPICodePack.Win32Native.Consts/2.0.0-preview6": {} + } + }, + "libraries": { + "LaunchpadDotNet/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/6.0.5": { + "type": "runtimepack", + "serviceable": false, + "sha512": "" + }, + "Microsoft.DotNet.ILCompiler/7.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-3utooKHhRKxgSLdViNbiKqeqczdxCr5TdXp3pe1zmOLNnfSfkMVubjBtm4wH6ZnmXkU3zGzbyE1P/HgffoiJWQ==", + "path": "microsoft.dotnet.ilcompiler/7.0.0", + "hashPath": "microsoft.dotnet.ilcompiler.7.0.0.nupkg.sha512" + }, + "Microsoft.NETCore.Platforms/3.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==", + "path": "microsoft.netcore.platforms/3.0.0", + "hashPath": "microsoft.netcore.platforms.3.0.0.nupkg.sha512" + }, + "Microsoft.Win32.SystemEvents/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Edg+pFW5C8WJb680Za2kTV8TqUi6Ahl/WldRVoOVJ23UQLpDHFspa+umgFjkWZw24ETsU99Cg+ErZz683M4chg==", + "path": "microsoft.win32.systemevents/4.6.0", + "hashPath": "microsoft.win32.systemevents.4.6.0.nupkg.sha512" + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler/7.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-tHV8gqgLn9THsC3a/D6zyrWwihBB53mm/L1J3DSklvAXvmRCxpWp2ULS1i8SJ48s5eXw3qbpoFOIfEqhXqldDg==", + "path": "runtime.win-x64.microsoft.dotnet.ilcompiler/7.0.0", + "hashPath": "runtime.win-x64.microsoft.dotnet.ilcompiler.7.0.0.nupkg.sha512" + }, + "System.Drawing.Common/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-2A3spjjoPZnvpVh/sDTzd+0H8ZqTdr+hH/6obB8MMfG81EJ85PmxCKDBxhBVQiA25PliKAZ1sKogDcq9mSnFEA==", + "path": "system.drawing.common/4.6.0", + "hashPath": "system.drawing.common.4.6.0.nupkg.sha512" + }, + "System.Reflection.Emit.ILGeneration/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-z3r/uEhMj+1wUNUa/EA2bWxEL9M8bgFt/22xCOQivES28ex3zWxE+2j0gQF/mdRwzlr0jir64HVlwV5J4zTOfw==", + "path": "system.reflection.emit.ilgeneration/4.6.0", + "hashPath": "system.reflection.emit.ilgeneration.4.6.0.nupkg.sha512" + }, + "System.Reflection.Emit.Lightweight/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-j/V5HVvxvBQ7uubYD0PptQW2KGsi1Pc2kZ9yfwLixv3ADdjL/4M78KyC5e+ymW612DY8ZE4PFoZmWpoNmN2mqg==", + "path": "system.reflection.emit.lightweight/4.6.0", + "hashPath": "system.reflection.emit.lightweight.4.6.0.nupkg.sha512" + }, + "WinCopies.IconExtractor/1.0.3-rc": { + "type": "package", + "serviceable": true, + "sha512": "sha512-TkFNV/dpztwqZZTwUNCmCkOVMSLx+iIyXhlW5HydbfkJubiY5gsPRtB/yoe/2DGau/KYIEI9TWcfeHwwbbaTfg==", + "path": "wincopies.iconextractor/1.0.3-rc", + "hashPath": "wincopies.iconextractor.1.0.3-rc.nupkg.sha512" + }, + "WinCopies.Util/2.2.0-preview3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-6kx5OWUZ/7x2BdPqmGSov92D7Y8E/wyBIs/IDqJlw5K6XHL2CA/g8U3/SWeWhtzjmxEJH4tRH2uTHZggBjzExA==", + "path": "wincopies.util/2.2.0-preview3", + "hashPath": "wincopies.util.2.2.0-preview3.nupkg.sha512" + }, + "WinCopies.WindowsAPICodePack.Win32Native/2.0.0-preview6": { + "type": "package", + "serviceable": true, + "sha512": "sha512-fDZFPhb6qF14seIMgEh0KXwos+eu4bLMPnJs55pkLINHwy/idWmvKARDsqAhXEKvaQtsfkZZVzOKTVBHwXb6SA==", + "path": "wincopies.windowsapicodepack.win32native/2.0.0-preview6", + "hashPath": "wincopies.windowsapicodepack.win32native.2.0.0-preview6.nupkg.sha512" + }, + "WinCopies.WindowsAPICodePack.Win32Native.Consts/2.0.0-preview6": { + "type": "package", + "serviceable": true, + "sha512": "sha512-eYl6TQJ0S9iYJ5l7IDXeiRJne0hjInCRMj041nhZTBWvcnrE51NMQvtDDeStP5Si2azz3AANkdktepzRrH+nqQ==", + "path": "wincopies.windowsapicodepack.win32native.consts/2.0.0-preview6", + "hashPath": "wincopies.windowsapicodepack.win32native.consts.2.0.0-preview6.nupkg.sha512" + } + }, + "runtimes": { + "win-x64": [ + "win", + "any", + "base" + ], + "win-x64-aot": [ + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win10-x64": [ + "win10", + "win81-x64", + "win81", + "win8-x64", + "win8", + "win7-x64", + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win10-x64-aot": [ + "win10-aot", + "win10-x64", + "win10", + "win81-x64-aot", + "win81-aot", + "win81-x64", + "win81", + "win8-x64-aot", + "win8-aot", + "win8-x64", + "win8", + "win7-x64-aot", + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win7-x64": [ + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win7-x64-aot": [ + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win8-x64": [ + "win8", + "win7-x64", + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win8-x64-aot": [ + "win8-aot", + "win8-x64", + "win8", + "win7-x64-aot", + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ], + "win81-x64": [ + "win81", + "win8-x64", + "win8", + "win7-x64", + "win7", + "win-x64", + "win", + "any", + "base" + ], + "win81-x64-aot": [ + "win81-aot", + "win81-x64", + "win81", + "win8-x64-aot", + "win8-aot", + "win8-x64", + "win8", + "win7-x64-aot", + "win7-aot", + "win7-x64", + "win7", + "win-x64-aot", + "win-aot", + "win-x64", + "win", + "aot", + "any", + "base" + ] + } +} \ No newline at end of file diff --git a/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.dll b/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.dll new file mode 100644 index 00000000..8e8d11fc Binary files /dev/null and b/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.dll differ diff --git a/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.pdb b/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.pdb new file mode 100644 index 00000000..a925657c Binary files /dev/null and b/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.pdb differ diff --git a/launchpad_dotnet/example/.gitignore b/launchpad_dotnet/example/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/launchpad_dotnet/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/launchpad_dotnet/example/README.md b/launchpad_dotnet/example/README.md new file mode 100644 index 00000000..1a229325 --- /dev/null +++ b/launchpad_dotnet/example/README.md @@ -0,0 +1,16 @@ +# launchpad_dotnet_example + +Demonstrates how to use the launchpad_dotnet plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/launchpad_dotnet/example/analysis_options.yaml b/launchpad_dotnet/example/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/launchpad_dotnet/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/launchpad_dotnet/example/lib/main.dart b/launchpad_dotnet/example/lib/main.dart new file mode 100644 index 00000000..a4314ca3 --- /dev/null +++ b/launchpad_dotnet/example/lib/main.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:launchpad_dotnet/launchpad_dotnet.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + final _launchpadDotnetPlugin = LaunchpadDotnet(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = + await _launchpadDotnetPlugin.getPlatformVersion() ?? 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/launchpad_dotnet/example/pubspec.lock b/launchpad_dotnet/example/pubspec.lock new file mode 100644 index 00000000..102d354a --- /dev/null +++ b/launchpad_dotnet/example/pubspec.lock @@ -0,0 +1,182 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + launchpad_dotnet: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" +sdks: + dart: ">=2.18.6 <3.0.0" + flutter: ">=2.5.0" diff --git a/launchpad_dotnet/example/pubspec.yaml b/launchpad_dotnet/example/pubspec.yaml new file mode 100644 index 00000000..b36c2eda --- /dev/null +++ b/launchpad_dotnet/example/pubspec.yaml @@ -0,0 +1,84 @@ +name: launchpad_dotnet_example +description: Demonstrates how to use the launchpad_dotnet plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=2.18.6 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + launchpad_dotnet: + # When depending on this package from a real application you should use: + # launchpad_dotnet: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/launchpad_dotnet/example/test/widget_test.dart b/launchpad_dotnet/example/test/widget_test.dart new file mode 100644 index 00000000..e0bf0495 --- /dev/null +++ b/launchpad_dotnet/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:launchpad_dotnet_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/launchpad_dotnet/example/windows/.gitignore b/launchpad_dotnet/example/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/launchpad_dotnet/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/launchpad_dotnet/example/windows/CMakeLists.txt b/launchpad_dotnet/example/windows/CMakeLists.txt new file mode 100644 index 00000000..9e8901d4 --- /dev/null +++ b/launchpad_dotnet/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(launchpad_dotnet_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "launchpad_dotnet_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/launchpad_dotnet/example/windows/flutter/CMakeLists.txt b/launchpad_dotnet/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/launchpad_dotnet/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/launchpad_dotnet/example/windows/flutter/generated_plugin_registrant.cc b/launchpad_dotnet/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..0311460e --- /dev/null +++ b/launchpad_dotnet/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + LaunchpadDotnetPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LaunchpadDotnetPluginCApi")); +} diff --git a/launchpad_dotnet/example/windows/flutter/generated_plugin_registrant.h b/launchpad_dotnet/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/launchpad_dotnet/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/launchpad_dotnet/example/windows/flutter/generated_plugins.cmake b/launchpad_dotnet/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..22fece7c --- /dev/null +++ b/launchpad_dotnet/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + launchpad_dotnet +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/launchpad_dotnet/example/windows/runner/CMakeLists.txt b/launchpad_dotnet/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..17411a8a --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/launchpad_dotnet/example/windows/runner/Runner.rc b/launchpad_dotnet/example/windows/runner/Runner.rc new file mode 100644 index 00000000..87154714 --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Volantis Development" "\0" + VALUE "FileDescription", "launchpad_dotnet_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "launchpad_dotnet_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 Volantis Development. All rights reserved." "\0" + VALUE "OriginalFilename", "launchpad_dotnet_example.exe" "\0" + VALUE "ProductName", "launchpad_dotnet_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/launchpad_dotnet/example/windows/runner/flutter_window.cpp b/launchpad_dotnet/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/launchpad_dotnet/example/windows/runner/flutter_window.h b/launchpad_dotnet/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/launchpad_dotnet/example/windows/runner/main.cpp b/launchpad_dotnet/example/windows/runner/main.cpp new file mode 100644 index 00000000..a80daa6e --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"launchpad_dotnet_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/launchpad_dotnet/example/windows/runner/resource.h b/launchpad_dotnet/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/launchpad_dotnet/example/windows/runner/resources/app_icon.ico b/launchpad_dotnet/example/windows/runner/resources/app_icon.ico new file mode 100644 index 00000000..c04e20ca Binary files /dev/null and b/launchpad_dotnet/example/windows/runner/resources/app_icon.ico differ diff --git a/launchpad_dotnet/example/windows/runner/runner.exe.manifest b/launchpad_dotnet/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/launchpad_dotnet/example/windows/runner/utils.cpp b/launchpad_dotnet/example/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/launchpad_dotnet/example/windows/runner/utils.h b/launchpad_dotnet/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/launchpad_dotnet/example/windows/runner/win32_window.cpp b/launchpad_dotnet/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/launchpad_dotnet/example/windows/runner/win32_window.h b/launchpad_dotnet/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/launchpad_dotnet/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/launchpad_dotnet/lib/extract_icon.dart b/launchpad_dotnet/lib/extract_icon.dart new file mode 100644 index 00000000..c126a348 --- /dev/null +++ b/launchpad_dotnet/lib/extract_icon.dart @@ -0,0 +1,29 @@ +import 'dart:ffi'; +import 'dart:io'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; + +typedef ExtractIconNative = Int32 Function( + Pointer inputPath, Pointer outputPath, Bool overwrite); +typedef ExtractIconFunc = int Function( + Pointer inputPath, Pointer outputPath, bool overwrite); + +DynamicLibrary loadLaunchpadDotnetLibrary() { + var libraryPath = kReleaseMode + ? '${Directory(Platform.resolvedExecutable).parent.path}/data/flutter_assets/packages/launchpad_dotnet/assets/bin/release/LaunchpadDotNet.dll' + : '${Directory.current.path}/../launchpad_dotnet/assets/bin/debug/LaunchpadDotNet.dll'; + + return DynamicLibrary.open(libraryPath); +} + +bool extractApplicationIcon(String inputPath, String outputPath, + {bool overwrite = false}) { + final extractIconFunc = loadLaunchpadDotnetLibrary() + .lookup>('extract_icon') + .asFunction(); + + final result = extractIconFunc( + inputPath.toNativeUtf8(), outputPath.toNativeUtf8(), overwrite); + + return (result != -1); +} diff --git a/launchpad_dotnet/lib/launchpad_dotnet.dart b/launchpad_dotnet/lib/launchpad_dotnet.dart new file mode 100644 index 00000000..5151de12 --- /dev/null +++ b/launchpad_dotnet/lib/launchpad_dotnet.dart @@ -0,0 +1,8 @@ + +import 'launchpad_dotnet_platform_interface.dart'; + +class LaunchpadDotnet { + Future getPlatformVersion() { + return LaunchpadDotnetPlatform.instance.getPlatformVersion(); + } +} diff --git a/launchpad_dotnet/lib/launchpad_dotnet_method_channel.dart b/launchpad_dotnet/lib/launchpad_dotnet_method_channel.dart new file mode 100644 index 00000000..50a368e9 --- /dev/null +++ b/launchpad_dotnet/lib/launchpad_dotnet_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'launchpad_dotnet_platform_interface.dart'; + +/// An implementation of [LaunchpadDotnetPlatform] that uses method channels. +class MethodChannelLaunchpadDotnet extends LaunchpadDotnetPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('launchpad_dotnet'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/launchpad_dotnet/lib/launchpad_dotnet_platform_interface.dart b/launchpad_dotnet/lib/launchpad_dotnet_platform_interface.dart new file mode 100644 index 00000000..2d06ef73 --- /dev/null +++ b/launchpad_dotnet/lib/launchpad_dotnet_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'launchpad_dotnet_method_channel.dart'; + +abstract class LaunchpadDotnetPlatform extends PlatformInterface { + /// Constructs a LaunchpadDotnetPlatform. + LaunchpadDotnetPlatform() : super(token: _token); + + static final Object _token = Object(); + + static LaunchpadDotnetPlatform _instance = MethodChannelLaunchpadDotnet(); + + /// The default instance of [LaunchpadDotnetPlatform] to use. + /// + /// Defaults to [MethodChannelLaunchpadDotnet]. + static LaunchpadDotnetPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [LaunchpadDotnetPlatform] when + /// they register themselves. + static set instance(LaunchpadDotnetPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/launchpad_dotnet/nuget.config b/launchpad_dotnet/nuget.config new file mode 100644 index 00000000..7a51d443 --- /dev/null +++ b/launchpad_dotnet/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/launchpad_dotnet/pubspec.lock b/launchpad_dotnet/pubspec.lock new file mode 100644 index 00000000..f8724914 --- /dev/null +++ b/launchpad_dotnet/pubspec.lock @@ -0,0 +1,497 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "50.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.7" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.4.2" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + color: + dependency: transitive + description: + name: color + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.4" + dartx: + dependency: transitive + description: + name: dartx + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + ffi: + dependency: "direct main" + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0+1" + flutter_gen_runner: + dependency: "direct dev" + description: + name: flutter_gen_runner + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0+1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.5" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.7.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + plugin_platform_interface: + dependency: "direct main" + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + time: + dependency: transitive + description: + name: time + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.18.6 <3.0.0" + flutter: ">=2.5.0" diff --git a/launchpad_dotnet/pubspec.yaml b/launchpad_dotnet/pubspec.yaml new file mode 100644 index 00000000..94e60ae2 --- /dev/null +++ b/launchpad_dotnet/pubspec.yaml @@ -0,0 +1,71 @@ +name: launchpad_dotnet +description: A dotnet interop library for Launchpad +version: 0.0.1 +homepage: https://launchpad.games + +environment: + sdk: '>=2.18.6 <3.0.0' + flutter: ">=2.5.0" + +dependencies: + ffi: ^2.0.1 + flutter: + sdk: flutter + path: ^1.8.2 + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + build_runner: ^2.3.3 + flutter_gen_runner: ^5.1.0+1 + flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + windows: + pluginClass: LaunchpadDotnetPluginCApi + ffiPlugin: true + + assets: + - assets/bin/release/LaunchpadDotNet.dll + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/launchpad_dotnet/test/launchpad_dotnet_method_channel_test.dart b/launchpad_dotnet/test/launchpad_dotnet_method_channel_test.dart new file mode 100644 index 00000000..bc3c1831 --- /dev/null +++ b/launchpad_dotnet/test/launchpad_dotnet_method_channel_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:launchpad_dotnet/launchpad_dotnet_method_channel.dart'; + +void main() { + MethodChannelLaunchpadDotnet platform = MethodChannelLaunchpadDotnet(); + const MethodChannel channel = MethodChannel('launchpad_dotnet'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/launchpad_dotnet/test/launchpad_dotnet_test.dart b/launchpad_dotnet/test/launchpad_dotnet_test.dart new file mode 100644 index 00000000..09d236bd --- /dev/null +++ b/launchpad_dotnet/test/launchpad_dotnet_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:launchpad_dotnet/launchpad_dotnet.dart'; +import 'package:launchpad_dotnet/launchpad_dotnet_platform_interface.dart'; +import 'package:launchpad_dotnet/launchpad_dotnet_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockLaunchpadDotnetPlatform + with MockPlatformInterfaceMixin + implements LaunchpadDotnetPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final LaunchpadDotnetPlatform initialPlatform = LaunchpadDotnetPlatform.instance; + + test('$MethodChannelLaunchpadDotnet is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + LaunchpadDotnet launchpadDotnetPlugin = LaunchpadDotnet(); + MockLaunchpadDotnetPlatform fakePlatform = MockLaunchpadDotnetPlatform(); + LaunchpadDotnetPlatform.instance = fakePlatform; + + expect(await launchpadDotnetPlugin.getPlatformVersion(), '42'); + }); +} diff --git a/launchpad_dotnet/windows/.gitignore b/launchpad_dotnet/windows/.gitignore new file mode 100644 index 00000000..b3eb2be1 --- /dev/null +++ b/launchpad_dotnet/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/launchpad_dotnet/windows/CMakeLists.txt b/launchpad_dotnet/windows/CMakeLists.txt new file mode 100644 index 00000000..eed05f70 --- /dev/null +++ b/launchpad_dotnet/windows/CMakeLists.txt @@ -0,0 +1,53 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "launchpad_dotnet") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "launchpad_dotnet_plugin") + +# Any new source files that you add to the plugin should be added here. +list(APPEND PLUGIN_SOURCES + "launchpad_dotnet_plugin.cpp" + "launchpad_dotnet_plugin.h" +) + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +add_library(${PLUGIN_NAME} SHARED + "include/launchpad_dotnet/launchpad_dotnet_plugin_c_api.h" + "launchpad_dotnet_plugin_c_api.cpp" + ${PLUGIN_SOURCES} +) + +# Apply a standard set of build settings that are configured in the +# application-level CMakeLists.txt. This can be removed for plugins that want +# full control over build settings. +apply_standard_settings(${PLUGIN_NAME}) + +# Symbols are hidden by default to reduce the chance of accidental conflicts +# between plugins. This should not be removed; any symbols that should be +# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(launchpad_dotnet_bundled_libraries + "${CMAKE_CURRENT_SOURCE_DIR}/../assets/bin/release/LaunchpadDotNet.dll" + PARENT_SCOPE +) diff --git a/launchpad_dotnet/windows/include/launchpad_dotnet/launchpad_dotnet_plugin_c_api.h b/launchpad_dotnet/windows/include/launchpad_dotnet/launchpad_dotnet_plugin_c_api.h new file mode 100644 index 00000000..4931cb9d --- /dev/null +++ b/launchpad_dotnet/windows/include/launchpad_dotnet/launchpad_dotnet_plugin_c_api.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_LAUNCHPAD_DOTNET_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_LAUNCHPAD_DOTNET_PLUGIN_C_API_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void LaunchpadDotnetPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_LAUNCHPAD_DOTNET_PLUGIN_C_API_H_ diff --git a/launchpad_dotnet/windows/launchpad_dotnet_plugin.cpp b/launchpad_dotnet/windows/launchpad_dotnet_plugin.cpp new file mode 100644 index 00000000..97989d07 --- /dev/null +++ b/launchpad_dotnet/windows/launchpad_dotnet_plugin.cpp @@ -0,0 +1,59 @@ +#include "launchpad_dotnet_plugin.h" + +// This must be included before many other Windows headers. +#include + +// For getPlatformVersion; remove unless needed for your plugin implementation. +#include + +#include +#include +#include + +#include +#include + +namespace launchpad_dotnet { + +// static +void LaunchpadDotnetPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "launchpad_dotnet", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +LaunchpadDotnetPlugin::LaunchpadDotnetPlugin() {} + +LaunchpadDotnetPlugin::~LaunchpadDotnetPlugin() {} + +void LaunchpadDotnetPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("getPlatformVersion") == 0) { + std::ostringstream version_stream; + version_stream << "Windows "; + if (IsWindows10OrGreater()) { + version_stream << "10+"; + } else if (IsWindows8OrGreater()) { + version_stream << "8"; + } else if (IsWindows7OrGreater()) { + version_stream << "7"; + } + result->Success(flutter::EncodableValue(version_stream.str())); + } else { + result->NotImplemented(); + } +} + +} // namespace launchpad_dotnet diff --git a/launchpad_dotnet/windows/launchpad_dotnet_plugin.h b/launchpad_dotnet/windows/launchpad_dotnet_plugin.h new file mode 100644 index 00000000..794383a8 --- /dev/null +++ b/launchpad_dotnet/windows/launchpad_dotnet_plugin.h @@ -0,0 +1,32 @@ +#ifndef FLUTTER_PLUGIN_LAUNCHPAD_DOTNET_PLUGIN_H_ +#define FLUTTER_PLUGIN_LAUNCHPAD_DOTNET_PLUGIN_H_ + +#include +#include + +#include + +namespace launchpad_dotnet { + +class LaunchpadDotnetPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + + LaunchpadDotnetPlugin(); + + virtual ~LaunchpadDotnetPlugin(); + + // Disallow copy and assign. + LaunchpadDotnetPlugin(const LaunchpadDotnetPlugin&) = delete; + LaunchpadDotnetPlugin& operator=(const LaunchpadDotnetPlugin&) = delete; + + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); +}; + +} // namespace launchpad_dotnet + +#endif // FLUTTER_PLUGIN_LAUNCHPAD_DOTNET_PLUGIN_H_ diff --git a/launchpad_dotnet/windows/launchpad_dotnet_plugin_c_api.cpp b/launchpad_dotnet/windows/launchpad_dotnet_plugin_c_api.cpp new file mode 100644 index 00000000..5ade2da0 --- /dev/null +++ b/launchpad_dotnet/windows/launchpad_dotnet_plugin_c_api.cpp @@ -0,0 +1,12 @@ +#include "include/launchpad_dotnet/launchpad_dotnet_plugin_c_api.h" + +#include + +#include "launchpad_dotnet_plugin.h" + +void LaunchpadDotnetPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + launchpad_dotnet::LaunchpadDotnetPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/launchpad_overlay/.gitignore b/launchpad_overlay/.gitignore new file mode 100644 index 00000000..7a848e2e --- /dev/null +++ b/launchpad_overlay/.gitignore @@ -0,0 +1,3 @@ +/bin +/obj +/.vs diff --git a/LaunchpadOverlay/DXHelper.cpp b/launchpad_overlay/DXHelper.cpp similarity index 100% rename from LaunchpadOverlay/DXHelper.cpp rename to launchpad_overlay/DXHelper.cpp diff --git a/LaunchpadOverlay/DXHelper.h b/launchpad_overlay/DXHelper.h similarity index 100% rename from LaunchpadOverlay/DXHelper.h rename to launchpad_overlay/DXHelper.h diff --git a/LaunchpadOverlay/DXHelperHelper.h b/launchpad_overlay/DXHelperHelper.h similarity index 100% rename from LaunchpadOverlay/DXHelperHelper.h rename to launchpad_overlay/DXHelperHelper.h diff --git a/LaunchpadOverlay/LaunchpadOverlay.aps b/launchpad_overlay/LaunchpadOverlay.aps similarity index 100% rename from LaunchpadOverlay/LaunchpadOverlay.aps rename to launchpad_overlay/LaunchpadOverlay.aps diff --git a/LaunchpadOverlay/LaunchpadOverlay.rc b/launchpad_overlay/LaunchpadOverlay.rc similarity index 97% rename from LaunchpadOverlay/LaunchpadOverlay.rc rename to launchpad_overlay/LaunchpadOverlay.rc index a2535bad..2e1c9f22 100644 --- a/LaunchpadOverlay/LaunchpadOverlay.rc +++ b/launchpad_overlay/LaunchpadOverlay.rc @@ -52,7 +52,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "E:\\Tools\\Launchpad\\Resources\\Graphics\\Launchpad.ico" +IDI_ICON1 ICON "E:\\Tools\\Launchpad\\Resources\\Graphics\\launchpad.ico" #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/LaunchpadOverlay/LaunchpadOverlay.vcxproj b/launchpad_overlay/LaunchpadOverlay.vcxproj similarity index 100% rename from LaunchpadOverlay/LaunchpadOverlay.vcxproj rename to launchpad_overlay/LaunchpadOverlay.vcxproj diff --git a/LaunchpadOverlay/LaunchpadOverlay.vcxproj.filters b/launchpad_overlay/LaunchpadOverlay.vcxproj.filters similarity index 100% rename from LaunchpadOverlay/LaunchpadOverlay.vcxproj.filters rename to launchpad_overlay/LaunchpadOverlay.vcxproj.filters diff --git a/LaunchpadOverlay/LaunchpadOverlay.vcxproj.user b/launchpad_overlay/LaunchpadOverlay.vcxproj.user similarity index 100% rename from LaunchpadOverlay/LaunchpadOverlay.vcxproj.user rename to launchpad_overlay/LaunchpadOverlay.vcxproj.user diff --git a/LaunchpadOverlay/LaunchpadOverlayWindow.cpp b/launchpad_overlay/LaunchpadOverlayWindow.cpp similarity index 100% rename from LaunchpadOverlay/LaunchpadOverlayWindow.cpp rename to launchpad_overlay/LaunchpadOverlayWindow.cpp diff --git a/LaunchpadOverlay/LaunchpadOverlayWindow.h b/launchpad_overlay/LaunchpadOverlayWindow.h similarity index 100% rename from LaunchpadOverlay/LaunchpadOverlayWindow.h rename to launchpad_overlay/LaunchpadOverlayWindow.h diff --git a/LaunchpadOverlay/Main.cpp b/launchpad_overlay/Main.cpp similarity index 100% rename from LaunchpadOverlay/Main.cpp rename to launchpad_overlay/Main.cpp diff --git a/LaunchpadOverlay/RCa16008 b/launchpad_overlay/RCa16008 similarity index 100% rename from LaunchpadOverlay/RCa16008 rename to launchpad_overlay/RCa16008 diff --git a/LaunchpadOverlay/RCb16008 b/launchpad_overlay/RCb16008 similarity index 100% rename from LaunchpadOverlay/RCb16008 rename to launchpad_overlay/RCb16008 diff --git a/LaunchpadOverlay/RCc16008 b/launchpad_overlay/RCc16008 similarity index 100% rename from LaunchpadOverlay/RCc16008 rename to launchpad_overlay/RCc16008 diff --git a/LaunchpadOverlay/Win32Application.cpp b/launchpad_overlay/Win32Application.cpp similarity index 100% rename from LaunchpadOverlay/Win32Application.cpp rename to launchpad_overlay/Win32Application.cpp diff --git a/LaunchpadOverlay/Win32Application.h b/launchpad_overlay/Win32Application.h similarity index 100% rename from LaunchpadOverlay/Win32Application.h rename to launchpad_overlay/Win32Application.h diff --git a/LaunchpadOverlay/d3dx12.h b/launchpad_overlay/d3dx12.h similarity index 100% rename from LaunchpadOverlay/d3dx12.h rename to launchpad_overlay/d3dx12.h diff --git a/LaunchpadOverlay/resource.h b/launchpad_overlay/resource.h similarity index 100% rename from LaunchpadOverlay/resource.h rename to launchpad_overlay/resource.h diff --git a/LaunchpadOverlay/stdafx.cpp b/launchpad_overlay/stdafx.cpp similarity index 100% rename from LaunchpadOverlay/stdafx.cpp rename to launchpad_overlay/stdafx.cpp diff --git a/LaunchpadOverlay/stdafx.h b/launchpad_overlay/stdafx.h similarity index 100% rename from LaunchpadOverlay/stdafx.h rename to launchpad_overlay/stdafx.h diff --git a/launchpad_overlay/x64/Debug/LaunchpadOverlay.exe b/launchpad_overlay/x64/Debug/LaunchpadOverlay.exe new file mode 100644 index 00000000..c89d1a33 Binary files /dev/null and b/launchpad_overlay/x64/Debug/LaunchpadOverlay.exe differ diff --git a/launchpad_overlay/x64/Debug/LaunchpadOverlay.ilk b/launchpad_overlay/x64/Debug/LaunchpadOverlay.ilk new file mode 100644 index 00000000..2e7acd46 Binary files /dev/null and b/launchpad_overlay/x64/Debug/LaunchpadOverlay.ilk differ diff --git a/launchpad_overlay/x64/Debug/LaunchpadOverlay.pdb b/launchpad_overlay/x64/Debug/LaunchpadOverlay.pdb new file mode 100644 index 00000000..6342944a Binary files /dev/null and b/launchpad_overlay/x64/Debug/LaunchpadOverlay.pdb differ diff --git a/launchpad_overlay/x64/Release/LaunchpadOverlay.exe b/launchpad_overlay/x64/Release/LaunchpadOverlay.exe new file mode 100644 index 00000000..263acf03 Binary files /dev/null and b/launchpad_overlay/x64/Release/LaunchpadOverlay.exe differ diff --git a/launchpad_overlay/x64/Release/LaunchpadOverlay.pdb b/launchpad_overlay/x64/Release/LaunchpadOverlay.pdb new file mode 100644 index 00000000..a4d5dc99 Binary files /dev/null and b/launchpad_overlay/x64/Release/LaunchpadOverlay.pdb differ diff --git a/launchpad_overlay/x64/Release/LaunchpadOverlay.zip b/launchpad_overlay/x64/Release/LaunchpadOverlay.zip new file mode 100644 index 00000000..187abe2f Binary files /dev/null and b/launchpad_overlay/x64/Release/LaunchpadOverlay.zip differ