mirror of
https://github.com/google/pebble.git
synced 2025-04-30 15:21:41 -04:00
149 lines
8.8 KiB
C
149 lines
8.8 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "applib/graphics/gtypes.h"
|
|
#include "applib/ui/animation.h"
|
|
|
|
//! @addtogroup UI
|
|
//! @{
|
|
//! @addtogroup UnobstructedArea
|
|
//!
|
|
//! \brief Events about when the app's unobstructed area changes for visually adapting
|
|
//!
|
|
//! Unobstructed Area enables a watchface to adapt to overlays partially obstructing it. Timeline
|
|
//! Peek is the only overlay, and it partially obstructs the bottom of watchfaces, displaying the
|
|
//! immediately upcoming or newly began event. Unobstructed Area is for use only with watchfaces.
|
|
//! There will be no Unobstructed Area events emitted for apps that are not watchfaces. Timeline
|
|
//! Peek is also limited to rectangular platforms, thus using Unobstructed Area on Chalk will also
|
|
//! result in no events.
|
|
//!
|
|
//! Watchfaces are encouraged to respect Unobstructed Area in order to dynamically resize within
|
|
//! the remaining screen area that isn't obstructed by an overlay. Unobstructed Area provides
|
|
//! handlers for when the screen area will begin and end changing as well as progress for every
|
|
//! frame of the animation, allowing one to animate the watchface to resize while the overlay
|
|
//! animates into or out of the screen. Additionally, at any point, a watchface can query the
|
|
//! Unobstructed Area using \ref layer_get_unobstructed_bounds even if there was not event or if it
|
|
//! didn't subscribe to them.
|
|
//!
|
|
//! The simplest usage of Unobstructed Area is to change rendering of your watchface to render
|
|
//! within the unobstructed bounds obtained by \ref layer_get_unobstructed_bounds. This is
|
|
//! accomplished by modifying your layer update procedure that you setup a layer with using
|
|
//! \ref layer_set_update_proc to use \ref layer_get_unobstructed_bounds instead of
|
|
//! \ref layer_get_bounds or \ref layer_get_frame and adjusting all the rendering logic within it
|
|
//! to handle resizing bounds. The app will redraw automatically whenever the Unobstructed
|
|
//! Area changes, even if you did not subscribe to the events.
|
|
//!
|
|
//! Subscribing to `will_change`, `change`, and `did_change` events with
|
|
//! \ref app_unobstructed_area_service_subscribe will allow you to obtain the final unobstructed
|
|
//! area as soon as the animation begins and the current animation progress for each frame as they
|
|
//! occur should you need that information to resize your watchface.
|
|
//!
|
|
//! When designing your watchface resizing logic or animation, take into consideration the way
|
|
//! Timeline Peek animates. Timeline Peek appears from the bottom with an animation that moves
|
|
//! slightly at first and snaps across with a bounce back. Because there is a bounce back in the
|
|
//! animation, there will be momentary frames where the unobstructed area is slightly smaller than
|
|
//! when Timeline Peek is visible but not animating.
|
|
//!
|
|
//! If you intend to hide or show layers on the screen depending on the unobstructed area, there
|
|
//! are two ways to do this. For anything inside a layer update procedure the simple solution would
|
|
//! be sufficient. That is, in your update procedure, you can to determine whether something is to
|
|
//! be rendered or not based on the remaining height available to you determined from
|
|
//! \ref layer_get_unobstructed_bounds.
|
|
//!
|
|
//! The other method for deciding whether to show or hide layers is to subscribe to events using
|
|
//! \ref app_unobstructed_area_service_subscribe. Note that hiding or showing elements in your
|
|
//! `will_change` can be too early due to the nature of the Timeline Peek animation where it moves
|
|
//! slowly in the first few frames. After judging the animation visually, you may instead show or
|
|
//! hide your layers based on both the final unobstructed area obtained from `will_change` in
|
|
//! combination with the progress obtained from `change`, or check the current height available to
|
|
//! you by calling \ref layer_get_unobstructed_bounds in your `change` handler to update your
|
|
//! state.
|
|
//!
|
|
//! For more advance usages such as functionally defined animations whose animation frames are a
|
|
//! function of animation progress, you will want to subscribe to `will_change`, `change`, and
|
|
//! optionally `did_change` events if you need to clean up resources or layers created in your
|
|
//! `will_change` handler. For example, in your `will_change` handler, you may use
|
|
//! \ref layer_get_unobstructed_bounds to get the current unobstructed area, and combine it with
|
|
//! the final unobstructed area passed into `will_change` to set up a \ref PropertyAnimation. A
|
|
//! separate example would be to save the animation progress passed to your `change` handler for
|
|
//! use in your own custom easing curve in your layer update procedure.
|
|
|
|
//! Handler that will be called just before the unobstructed area will begin changing
|
|
//! @param final_unobstructed_screen_area The final unobstructed screen area after
|
|
//! the unobstructed area has finished changing.
|
|
//! @param context A user-provided context.
|
|
typedef void (*UnobstructedAreaWillChangeHandler)(GRect final_unobstructed_screen_area,
|
|
void *context);
|
|
|
|
//! Handler that will be called every time the unobstructed area changes
|
|
//! @param progress The progress of the animation changing the unobstructed area.
|
|
//! @param context A user-provided context.
|
|
typedef void (*UnobstructedAreaChangeHandler)(AnimationProgress progress, void *context);
|
|
|
|
//! Handler that will be called after the unobstructed area has finished changing
|
|
//! @param context A user-provided context.
|
|
typedef void (*UnobstructedAreaDidChangeHandler)(void *context);
|
|
|
|
typedef struct UnobstructedAreaHandlers {
|
|
//! Handler that will be called just before the unobstructed area will begin changing.
|
|
UnobstructedAreaWillChangeHandler will_change;
|
|
//! Handler that will be called every time the unobstructed area changes.
|
|
UnobstructedAreaChangeHandler change;
|
|
//! Handler that will be called after the unobstructed area has finished changing.
|
|
UnobstructedAreaDidChangeHandler did_change;
|
|
} UnobstructedAreaHandlers;
|
|
|
|
//! @internal
|
|
//! Puts a will change event notifying of the start of an obstruction animation.
|
|
//! @param current_y The beginning obstruction y which is saved in the UnobstructedAreaState
|
|
//! @param final_y The final obstruction y which is converted to the unobstructed area and passed
|
|
//! to the `will_change` handler
|
|
void unobstructed_area_service_will_change(int16_t current_y, int16_t final_y);
|
|
|
|
//! @internal
|
|
//! Puts a change event notifying a frame change of an obstruction animation.
|
|
//! If the app was newly started, this will also put a will change event.
|
|
//! @param current_y The current obstruction y to be saved in UnobstructedAreaState for this frame
|
|
//! @param final_y The final obstruction y which is converted to the unobstructed area and may be
|
|
//! passed to the `will_change` handler if the app did not yet receive a will change event due to
|
|
//! being started mid-animation.
|
|
void unobstructed_area_service_change(int16_t current_y, int16_t final_y,
|
|
AnimationProgress progress);
|
|
//! @internal
|
|
//! Puts a did change event notifying that the obstruction animation is complete.
|
|
//! @param final_y The final obstruction y which is converted to the unobstructed area and may be
|
|
//! passed to the `will_change` handler if the app did not yet receive a will change event due to
|
|
//! being started mid-animation.
|
|
void unobstructed_area_service_did_change(int16_t final_y);
|
|
|
|
//! Subscribe to be notified when the app's unobstructed area changes. When an unobstructed area
|
|
//! begins changing, the `will_change` handler will be called, and every `will_change` call is
|
|
//! always paired with a `did_change` call that occurs when it is done changing given that
|
|
//! the `will_change` and `did_change` handlers are set. When subscribing while the unobstructed
|
|
//! area is changing, the `will_change` handler will be called after subscription in the next event
|
|
//! loop.
|
|
//! @param handlers The handlers that should be called when the unobstructed area changes.
|
|
//! @param context A user-provided context that will be passed to the callback handlers.
|
|
//! @see layer_get_unobstructed_bounds
|
|
void app_unobstructed_area_service_subscribe(UnobstructedAreaHandlers handlers, void *context);
|
|
|
|
//! Unsubscribe from notifications about changes to the app's unobstructed area.
|
|
void app_unobstructed_area_service_unsubscribe(void);
|
|
|
|
//! @} // end addtogroup UnobstructedArea
|
|
//! @} // end addtogroup UI
|