mirror of
https://github.com/google/pebble.git
synced 2025-06-15 14:13:11 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
242
src/fw/applib/legacy2/ui/action_bar_layer_legacy2.c
Normal file
242
src/fw/applib/legacy2/ui/action_bar_layer_legacy2.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "action_bar_layer_legacy2.h"
|
||||
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
inline static bool action_bar_legacy2_is_highlighted(ActionBarLayerLegacy2 *action_bar,
|
||||
uint8_t index) {
|
||||
PBL_ASSERTN(index < NUM_ACTION_BAR_LEGACY2_ITEMS);
|
||||
return (bool) (action_bar->is_highlighted & (1 << index));
|
||||
}
|
||||
|
||||
inline static void action_bar_legacy2_set_highlighted(ActionBarLayerLegacy2 *action_bar,
|
||||
uint8_t index, bool highlighted) {
|
||||
PBL_ASSERT(index < NUM_ACTION_BAR_LEGACY2_ITEMS, "Index: %"PRIu8, index);
|
||||
|
||||
const uint8_t bit = (1 << index);
|
||||
if (highlighted) {
|
||||
action_bar->is_highlighted |= bit;
|
||||
} else {
|
||||
action_bar->is_highlighted &= ~bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void action_bar_legacy2_changed_proc(ActionBarLayerLegacy2 *action_bar, GContext* ctx) {
|
||||
if (action_bar->layer.window && action_bar->layer.window->on_screen == false) {
|
||||
// clear first, fixes issue of returning from other page while highlighted
|
||||
for (int i = 0; i < NUM_ACTION_BAR_LEGACY2_ITEMS; i++) {
|
||||
action_bar_legacy2_set_highlighted(action_bar, i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void action_bar_legacy2_update_proc(ActionBarLayerLegacy2 *action_bar, GContext* ctx) {
|
||||
const GColor bg_color = get_native_color(action_bar->background_color);
|
||||
graphics_context_set_fill_color(ctx, bg_color);
|
||||
const uint8_t radius = 3;
|
||||
const uint8_t margin = 1;
|
||||
graphics_fill_round_rect(ctx, &action_bar->layer.bounds, radius,
|
||||
GCornerTopLeft | GCornerBottomLeft);
|
||||
GRect rect = action_bar->layer.bounds;
|
||||
rect.origin.x += margin;
|
||||
rect.origin.y += margin;
|
||||
rect.size.w -= margin;
|
||||
rect.size.h -= 2 * margin;
|
||||
rect.size.h /= NUM_ACTION_BAR_LEGACY2_ITEMS;
|
||||
const bool is_white = gcolor_equal(bg_color, GColorWhite);
|
||||
const GColor highlighted_color = (is_white) ? GColorBlack : GColorWhite;
|
||||
for (unsigned int index = 0; index < NUM_ACTION_BAR_LEGACY2_ITEMS; ++index) {
|
||||
const GBitmap *icon = action_bar->icons[index];
|
||||
if (icon) {
|
||||
const bool is_highlighted = action_bar_legacy2_is_highlighted(action_bar, index);
|
||||
if (is_highlighted) {
|
||||
graphics_context_set_fill_color(ctx, highlighted_color);
|
||||
GCornerMask corner;
|
||||
switch (index) {
|
||||
case 0: corner = GCornerTopLeft; break;
|
||||
case NUM_ACTION_BAR_LEGACY2_ITEMS - 1: corner = GCornerBottomLeft; break;
|
||||
default: corner = GCornerNone; break;
|
||||
}
|
||||
graphics_fill_round_rect(ctx, &rect, radius - margin, corner);
|
||||
}
|
||||
GRect icon_rect = icon->bounds;
|
||||
const bool clip = true;
|
||||
grect_align(&icon_rect, &rect, GAlignCenter, clip);
|
||||
const GCompOp op = (is_white != is_highlighted) ? GCompOpAssign : GCompOpAssignInverted;
|
||||
graphics_context_set_compositing_mode(ctx, op);
|
||||
graphics_draw_bitmap_in_rect(ctx, (GBitmap*)icon, &icon_rect);
|
||||
}
|
||||
rect.origin.y += rect.size.h;
|
||||
}
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_init(ActionBarLayerLegacy2 *action_bar) {
|
||||
*action_bar = (ActionBarLayerLegacy2){};
|
||||
layer_init(&action_bar->layer, &GRectZero);
|
||||
action_bar->layer.update_proc = (LayerUpdateProc) action_bar_legacy2_update_proc;
|
||||
action_bar->layer.property_changed_proc =
|
||||
(PropertyChangedProc) action_bar_legacy2_changed_proc;
|
||||
action_bar->background_color = GColor2Black;
|
||||
}
|
||||
|
||||
ActionBarLayerLegacy2 *action_bar_layer_legacy2_create(void) {
|
||||
ActionBarLayerLegacy2 * layer = task_malloc(sizeof(ActionBarLayerLegacy2));
|
||||
if (layer) {
|
||||
action_bar_layer_legacy2_init(layer);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_deinit(ActionBarLayerLegacy2 *action_bar_layer) {
|
||||
layer_deinit(&action_bar_layer->layer);
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_destroy(ActionBarLayerLegacy2 *action_bar_layer) {
|
||||
if (action_bar_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
action_bar_layer_legacy2_deinit(action_bar_layer);
|
||||
task_free(action_bar_layer);
|
||||
}
|
||||
|
||||
Layer* action_bar_layer_legacy2_get_layer(ActionBarLayerLegacy2 *action_bar_layer) {
|
||||
return &action_bar_layer->layer;
|
||||
}
|
||||
|
||||
inline static void* action_bar_legacy2_get_context(ActionBarLayerLegacy2 *action_bar) {
|
||||
return action_bar->context ? action_bar->context : action_bar;
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_set_context(ActionBarLayerLegacy2 *action_bar, void *context) {
|
||||
action_bar->context = context;
|
||||
}
|
||||
|
||||
static void action_bar_legacy2_raw_up_down_handler(ClickRecognizerRef recognizer,
|
||||
ActionBarLayerLegacy2 *action_bar,
|
||||
bool is_highlighted) {
|
||||
const ButtonId button_id = click_recognizer_get_button_id(recognizer);
|
||||
const uint8_t index = button_id - 1;
|
||||
const GBitmap *icon = action_bar->icons[index];
|
||||
|
||||
// is_highlighted will cause the icon in the action bar to render normal or inverted:
|
||||
action_bar_legacy2_set_highlighted(action_bar, index, is_highlighted);
|
||||
if (icon == NULL) {
|
||||
return;
|
||||
} else {
|
||||
layer_mark_dirty(&action_bar->layer);
|
||||
}
|
||||
}
|
||||
|
||||
static void action_bar_legacy2_raw_up_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
ActionBarLayerLegacy2 *action_bar = (ActionBarLayerLegacy2 *)context;
|
||||
action_bar_legacy2_raw_up_down_handler(recognizer, action_bar, false);
|
||||
}
|
||||
|
||||
static void action_bar_legacy2_raw_down_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
ActionBarLayerLegacy2 *action_bar = (ActionBarLayerLegacy2 *)context;
|
||||
action_bar_legacy2_raw_up_down_handler(recognizer, action_bar, true);
|
||||
}
|
||||
|
||||
static void action_bar_legacy2_click_config_provider(ActionBarLayerLegacy2 *action_bar) {
|
||||
void *context = action_bar_legacy2_get_context(action_bar);
|
||||
// For UP, SELECT and DOWN, setup the raw handler and assign the user specified context:
|
||||
for (ButtonId button_id = BUTTON_ID_UP; button_id < NUM_BUTTONS; ++button_id) {
|
||||
window_raw_click_subscribe(button_id, action_bar_legacy2_raw_down_handler,
|
||||
action_bar_legacy2_raw_up_handler, action_bar);
|
||||
window_set_click_context(button_id, context);
|
||||
}
|
||||
// If back button is overridden, set context of BACK click recognizer as well:
|
||||
if (action_bar->window && action_bar->window->overrides_back_button) {
|
||||
window_set_click_context(BUTTON_ID_BACK, context);
|
||||
}
|
||||
if (action_bar->click_config_provider) {
|
||||
action_bar->click_config_provider(context);
|
||||
}
|
||||
}
|
||||
|
||||
inline static void action_bar_legacy2_update_click_config_provider(
|
||||
ActionBarLayerLegacy2 *action_bar) {
|
||||
if (action_bar->window) {
|
||||
window_set_click_config_provider_with_context(
|
||||
action_bar->window,
|
||||
(ClickConfigProvider) action_bar_legacy2_click_config_provider,
|
||||
action_bar);
|
||||
}
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_set_click_config_provider(ActionBarLayerLegacy2 *action_bar,
|
||||
ClickConfigProvider click_config_provider) {
|
||||
action_bar->click_config_provider = click_config_provider;
|
||||
action_bar_legacy2_update_click_config_provider(action_bar);
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_set_icon(ActionBarLayerLegacy2 *action_bar, ButtonId button_id,
|
||||
const GBitmap *icon) {
|
||||
if (button_id < BUTTON_ID_UP || button_id >= NUM_BUTTONS) {
|
||||
return;
|
||||
}
|
||||
if (action_bar->icons[button_id - 1] == icon) {
|
||||
return;
|
||||
}
|
||||
action_bar->icons[button_id - 1] = icon;
|
||||
layer_mark_dirty(&action_bar->layer);
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_clear_icon(ActionBarLayerLegacy2 *action_bar, ButtonId button_id) {
|
||||
action_bar_layer_legacy2_set_icon(action_bar, button_id, NULL);
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_add_to_window(ActionBarLayerLegacy2 *action_bar,
|
||||
struct Window *window) {
|
||||
const uint8_t vertical_margin = 3;
|
||||
const GRect *window_bounds = &window->layer.bounds;
|
||||
GRect rect = GRect(0, 0, ACTION_BAR_LEGACY2_WIDTH,
|
||||
window_bounds->size.h - (vertical_margin * 2));
|
||||
layer_set_bounds(&action_bar->layer, &rect);
|
||||
rect.origin.x = window_bounds->size.w - ACTION_BAR_LEGACY2_WIDTH;
|
||||
rect.origin.y = vertical_margin;
|
||||
layer_set_frame(&action_bar->layer, &rect);
|
||||
layer_add_child(&window->layer, &action_bar->layer);
|
||||
|
||||
action_bar->window = window;
|
||||
action_bar_legacy2_update_click_config_provider(action_bar);
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_remove_from_window(ActionBarLayerLegacy2 *action_bar) {
|
||||
if (action_bar == NULL || action_bar->window == NULL) {
|
||||
return;
|
||||
}
|
||||
layer_remove_from_parent(&action_bar->layer);
|
||||
window_set_click_config_provider_with_context(action_bar->window, NULL, NULL);
|
||||
action_bar->window = NULL;
|
||||
}
|
||||
|
||||
void action_bar_layer_legacy2_set_background_color_2bit(ActionBarLayerLegacy2 *action_bar,
|
||||
GColor2 background_color) {
|
||||
GColor native_background_color = get_native_color(background_color);
|
||||
if (gcolor_equal(native_background_color, get_native_color(action_bar->background_color))) {
|
||||
return;
|
||||
}
|
||||
action_bar->background_color = get_closest_gcolor2(native_background_color);
|
||||
layer_mark_dirty(&(action_bar->layer));
|
||||
}
|
246
src/fw/applib/legacy2/ui/action_bar_layer_legacy2.h
Normal file
246
src/fw/applib/legacy2/ui/action_bar_layer_legacy2.h
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* 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/ui/layer.h"
|
||||
#include "applib/ui/click.h"
|
||||
|
||||
//! @file action_bar_layer.h
|
||||
//! @addtogroup UI
|
||||
//! @{
|
||||
//! @addtogroup Layer Layers
|
||||
//! @{
|
||||
//! @addtogroup ActionBarLayerLegacy2
|
||||
//! \brief Vertical, bar-shaped control widget on the right edge of the window
|
||||
//!
|
||||
//! 
|
||||
//! ActionBarLayerLegacy2 is a Layer that displays a bar on the right edge of the
|
||||
//! window. The bar can contain up to 3 icons, each corresponding with one of
|
||||
//! the buttons on the right side of the watch. The purpose of each icon is
|
||||
//! to provide a hint (feed-forward) to what action a click on the respective
|
||||
//! button will cause.
|
||||
//!
|
||||
//! The action bar is useful when there are a few (up to 3) main actions that
|
||||
//! are desirable to be able to take quickly, literally with one press of a
|
||||
//! button.
|
||||
//!
|
||||
//! <h3>More actions</h3>
|
||||
//! If there are more than 3 actions the user might want to take:
|
||||
//! * Try assigning the top and bottom icons of the action bar to the two most
|
||||
//! immediate actions and use the middle icon to push a Window with a MenuLayer
|
||||
//! with less immediate actions.
|
||||
//! * Secondary actions that are not vital, can be "hidden" under a long click.
|
||||
//! Try to group similar actions to one button. For example, in a Music app,
|
||||
//! a single click on the top button is tied to the action to jump to the
|
||||
//! previous track. Holding that same button means seek backwards.
|
||||
//!
|
||||
//! <h3>Directionality mapping</h3>
|
||||
//! When the top and bottom buttons are used to control navigating through
|
||||
//! a (potentially virtual, non-visible) list of items, follow this guideline:
|
||||
//! * Tie the top button to the action that goes to the _previous_ item in the
|
||||
//! list, for example "jump to previous track" in a Music app.
|
||||
//! * Tie the bottom button to the action that goes to the _next_ item in the
|
||||
//! list, for example "jump to next track" in a Music app.
|
||||
//!
|
||||
//! <h3>Geometry</h3>
|
||||
//! * The action bar is 20 pixels wide. Use the \ref ACTION_BAR_LEGACY2_WIDTH define.
|
||||
//! * The top and bottom spacing is 3 pixels each (the space between the top and
|
||||
//! bottom of the frame of the action bar and the edges of the window it is
|
||||
//! contained in).
|
||||
//! * Icons should not be wider than 18 pixels. It is recommended to use a size
|
||||
//! of around 14 x 14 pixels for the "visual core" of the icon, and extending
|
||||
//! or contracting where needed.
|
||||
//! <h3>Example Code</h3>
|
||||
//! The code example below shows how to do the initial setup of the action bar
|
||||
//! in a window's `.load` handler.
|
||||
//! Configuring the button actions is similar to the process when using
|
||||
//! \ref window_set_click_config_provider(). See \ref Clicks for more
|
||||
//! information.
|
||||
//!
|
||||
//! \code{.c}
|
||||
//! ActionBarLayerLegacy2 *action_bar;
|
||||
//!
|
||||
//! // The implementation of my_next_click_handler and my_previous_click_handler
|
||||
//! // is omitted for the sake of brevity. See the Clicks reference docs.
|
||||
//!
|
||||
//! void click_config_provider(void *context) {
|
||||
//! window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) my_next_click_handler);
|
||||
//! window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler) my_previous_click_handler);
|
||||
//! }
|
||||
//!
|
||||
//! void window_load(Window *window) {
|
||||
//! ...
|
||||
//! // Initialize the action bar:
|
||||
//! action_bar = action_bar_layer_legacy2_create();
|
||||
//! // Associate the action bar with the window:
|
||||
//! action_bar_layer_legacy2_add_to_window(action_bar, window);
|
||||
//! // Set the click config provider:
|
||||
//! action_bar_layer_legacy2_set_click_config_provider(action_bar,
|
||||
//! click_config_provider);
|
||||
//!
|
||||
//! // Set the icons:
|
||||
//! // The loading of the icons is omitted for brevity... See gbitmap_create_with_resource()
|
||||
//! action_bar_layer_legacy2_set_icon(action_bar, BUTTON_ID_UP, &my_icon_previous);
|
||||
//! action_bar_layer_legacy2_set_icon(action_bar, BUTTON_ID_DOWN, &my_icon_next);
|
||||
//! }
|
||||
//! \endcode
|
||||
//! @{
|
||||
|
||||
//! The width of the action bar in pixels.
|
||||
#define ACTION_BAR_LEGACY2_WIDTH 20
|
||||
|
||||
//! The maximum number of action bar items.
|
||||
#define NUM_ACTION_BAR_LEGACY2_ITEMS 3
|
||||
|
||||
struct Window;
|
||||
struct GBitmap;
|
||||
|
||||
//! Data structure of an action bar.
|
||||
//! @note an `ActionBarLayerLegacy2 *` can safely be casted to a `Layer *` and can
|
||||
//! thus be used with all other functions that take a `Layer *` as an argument.
|
||||
//! <br/>For example, the following is legal:
|
||||
//! \code{.c}
|
||||
//! ActionBarLayerLegacy2 action_bar;
|
||||
//! ...
|
||||
//! layer_set_hidden((Layer *)&action_bar, true);
|
||||
//! \endcode
|
||||
typedef struct ActionBarLayerLegacy2 {
|
||||
Layer layer;
|
||||
const struct GBitmap *icons[NUM_ACTION_BAR_LEGACY2_ITEMS];
|
||||
struct Window *window;
|
||||
void *context;
|
||||
ClickConfigProvider click_config_provider;
|
||||
unsigned is_highlighted:NUM_ACTION_BAR_LEGACY2_ITEMS;
|
||||
GColor2 background_color:2;
|
||||
} ActionBarLayerLegacy2;
|
||||
|
||||
//! Initializes the action bar and reverts any state back to the default state:
|
||||
//! * Background color: \ref GColorBlack
|
||||
//! * No click configuration provider (`NULL`)
|
||||
//! * No icons
|
||||
//! * Not added to / associated with any window, thus not catching any button input yet.
|
||||
//! @note Do not call this function on an action bar that is still or already added to a window.
|
||||
//! @param action_bar The action bar to initialize
|
||||
void action_bar_layer_legacy2_init(ActionBarLayerLegacy2 *action_bar);
|
||||
|
||||
//! Creates a new ActionBarLayerLegacy2 on the heap and initalizes it with the default values.
|
||||
//! * Background color: \ref GColorBlack
|
||||
//! * No click configuration provider (`NULL`)
|
||||
//! * No icons
|
||||
//! * Not added to / associated with any window, thus not catching any button input yet.
|
||||
//! @return A pointer to the ActionBarLayerLegacy2. `NULL` if the ActionBarLayerLegacy2 could not
|
||||
//! be created
|
||||
ActionBarLayerLegacy2 *action_bar_layer_legacy2_create(void);
|
||||
|
||||
void action_bar_layer_legacy2_deinit(ActionBarLayerLegacy2 *action_bar_layer);
|
||||
|
||||
//! Destroys a ActionBarLayerLegacy2 previously created by action_bar_layer_legacy2_create
|
||||
void action_bar_layer_legacy2_destroy(ActionBarLayerLegacy2 *action_bar_layer);
|
||||
|
||||
//! Gets the "root" Layer of the action bar layer, which is the parent for the sub-
|
||||
//! layers used for its implementation.
|
||||
//! @param action_bar_layer Pointer to the ActionBarLayerLegacy2 for which to get the "root" Layer
|
||||
//! @return The "root" Layer of the action bar layer.
|
||||
//! @internal
|
||||
//! @note The result is always equal to `(Layer *) action_bar_layer`.
|
||||
Layer*action_bar_layer_legacy2_get_layer(ActionBarLayerLegacy2 *action_bar_layer);
|
||||
|
||||
//! Sets the context parameter, which will be passed in to \ref ClickHandler
|
||||
//! callbacks and the \ref ClickConfigProvider callback of the action bar.
|
||||
//! @note By default, a pointer to the action bar itself is passed in, if the
|
||||
//! context has not been set or if it has been set to `NULL`.
|
||||
//! @param action_bar The action bar for which to assign the new context
|
||||
//! @param context The new context
|
||||
//! @see action_bar_layer_legacy2_set_click_config_provider()
|
||||
//! @see \ref Clicks
|
||||
void action_bar_layer_legacy2_set_context(ActionBarLayerLegacy2 *action_bar, void *context);
|
||||
|
||||
//! Sets the click configuration provider callback of the action bar.
|
||||
//! In this callback your application can associate handlers to the different
|
||||
//! types of click events for each of the buttons, see \ref Clicks.
|
||||
//! @note If the action bar had already been added to a window and the window
|
||||
//! is currently on-screen, the click configuration provider will be called
|
||||
//! before this function returns. Otherwise, it will be called by the system
|
||||
//! when the window becomes on-screen.
|
||||
//! @note The `.raw` handlers cannot be used without breaking the automatic
|
||||
//! highlighting of the segment of the action bar that for which a button is
|
||||
//! @see action_bar_layer_legacy2_set_icon()
|
||||
//! @param action_bar The action bar for which to assign a new click
|
||||
//! configuration provider
|
||||
//! @param click_config_provider The new click configuration provider
|
||||
void action_bar_layer_legacy2_set_click_config_provider(ActionBarLayerLegacy2 *action_bar,
|
||||
ClickConfigProvider click_config_provider);
|
||||
|
||||
//! Sets an action bar icon onto one of the 3 slots as identified by `button_id`.
|
||||
//! Only \ref BUTTON_ID_UP, \ref BUTTON_ID_SELECT and \ref BUTTON_ID_DOWN can be
|
||||
//! used. Whenever an icon is set, the click configuration provider will be
|
||||
//! called, to give the application the opportunity to reconfigure the button
|
||||
//! interaction.
|
||||
//! @param action_bar The action bar for which to set the new icon
|
||||
//! @param button_id The identifier of the button for which to set the icon
|
||||
//! @param icon Pointer to the \ref GBitmap icon
|
||||
//! @see action_bar_layer_legacy2_set_click_config_provider()
|
||||
void action_bar_layer_legacy2_set_icon(ActionBarLayerLegacy2 *action_bar, ButtonId button_id,
|
||||
const GBitmap *icon);
|
||||
|
||||
//! Convenience function to clear out an existing icon.
|
||||
//! All it does is call `action_bar_layer_legacy2_set_icon(action_bar, button_id, NULL)`
|
||||
//! @param action_bar The action bar for which to clear an icon
|
||||
//! @param button_id The identifier of the button for which to clear the icon
|
||||
//! @see action_bar_layer_legacy2_set_icon()
|
||||
void action_bar_layer_legacy2_clear_icon(ActionBarLayerLegacy2 *action_bar, ButtonId button_id);
|
||||
|
||||
//! Adds the action bar's layer on top of the window's root layer. It also
|
||||
//! adjusts the layout of the action bar to match the geometry of the window it
|
||||
//! gets added to.
|
||||
//! Lastly, it calls \ref window_set_click_config_provider_with_context() on
|
||||
//! the window to set it up to work with the internal callback and raw click
|
||||
//! handlers of the action bar, to enable the highlighting of the section of the
|
||||
//! action bar when the user presses a button.
|
||||
//! @note After this call, do not use
|
||||
//! \ref window_set_click_config_provider_with_context() with the window that
|
||||
//! the action bar has been added to (this would de-associate the action bar's
|
||||
//! click config provider and context). Instead use
|
||||
//! \ref action_bar_layer_legacy2_set_click_config_provider() and
|
||||
//! \ref action_bar_layer_legacy2_set_context() to register the click configuration
|
||||
//! provider to configure the buttons actions.
|
||||
//! @note It is advised to call this is in the window's `.load` or `.appear`
|
||||
//! handler. Make sure to call \ref action_bar_layer_legacy2_remove_from_window() in the
|
||||
//! window's `.unload` or `.disappear` handler.
|
||||
//! @note Adding additional layers to the window's root layer after this calll
|
||||
//! can occlude the action bar.
|
||||
//! @param action_bar The action bar to associate with the window
|
||||
//! @param window The window with which the action bar is to be associated
|
||||
void action_bar_layer_legacy2_add_to_window(ActionBarLayerLegacy2 *action_bar,
|
||||
struct Window *window);
|
||||
|
||||
//! Removes the action bar from the window and unconfigures the window's
|
||||
//! click configuration provider. `NULL` is set as the window's new click config
|
||||
//! provider and also as its callback context. If it has not been added to a
|
||||
//! window before, this function is a no-op.
|
||||
//! @param action_bar The action bar to de-associate from its current window
|
||||
void action_bar_layer_legacy2_remove_from_window(ActionBarLayerLegacy2 *action_bar);
|
||||
|
||||
//! Sets the background color of the action bar. Defaults to \ref GColorBlack.
|
||||
//! The action bar's layer is automatically marked dirty.
|
||||
//! @param action_bar The action bar of which to set the background color
|
||||
//! @param background_color The new background color
|
||||
void action_bar_layer_legacy2_set_background_color_2bit(ActionBarLayerLegacy2 *action_bar,
|
||||
GColor2 background_color);
|
||||
|
||||
//! @} // end addtogroup ActionBarLayerLegacy2
|
||||
//! @} // end addtogroup Layer
|
||||
//! @} // end addtogroup UI
|
408
src/fw/applib/legacy2/ui/animation_legacy2.c
Normal file
408
src/fw/applib/legacy2/ui/animation_legacy2.c
Normal file
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "animation_private_legacy2.h"
|
||||
#include "animation_legacy2.h"
|
||||
|
||||
#include "applib/ui/animation_timing.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "kernel/kernel_applib_state.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/math.h"
|
||||
#include "util/order.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
///////////////////
|
||||
// Base AnimationLegacy2
|
||||
//
|
||||
|
||||
static const uint32_t ANIMATION_TARGET_FRAME_INTERVAL_MS_LEGACY2 = 40; // 25 Hz
|
||||
|
||||
static void animation_legacy2_private_run(AnimationLegacy2Scheduler *animation_legacy2_scheduler);
|
||||
|
||||
// Unfortunately, the fields of the AnimationLegacy2 struct were made part of
|
||||
// the Pebble SDK public interface, and some apps statically allocated
|
||||
// AnimationLegacy2 or PropertyAnimation structs. Therefore, the size of an
|
||||
// AnimationLegacy2 struct can never change without breaking apps. Thankfully,
|
||||
// some bits of the AnimationLegacy2 struct were left unused due to padding and
|
||||
// unnecessary use of bitfields. To be able to implement the custom
|
||||
// animation curves feature, a function pointer needed to be added into
|
||||
// the struct. There are only 30 unallocated bits in total in the
|
||||
// struct: 28 bits of padding at the end, and two bits in the
|
||||
// AnimationCurve enum (AnimationLegacy2.curve = 0b1xx encodes custom function,
|
||||
// leaving lowest two bits free).
|
||||
//
|
||||
// Out of the 32 bits that make up a function pointer, only 31 bits need
|
||||
// to be encoded. The least-significant bit will always be 1, indicating
|
||||
// that the function is in Thumb-mode. Since we have only 30 bits free
|
||||
// in the struct, this means that we need to drop at least one bit from
|
||||
// the pointer, restricting us from being able to store a pointer to a
|
||||
// function anywhere in one half of the total address space. Since
|
||||
// current Pebble hardware (at the time of writing) can only have code
|
||||
// in one of a few small ranges, a pointer can be packed into much fewer
|
||||
// than 28 bits while still being able to address a function anywhere in
|
||||
// memory that exists.
|
||||
//
|
||||
// For reference, those ranges are:
|
||||
// 0x0000 0000 - 0x0001 FFFF - Internal Flash, remapped at 0x0
|
||||
// 0x0800 0000 - 0x0801 FFFF - Internal Flash
|
||||
// 0x2000 0000 - 0x2002 FFFF - Internal SRAM
|
||||
//
|
||||
// When such a time comes that more ranges are required, the spare two
|
||||
// bits in AnimationLegacy2.curve can be utilized.
|
||||
#ifndef UNITTEST
|
||||
_Static_assert(sizeof(AnimationLegacy2) <= 40, "Breaking back-compatibility!");
|
||||
_Static_assert(sizeof(AnimationLegacy2Scheduler) <= 16, "Breaking back-compatibility!");
|
||||
#endif
|
||||
|
||||
|
||||
// Pack a function pointer into 28 bits. We do this by dropping bits
|
||||
// 1, 26, 30 and 31 and packing the remainder together.
|
||||
static uintptr_t prv_custom_curve_ptr_pack(AnimationCurveFunction ptr) {
|
||||
uintptr_t bits = (uintptr_t)ptr;
|
||||
uint8_t top_byte = bits >> 24;
|
||||
PBL_ASSERTN((top_byte & 0b11000100) == 0); // Function pointer outside of packable range!
|
||||
top_byte = ((top_byte & 0b00111000) >> 1) | (top_byte & 0b11);
|
||||
bits = (top_byte << 24) | (bits & 0xffffff);
|
||||
bits >>= 1;
|
||||
return bits;
|
||||
}
|
||||
|
||||
// Unpack a function pointer previously packed by prv_custom_curve_ptr_pack
|
||||
static AnimationCurveFunction prv_custom_curve_ptr_unpack(uintptr_t bits) {
|
||||
bits = (bits << 1) | 1; // Set the Thumb bit on the function pointer
|
||||
uint8_t top_byte = bits >> 24;
|
||||
top_byte = ((top_byte & 0b11100) << 1) | (top_byte & 0b11);
|
||||
bits = (top_byte << 24) | (bits & 0xffffff);
|
||||
return (AnimationCurveFunction)bits;
|
||||
}
|
||||
|
||||
struct AnimationLegacy2 *animation_legacy2_create(void) {
|
||||
AnimationLegacy2 *animation = task_malloc(sizeof(AnimationLegacy2));
|
||||
if (animation) {
|
||||
animation_legacy2_init(animation);
|
||||
}
|
||||
return (animation);
|
||||
}
|
||||
|
||||
void animation_legacy2_destroy(AnimationLegacy2 *animation) {
|
||||
if (animation == NULL) {
|
||||
return;
|
||||
}
|
||||
animation_legacy2_unschedule(animation);
|
||||
task_free(animation);
|
||||
}
|
||||
|
||||
void animation_legacy2_init(struct AnimationLegacy2 *animation) {
|
||||
PBL_ASSERTN(animation != NULL);
|
||||
*animation = (AnimationLegacy2){};
|
||||
animation->duration_ms = 250;
|
||||
animation->curve = AnimationCurveEaseInOut;
|
||||
animation->handlers = (AnimationLegacy2Handlers) { NULL, NULL };
|
||||
animation->context = NULL;
|
||||
animation->is_completed = false;
|
||||
}
|
||||
|
||||
static int animation_legacy2_scheduler_comparator(AnimationLegacy2 *animation_legacy2_a,
|
||||
AnimationLegacy2 *animation_legacy2_b) {
|
||||
return serial_distance32(animation_legacy2_a->abs_start_time_ms,
|
||||
animation_legacy2_b->abs_start_time_ms);
|
||||
}
|
||||
|
||||
static AnimationLegacy2Scheduler* animation_legacy2_scheduler_data_for_app_ctx_idx(
|
||||
AppTaskCtxIdx idx) {
|
||||
if (idx == AppTaskCtxIdxApp) {
|
||||
return (AnimationLegacy2Scheduler *)app_state_get_animation_state();
|
||||
}
|
||||
return (AnimationLegacy2Scheduler *)kernel_applib_get_animation_state();
|
||||
}
|
||||
|
||||
static AnimationLegacy2Scheduler* get_current_scheduler(void) {
|
||||
if (pebble_task_get_current() == PebbleTask_App) {
|
||||
return (AnimationLegacy2Scheduler *)app_state_get_animation_state();
|
||||
}
|
||||
return (AnimationLegacy2Scheduler *)kernel_applib_get_animation_state();
|
||||
}
|
||||
|
||||
inline static uint32_t animation_legacy2_get_ms_since_system_start(void) {
|
||||
return (sys_get_ticks() * 1000 / RTC_TICKS_HZ);
|
||||
}
|
||||
|
||||
static void animation_legacy2_timer_callback(
|
||||
AnimationLegacy2Scheduler *animation_legacy2_scheduler) {
|
||||
animation_legacy2_scheduler->timer_handle = NULL;
|
||||
animation_legacy2_private_run(animation_legacy2_scheduler);
|
||||
}
|
||||
|
||||
static void animation_legacy2_reschedule_timer(
|
||||
AnimationLegacy2Scheduler *animation_legacy2_scheduler,
|
||||
uint32_t rate_control_delay_ms) {
|
||||
AnimationLegacy2 *animation = (AnimationLegacy2 *) animation_legacy2_scheduler->head;
|
||||
if (animation == NULL) {
|
||||
return;
|
||||
}
|
||||
const uint32_t now = animation_legacy2_get_ms_since_system_start();
|
||||
const int32_t delta_ms = serial_distance32(now, animation->abs_start_time_ms);
|
||||
const uint32_t interval_ms = MAX(delta_ms, 0) + rate_control_delay_ms;
|
||||
|
||||
if (animation_legacy2_scheduler->timer_handle != NULL) {
|
||||
app_timer_reschedule(animation_legacy2_scheduler->timer_handle, interval_ms);
|
||||
// Ignore the return value of reschedule. If it fails it probably means the callback is already
|
||||
// fired and we're waiting for the handler to be called. This will end up rescheduling us for
|
||||
// the right time once that gets handled.
|
||||
} else {
|
||||
animation_legacy2_scheduler->timer_handle =
|
||||
app_timer_register(interval_ms, (AppTimerCallback) animation_legacy2_timer_callback,
|
||||
animation_legacy2_scheduler);
|
||||
PBL_ASSERTN(animation_legacy2_scheduler->timer_handle != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void animation_legacy2_private_schedule(AnimationLegacy2 *animation,
|
||||
AnimationLegacy2Scheduler* animation_legacy2_scheduler) {
|
||||
PBL_ASSERTN(animation != NULL);
|
||||
PBL_ASSERTN(animation->implementation->update != NULL);
|
||||
|
||||
if (animation_legacy2_is_scheduled(animation)) {
|
||||
animation_legacy2_unschedule(animation);
|
||||
}
|
||||
|
||||
const uint32_t now = animation_legacy2_get_ms_since_system_start();
|
||||
animation->abs_start_time_ms = now + animation->delay_ms;
|
||||
if (animation->implementation->setup != NULL) {
|
||||
animation->implementation->setup(animation);
|
||||
}
|
||||
|
||||
const bool old_head_is_animating = animation_legacy2_scheduler->head
|
||||
? (((AnimationLegacy2 *)animation_legacy2_scheduler->head)->abs_start_time_ms <= now)
|
||||
: false;
|
||||
const bool ascending = true;
|
||||
animation_legacy2_scheduler->head = list_sorted_add(animation_legacy2_scheduler->head,
|
||||
&animation->list_node, (Comparator) animation_legacy2_scheduler_comparator, ascending);
|
||||
const bool has_new_head = (&animation->list_node == animation_legacy2_scheduler->head);
|
||||
if (has_new_head) {
|
||||
// Only reschedule the timer if the previous head animation wasn't running yet:
|
||||
if (old_head_is_animating == false) {
|
||||
animation_legacy2_reschedule_timer(animation_legacy2_scheduler, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void animation_legacy2_private_unschedule(AnimationLegacy2 *animation,
|
||||
AnimationLegacy2Scheduler *animation_legacy2_scheduler, const bool finished) {
|
||||
if (animation == NULL || !animation_legacy2_is_scheduled(animation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_ASSERTN(animation->implementation != NULL);
|
||||
|
||||
const bool was_old_head = (animation == (AnimationLegacy2 *) animation_legacy2_scheduler->head);
|
||||
list_remove(&animation->list_node, &animation_legacy2_scheduler->head, NULL);
|
||||
// Reschedule the timer if we're removing the head animation:
|
||||
if (was_old_head && animation_legacy2_scheduler->head != NULL) {
|
||||
animation_legacy2_reschedule_timer(animation_legacy2_scheduler, 0);
|
||||
}
|
||||
// Reset these fields, before calling .stopped(), so that this animation
|
||||
// instance can be rescheduled again in the .stopped() handler, if needed.
|
||||
animation->abs_start_time_ms = 0;
|
||||
animation->is_completed = false;
|
||||
if (animation->handlers.stopped) {
|
||||
animation->handlers.stopped(animation, finished, animation->context);
|
||||
}
|
||||
if (animation->implementation->teardown != NULL) {
|
||||
animation->implementation->teardown(animation);
|
||||
}
|
||||
}
|
||||
|
||||
void animation_legacy2_private_unschedule_all(AppTaskCtxIdx idx) {
|
||||
AnimationLegacy2Scheduler *animation_legacy2_scheduler =
|
||||
animation_legacy2_scheduler_data_for_app_ctx_idx(idx);
|
||||
AnimationLegacy2 *animation = (AnimationLegacy2 *) animation_legacy2_scheduler->head;
|
||||
while (animation) {
|
||||
AnimationLegacy2 *next = (AnimationLegacy2 *) list_get_next(&animation->list_node);
|
||||
animation_legacy2_private_unschedule(animation, animation_legacy2_scheduler, false);
|
||||
animation = next;
|
||||
}
|
||||
}
|
||||
|
||||
void animation_legacy2_private_init_scheduler(
|
||||
AnimationLegacy2Scheduler *animation_legacy2_scheduler) {
|
||||
*animation_legacy2_scheduler = (AnimationLegacy2Scheduler) {
|
||||
.timer_handle = NULL,
|
||||
.last_delay_ms = ANIMATION_TARGET_FRAME_INTERVAL_MS_LEGACY2,
|
||||
.last_frame_time = animation_legacy2_get_ms_since_system_start()
|
||||
};
|
||||
}
|
||||
|
||||
void animation_legacy2_schedule(AnimationLegacy2 *animation) {
|
||||
animation_legacy2_private_schedule(animation, get_current_scheduler());
|
||||
}
|
||||
|
||||
void animation_legacy2_unschedule(AnimationLegacy2 *animation) {
|
||||
animation_legacy2_private_unschedule(animation, get_current_scheduler(), false);
|
||||
}
|
||||
|
||||
void animation_legacy2_unschedule_all(void) {
|
||||
animation_legacy2_private_unschedule_all(AppTaskCtxIdxApp);
|
||||
}
|
||||
|
||||
bool animation_legacy2_is_scheduled(AnimationLegacy2 *animation) {
|
||||
AnimationLegacy2Scheduler *animation_legacy2_scheduler = get_current_scheduler();
|
||||
return list_contains(animation_legacy2_scheduler->head, &animation->list_node);
|
||||
}
|
||||
|
||||
static void animation_legacy2_private_run(AnimationLegacy2Scheduler *animation_legacy2_scheduler) {
|
||||
AnimationLegacy2 *animation = (AnimationLegacy2 *) animation_legacy2_scheduler->head;
|
||||
|
||||
const uint32_t now = animation_legacy2_get_ms_since_system_start();
|
||||
|
||||
while (animation) {
|
||||
const int32_t rel_ms_running = serial_distance32(animation->abs_start_time_ms, now);
|
||||
|
||||
if (rel_ms_running < 0) {
|
||||
// AnimationLegacy2s are ordered by abs_start_time_ms.
|
||||
// We've reached an animation that should not start yet, so
|
||||
// everything after and including this animation shouldn't run yet.
|
||||
break;
|
||||
}
|
||||
|
||||
// Get a pointer to next now, because after unscheduling this animation won't have a next.
|
||||
AnimationLegacy2 *next = (AnimationLegacy2*) list_get_next(&animation->list_node);
|
||||
|
||||
if (animation->is_completed) {
|
||||
// Unschedule + call animation.stopped callback:
|
||||
const bool finished = true;
|
||||
animation_legacy2_private_unschedule(animation, animation_legacy2_scheduler, finished);
|
||||
} else {
|
||||
// If this is the animation's first frame, call the 'started' handler:
|
||||
if (animation->handlers.started &&
|
||||
animation->abs_start_time_ms > animation_legacy2_scheduler->last_frame_time) {
|
||||
animation->handlers.started(animation, animation->context);
|
||||
}
|
||||
|
||||
const uint32_t time_normalized_raw = (animation->duration_ms != 0)
|
||||
? ((ANIMATION_NORMALIZED_MAX * rel_ms_running) / animation->duration_ms)
|
||||
: ANIMATION_NORMALIZED_MAX;
|
||||
const uint32_t time_normalized = MIN(time_normalized_raw, ANIMATION_NORMALIZED_MAX);
|
||||
uint32_t distance_normalized;
|
||||
if (animation->curve >= AnimationCurveCustomFunction) {
|
||||
distance_normalized = prv_custom_curve_ptr_unpack(
|
||||
animation->custom_curve_function)(time_normalized);
|
||||
} else {
|
||||
distance_normalized = animation_timing_curve(time_normalized, animation->curve);
|
||||
}
|
||||
animation->implementation->update(animation, distance_normalized);
|
||||
|
||||
const bool completed = (time_normalized == ANIMATION_NORMALIZED_MAX);
|
||||
if (completed) {
|
||||
if (animation->duration_ms != ANIMATION_DURATION_INFINITE) {
|
||||
// Leave the animation on the list for now, we'll unschedule it the next time,
|
||||
// so it's guaranteed the animation.stopped callback gets fired after the
|
||||
// (render) events caused by this last update have been processed:
|
||||
animation->is_completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
animation = next;
|
||||
};
|
||||
|
||||
// Frame rate control:
|
||||
const int32_t frame_interval_ms = serial_distance32(
|
||||
animation_legacy2_scheduler->last_frame_time, now);
|
||||
const int32_t error_ms = frame_interval_ms - ANIMATION_TARGET_FRAME_INTERVAL_MS_LEGACY2;
|
||||
const int32_t theoretic_delay_ms = animation_legacy2_scheduler->last_delay_ms - error_ms;
|
||||
const uint32_t delay_ms = CLIP(theoretic_delay_ms, (int32_t) 0,
|
||||
(int32_t) ANIMATION_TARGET_FRAME_INTERVAL_MS_LEGACY2);
|
||||
|
||||
animation_legacy2_reschedule_timer(animation_legacy2_scheduler, delay_ms);
|
||||
animation_legacy2_scheduler->last_delay_ms = delay_ms;
|
||||
animation_legacy2_scheduler->last_frame_time = now;
|
||||
}
|
||||
|
||||
void animation_legacy2_set_handlers(AnimationLegacy2 *animation, AnimationLegacy2Handlers handlers,
|
||||
void *context) {
|
||||
PBL_ASSERTN(animation->abs_start_time_ms == 0); // can't set after animation has been added
|
||||
animation->context = context;
|
||||
animation->handlers = handlers;
|
||||
}
|
||||
|
||||
void animation_legacy2_set_implementation(AnimationLegacy2 *animation,
|
||||
const AnimationLegacy2Implementation *implementation) {
|
||||
PBL_ASSERTN(animation->abs_start_time_ms == 0); // can't set after animation has been added
|
||||
animation->implementation = implementation;
|
||||
}
|
||||
|
||||
void *animation_legacy2_get_context(AnimationLegacy2 *animation) {
|
||||
return animation->context;
|
||||
}
|
||||
|
||||
void animation_legacy2_set_delay(AnimationLegacy2 *animation, uint32_t delay_ms) {
|
||||
PBL_ASSERTN(animation->abs_start_time_ms == 0); // can't set after animation has been added
|
||||
animation->delay_ms = delay_ms;
|
||||
}
|
||||
|
||||
void animation_legacy2_set_duration(AnimationLegacy2 *animation, uint32_t duration_ms) {
|
||||
PBL_ASSERTN(animation->abs_start_time_ms == 0); // can't set after animation has been added
|
||||
animation->duration_ms = duration_ms;
|
||||
}
|
||||
|
||||
void animation_legacy2_set_curve(AnimationLegacy2 *animation, AnimationCurve curve) {
|
||||
PBL_ASSERTN(animation->abs_start_time_ms == 0); // can't set after animation has been added
|
||||
PBL_ASSERTN(curve < AnimationCurveCustomFunction);
|
||||
animation->curve = curve;
|
||||
}
|
||||
|
||||
void animation_legacy2_set_custom_curve(AnimationLegacy2 *animation,
|
||||
AnimationCurveFunction curve_function) {
|
||||
animation->curve = AnimationCurveCustomFunction;
|
||||
animation->custom_curve_function = prv_custom_curve_ptr_pack(curve_function);
|
||||
}
|
||||
|
||||
AnimationCurveFunction animation_legacy2_get_custom_curve(AnimationLegacy2 *animation) {
|
||||
return prv_custom_curve_ptr_unpack(animation->custom_curve_function);
|
||||
}
|
||||
|
||||
static void dump_scheduler(char* buffer, int buffer_size,
|
||||
AnimationLegacy2Scheduler* animation_legacy2_scheduler) {
|
||||
AnimationLegacy2 *animation = (AnimationLegacy2 *) animation_legacy2_scheduler->head;
|
||||
while (animation) {
|
||||
dbgserial_putstr_fmt(buffer, buffer_size,
|
||||
"<%p> { abs_start_time_ms = %"PRIu32", delay = %"PRIu32", duration = %"PRIu32", "
|
||||
"curve = %i, run = %p }",
|
||||
animation, animation->abs_start_time_ms, animation->delay_ms, animation->duration_ms,
|
||||
animation->curve, animation->implementation->update);
|
||||
|
||||
animation = (AnimationLegacy2 *)list_get_next(&animation->list_node);
|
||||
}
|
||||
}
|
||||
|
||||
void command_legacy2_animations_info(void) {
|
||||
char buffer[128];
|
||||
dbgserial_putstr_fmt(buffer, sizeof(buffer), "Now: %"PRIu32,
|
||||
animation_legacy2_get_ms_since_system_start());
|
||||
|
||||
dbgserial_putstr_fmt(buffer, sizeof(buffer), "Kernel AnimationLegacy2s:");
|
||||
dump_scheduler(buffer, sizeof(buffer),
|
||||
(AnimationLegacy2Scheduler *)kernel_applib_get_animation_state());
|
||||
|
||||
dbgserial_putstr_fmt(buffer, sizeof(buffer), "App AnimationLegacy2s:");
|
||||
dump_scheduler(buffer, sizeof(buffer),
|
||||
(AnimationLegacy2Scheduler *)app_state_get_animation_state());
|
||||
}
|
286
src/fw/applib/legacy2/ui/animation_legacy2.h
Normal file
286
src/fw/applib/legacy2/ui/animation_legacy2.h
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "util/list.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "applib/ui/animation.h"
|
||||
|
||||
//! @file animation.h
|
||||
//! @addtogroup UI
|
||||
//! @{
|
||||
//! @addtogroup AnimationLegacy2
|
||||
//! \brief Abstract framework to create arbitrary animations
|
||||
//!
|
||||
//! The AnimationLegacy2 framework provides your Pebble app with an base layer to create arbitrary
|
||||
//! animations. The simplest way to work with animations is to use the layer frame
|
||||
//! \ref PropertyAnimationLegacy2, which enables you to move a Layer around on the screen.
|
||||
//! Using animation_legacy2_set_implementation(), you can implement a custom animation.
|
||||
//!
|
||||
|
||||
|
||||
//! @{
|
||||
|
||||
///////////////////
|
||||
// Base AnimationLegacy2
|
||||
//
|
||||
|
||||
struct AnimationLegacy2;
|
||||
struct AnimationLegacy2Implementation;
|
||||
struct AnimationLegacy2Handlers;
|
||||
|
||||
|
||||
//! Creates a new AnimationLegacy2 on the heap and initalizes it with the default values.
|
||||
//!
|
||||
//! * Duration: 250ms,
|
||||
//! * Curve: \ref AnimationCurveEaseInOut (ease-in-out),
|
||||
//! * Delay: 0ms,
|
||||
//! * Handlers: `{NULL, NULL}` (none),
|
||||
//! * Context: `NULL` (none),
|
||||
//! * Implementation: `NULL` (no implementation),
|
||||
//! * Scheduled: no
|
||||
//! @return A pointer to the animation. `NULL` if the animation could not
|
||||
//! be created
|
||||
struct AnimationLegacy2 *animation_legacy2_create(void);
|
||||
|
||||
//! Destroys an AnimationLegacy2 previously created by animation_legacy2_create.
|
||||
void animation_legacy2_destroy(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! @internal
|
||||
//! resets animation to default values (see animation_create)
|
||||
void animation_legacy2_init(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! Sets the time in milliseconds that an animation takes from start to finish.
|
||||
//! @param animation The animation for which to set the duration.
|
||||
//! @param duration_ms The duration in milliseconds of the animation. This excludes
|
||||
//! any optional delay as set using \ref animation_legacy2_set_delay().
|
||||
void animation_legacy2_set_duration(struct AnimationLegacy2 *animation, uint32_t duration_ms);
|
||||
|
||||
//! Sets an optional delay for the animation.
|
||||
//! @param animation The animation for which to set the delay.
|
||||
//! @param delay_ms The delay in milliseconds that the animation system should
|
||||
//! wait from the moment the animation is scheduled to starting the animation.
|
||||
void animation_legacy2_set_delay(struct AnimationLegacy2 *animation, uint32_t delay_ms);
|
||||
|
||||
//! Sets the animation curve for the animation.
|
||||
//! @param animation The animation for which to set the curve.
|
||||
//! @param curve The type of curve.
|
||||
//! @see AnimationCurve
|
||||
void animation_legacy2_set_curve(struct AnimationLegacy2 *animation, AnimationCurve curve);
|
||||
|
||||
//! Sets a custom animation curve function.
|
||||
//! @param animation The animation for which to set the curve.
|
||||
//! @param curve_function The custom animation curve function.
|
||||
//! @see AnimationCurveFunction
|
||||
void animation_legacy2_set_custom_curve(struct AnimationLegacy2 *animation,
|
||||
AnimationCurveFunction curve_function);
|
||||
|
||||
//! Gets the custom animation curve function.
|
||||
//! @param animation The animation for which to get the curve.
|
||||
//! @return The custom animation curve function for the given animation. NULL if not set.
|
||||
//! @see AnimationCurveFunction
|
||||
AnimationCurveFunction animation_legacy2_get_custom_curve(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! The function pointer type of the handler that will be called when an animation is started,
|
||||
//! just before updating the first frame of the animation.
|
||||
//! @param animation The animation that was started.
|
||||
//! @param context The pointer to custom, application specific data, as set using
|
||||
//! \ref animation_legacy2_set_handlers()
|
||||
//! @note This is called after any optional delay as set by \ref animation_legacy2_set_delay()
|
||||
//! has expired.
|
||||
//! @see animation_legacy2_set_handlers
|
||||
typedef void (*AnimationLegacy2StartedHandler)(struct AnimationLegacy2 *animation, void *context);
|
||||
|
||||
//! The function pointer type of the handler that will be called when the animation is stopped.
|
||||
//! @param animation The animation that was stopped.
|
||||
//! @param finished True if the animation was stopped because it was finished normally,
|
||||
//! or False if the animation was stopped prematurely, because it was unscheduled before finishing.
|
||||
//! @param context The pointer to custom, application specific data, as set using
|
||||
//! \ref animation_legacy2_set_handlers()
|
||||
//! @see animation_legacy2_set_handlers
|
||||
typedef void (*AnimationLegacy2StoppedHandler)(struct AnimationLegacy2 *animation, bool finished,
|
||||
void *context);
|
||||
|
||||
//! The handlers that will get called when an animation starts and stops.
|
||||
//! See documentation with the function pointer types for more information.
|
||||
//! @see animation_legacy2_set_handlers
|
||||
typedef struct AnimationLegacy2Handlers {
|
||||
//! The handler that will be called when an animation is started.
|
||||
AnimationLegacy2StartedHandler started;
|
||||
//! The handler that will be called when an animation is stopped.
|
||||
AnimationLegacy2StoppedHandler stopped;
|
||||
} AnimationLegacy2Handlers;
|
||||
|
||||
//! Sets the callbacks for the animation.
|
||||
//! Often an application needs to run code at the start or at the end of an animation.
|
||||
//! Using this function is possible to register callback functions with an animation,
|
||||
//! that will get called at the start and end of the animation.
|
||||
//! @param animation The animation for which to set up the callbacks.
|
||||
//! @param callbacks The callbacks.
|
||||
//! @param context A pointer to application specific data, that will be passed as an argument by
|
||||
//! the animation subsystem when a callback is called.
|
||||
void animation_legacy2_set_handlers(struct AnimationLegacy2 *animation,
|
||||
AnimationLegacy2Handlers callbacks, void *context);
|
||||
|
||||
//! Gets the application-specific callback context of the animation.
|
||||
//! This `void` pointer is passed as an argument when the animation system calls AnimationHandlers
|
||||
//! callbacks. The context pointer can be set to point to any application specific data using
|
||||
//! \ref animation_legacy2_set_handlers().
|
||||
//! @param animation The animation.
|
||||
//! @see animation_legacy2_set_handlers
|
||||
void *animation_legacy2_get_context(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! Schedules the animation. Call this once after configuring an animation to get it to
|
||||
//! start running.
|
||||
//!
|
||||
//! If the animation's implementation has a `.setup` callback it will get called before
|
||||
//! this function returns.
|
||||
//!
|
||||
//! @note If the animation was already scheduled,
|
||||
//! it will first unschedule it and then re-schedule it again.
|
||||
//! Note that in that case, the animation's `.stopped` handler, the implementation's
|
||||
//! `.teardown` and `.setup` will get called, due to the unscheduling and scheduling.
|
||||
//! @param animation The animation to schedule.
|
||||
//! @see \ref animation_legacy2_unschedule()
|
||||
void animation_legacy2_schedule(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! Unschedules the animation, which in effect stops the animation.
|
||||
//! @param animation The animation to unschedule.
|
||||
//! @note If the animation was not yet finished, unscheduling it will
|
||||
//! cause its `.stopped` handler to get called, with the "finished" argument set to false.
|
||||
//! @see \ref animation_legacy2_schedule()
|
||||
void animation_legacy2_unschedule(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! Unschedules all animations of the application.
|
||||
//! @see animation_legacy2_unschedule
|
||||
void animation_legacy2_unschedule_all(void);
|
||||
|
||||
//! @return True if the animation was scheduled, or false if it was not.
|
||||
//! @note An animation will be scheduled when it is running and not finished yet.
|
||||
//! An animation that has finished is automatically unscheduled.
|
||||
//! @param animation The animation for which to get its scheduled state.
|
||||
//! @see animation_legacy2_schedule
|
||||
//! @see animation_legacy2_unschedule
|
||||
bool animation_legacy2_is_scheduled(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! The data structure of an animation.
|
||||
typedef struct AnimationLegacy2 {
|
||||
ListNode list_node;
|
||||
const struct AnimationLegacy2Implementation *implementation;
|
||||
AnimationLegacy2Handlers handlers;
|
||||
void *context;
|
||||
//! Absolute time when the animation got scheduled, in ms since system start.
|
||||
uint32_t abs_start_time_ms;
|
||||
uint32_t delay_ms;
|
||||
uint32_t duration_ms;
|
||||
AnimationCurve curve:3;
|
||||
bool is_completed:1;
|
||||
//! Pointer to a custom curve. Unfortunately, due to backward-compatibility
|
||||
//! constraints, it must fit into 28 bits.
|
||||
//! It is only valid when curve == AnimationCurveCustomFunction.
|
||||
//! The mapping from 28-bit field to pointer is unpublished. Call
|
||||
//! animation_legacy2_set_custom_curve() to ensure your app continues to run
|
||||
//! after future Pebble updates.
|
||||
uintptr_t custom_curve_function:28;
|
||||
} AnimationLegacy2;
|
||||
|
||||
///////////////////////////////////////
|
||||
// Implementing custom animation types
|
||||
|
||||
//! Pointer to function that (optionally) prepares the animation for running.
|
||||
//! This callback is called when the animation is added to the scheduler.
|
||||
//! @param animation The animation that needs to be set up.
|
||||
//! @see animation_legacy2_schedule
|
||||
//! @see AnimationTeardownImplementation
|
||||
typedef void (*AnimationLegacy2SetupImplementation)(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! Pointer to function that updates the animation according to the given normalized distance.
|
||||
//! This callback will be called repeatedly by the animation scheduler whenever the animation needs
|
||||
//! to be updated.
|
||||
//! @param animation The animation that needs to update; gets passed in by the animation framework.
|
||||
//! @param distance_normalized The current normalized distance; gets passed in by the animation
|
||||
//! framework for each animation frame.
|
||||
//! This is a value between \ref ANIMATION_NORMALIZED_MIN and \ref ANIMATION_NORMALIZED_MAX.
|
||||
//! At the start of the animation, the value will be \ref ANIMATION_NORMALIZED_MIN.
|
||||
//! At the end of the animation, the value will be \ref ANIMATION_NORMALIZED_MAX.
|
||||
//! For each frame during the animation, the value will be the distance along the
|
||||
//! animation path, mapped between \ref ANIMATION_NORMALIZED_MIN and
|
||||
//! \ref ANIMATION_NORMALIZED_MAX based on the animation duration and the
|
||||
//! \ref AnimationCurve set.
|
||||
//! For example, say an animation was scheduled at t = 1.0s, has a delay of 1.0s,
|
||||
//! a duration of 2.0s and a curve of AnimationCurveLinear.
|
||||
//! Then the .update callback will get called on t = 2.0s with
|
||||
//! distance_normalized = \ref ANIMATION_NORMALIZED_MIN. For each frame
|
||||
//! thereafter until t = 4.0s, the update callback will get called where
|
||||
//! distance_normalized is
|
||||
//! (\ref ANIMATION_NORMALIZED_MIN + (((\ref ANIMATION_NORMALIZED_MAX -
|
||||
//! \ref ANIMATION_NORMALIZED_MIN) * t) / duration)).
|
||||
//! Other animation curves will result in a non-linear relation between
|
||||
//! distance_normalized and time.
|
||||
//! @internal
|
||||
//! @see animation_legacy2_timing.h
|
||||
typedef void (*AnimationLegacy2UpdateImplementation)(struct AnimationLegacy2 *animation,
|
||||
const uint32_t distance_normalized);
|
||||
|
||||
//! Pointer to function that (optionally) cleans up the animation.
|
||||
//! This callback is called when the animation is removed from the scheduler.
|
||||
//! In case the `.setup` implementation
|
||||
//! allocated any memory, this is a good place to release that memory again.
|
||||
//! @param animation The animation that needs to be teared down.
|
||||
//! @see animation_legacy2_unschedule
|
||||
//! @see AnimationSetupImplementation
|
||||
typedef void (*AnimationLegacy2TeardownImplementation)(struct AnimationLegacy2 *animation);
|
||||
|
||||
//! The 3 callbacks that implement a custom animation.
|
||||
//! Only the `.update` callback is mandatory, `.setup` and `.teardown` are optional.
|
||||
//! See the documentation with the function pointer typedefs for more information.
|
||||
//!
|
||||
//! @note The `.setup` callback is called immediately after scheduling the animation,
|
||||
//! regardless if there is a delay set for that animation using \ref animation_legacy2_set_delay().
|
||||
//!
|
||||
//! The diagram below illustrates the order in which callbacks can be expected to get called
|
||||
//! over the life cycle of an animation. It also illustrates where the implementation of
|
||||
//! different animation callbacks are intended to be “living”.
|
||||
//! 
|
||||
//!
|
||||
//! @see AnimationLegacy2SetupImplementation
|
||||
//! @see AnimationLegacy2UpdateImplementation
|
||||
//! @see AnimationLegacy2TeardownImplementation
|
||||
typedef struct AnimationLegacy2Implementation {
|
||||
//! Called by the animation system when an animation is scheduled, to prepare it for running.
|
||||
//! This callback is optional and can be left `NULL` when not needed.
|
||||
AnimationLegacy2SetupImplementation setup;
|
||||
//! Called by the animation system when the animation needs to calculate the next animation frame.
|
||||
//! This callback is mandatory and should not be left `NULL`.
|
||||
AnimationLegacy2UpdateImplementation update;
|
||||
//! Called by the animation system when an animation is unscheduled, to clean up after it has run.
|
||||
//! This callback is optional and can be left `NULL` when not needed.
|
||||
AnimationLegacy2TeardownImplementation teardown;
|
||||
} AnimationLegacy2Implementation;
|
||||
|
||||
//! Sets the implementation of the custom animation.
|
||||
//! When implementing custom animations, use this function to specify what functions need to be called to
|
||||
//! for the setup, frame update and teardown of the animation.
|
||||
//! @param animation The animation for which to set the implementation.
|
||||
//! @param implementation The structure with function pointers to the implementation of the setup, update and teardown functions.
|
||||
//! @see AnimationImplementation
|
||||
void animation_legacy2_set_implementation(struct AnimationLegacy2 *animation,
|
||||
const AnimationLegacy2Implementation *implementation);
|
||||
|
||||
//! @} // group AnimationLegacy2
|
||||
//! @} // group UI
|
36
src/fw/applib/legacy2/ui/animation_private_legacy2.h
Normal file
36
src/fw/applib/legacy2/ui/animation_private_legacy2.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 "process_management/app_manager.h"
|
||||
|
||||
#include "syscall/syscall.h"
|
||||
|
||||
typedef struct {
|
||||
ListNode *head; //! Pointer to the Animation struct that is the animation that is scheduled
|
||||
//! first.
|
||||
AppTimer* timer_handle;
|
||||
|
||||
//! The delay the animation scheduler uses between finishing a frame and starting a new one.
|
||||
//! Derived from actual rendering/calculation times, using a PID-like control algorithm.
|
||||
uint32_t last_delay_ms;
|
||||
uint32_t last_frame_time; //! Absolute RTC time of the moment the last animation frame started.
|
||||
} AnimationLegacy2Scheduler;
|
||||
|
||||
void animation_legacy2_private_init_scheduler(AnimationLegacy2Scheduler* scheduler);
|
||||
|
||||
void animation_legacy2_private_unschedule_all(AppTaskCtxIdx app_task_ctx_idx);
|
81
src/fw/applib/legacy2/ui/menu_layer_legacy2.c
Normal file
81
src/fw/applib/legacy2/ui/menu_layer_legacy2.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "menu_layer_legacy2.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
extern void menu_layer_init_scroll_layer_callbacks(MenuLayer *menu_layer);
|
||||
|
||||
void menu_layer_legacy2_init(MenuLayer *menu_layer, const GRect *frame) {
|
||||
*menu_layer = (MenuLayer){};
|
||||
|
||||
ScrollLayer *scroll_layer = &menu_layer->scroll_layer;
|
||||
scroll_layer_init(scroll_layer, frame);
|
||||
menu_layer_init_scroll_layer_callbacks(menu_layer);
|
||||
scroll_layer_set_context(scroll_layer, menu_layer);
|
||||
|
||||
menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack);
|
||||
menu_layer_set_highlight_colors(menu_layer, GColorBlack, GColorWhite);
|
||||
|
||||
InverterLayer *inverter = &menu_layer->inverter;
|
||||
inverter_layer_init(inverter, &GRectZero);
|
||||
scroll_layer_add_child(scroll_layer, &inverter->layer);
|
||||
}
|
||||
|
||||
MenuLayer* menu_layer_legacy2_create(GRect frame) {
|
||||
MenuLayer *layer = task_malloc(sizeof(MenuLayer));
|
||||
if (layer) {
|
||||
menu_layer_legacy2_init(layer, &frame);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
void menu_layer_legacy2_set_callbacks(MenuLayer *menu_layer,
|
||||
void *callback_context,
|
||||
MenuLayerCallbacksLegacy2 callbacks) {
|
||||
menu_layer_set_callbacks(menu_layer, callback_context, &(MenuLayerCallbacks) {
|
||||
.get_num_sections = callbacks.get_num_sections,
|
||||
.get_num_rows = callbacks.get_num_rows,
|
||||
.get_cell_height = callbacks.get_cell_height,
|
||||
.get_header_height = callbacks.get_header_height,
|
||||
.draw_row = callbacks.draw_row,
|
||||
.draw_header = callbacks.draw_header,
|
||||
.select_click = callbacks.select_click,
|
||||
.select_long_click = callbacks.select_long_click,
|
||||
.selection_changed = callbacks.selection_changed,
|
||||
.get_separator_height = callbacks.get_separator_height,
|
||||
.draw_separator = callbacks.draw_separator,
|
||||
});
|
||||
}
|
||||
|
||||
void menu_layer_legacy2_set_callbacks__deprecated(MenuLayer *menu_layer,
|
||||
void *callback_context,
|
||||
MenuLayerCallbacksLegacy2__deprecated callbacks) {
|
||||
menu_layer_set_callbacks(menu_layer, callback_context, &(MenuLayerCallbacks) {
|
||||
.get_num_sections = callbacks.get_num_sections,
|
||||
.get_num_rows = callbacks.get_num_rows,
|
||||
.get_cell_height = callbacks.get_cell_height,
|
||||
.get_header_height = callbacks.get_header_height,
|
||||
.draw_row = callbacks.draw_row,
|
||||
.draw_header = callbacks.draw_header,
|
||||
.select_click = callbacks.select_click,
|
||||
.select_long_click = callbacks.select_long_click,
|
||||
.selection_changed = callbacks.selection_changed,
|
||||
});
|
||||
}
|
159
src/fw/applib/legacy2/ui/menu_layer_legacy2.h
Normal file
159
src/fw/applib/legacy2/ui/menu_layer_legacy2.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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/ui/menu_layer.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
#define MENU_CELL_LEGACY2_BASIC_SEPARATOR_HEIGHT ((const int16_t) 1)
|
||||
|
||||
//! Data structure containing all the callbacks of a MenuLayer.
|
||||
typedef struct MenuLayerCallbacksLegacy2 {
|
||||
//! Callback that gets called to get the number of sections in the menu.
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the number of sections defaults to 1.
|
||||
MenuLayerGetNumberOfSectionsCallback get_num_sections;
|
||||
|
||||
//! Callback that gets called to get the number of rows in a section. This
|
||||
//! can get called at various moments throughout the life of a menu.
|
||||
//! @note Must be set to a valid callback; `NULL` causes undefined behavior.
|
||||
MenuLayerGetNumberOfRowsInSectionsCallback get_num_rows;
|
||||
|
||||
//! Callback that gets called to get the height of a cell.
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the default height of 44 pixels is used.
|
||||
MenuLayerGetCellHeightCallback get_cell_height;
|
||||
|
||||
//! Callback that gets called to get the height of a section header.
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the defaults height of 0 pixels is used. This disables
|
||||
//! section headers.
|
||||
MenuLayerGetHeaderHeightCallback get_header_height;
|
||||
|
||||
//! Callback that gets called to render a menu item.
|
||||
//! This gets called for each menu item, every time it needs to be
|
||||
//! re-rendered.
|
||||
//! @note Must be set to a valid callback; `NULL` causes undefined behavior.
|
||||
MenuLayerDrawRowCallback draw_row;
|
||||
|
||||
//! Callback that gets called to render a section header.
|
||||
//! This gets called for each section header, every time it needs to be
|
||||
//! re-rendered.
|
||||
//! @note Must be set to a valid callback, unless `.get_header_height` is
|
||||
//! `NULL`. Causes undefined behavior otherwise.
|
||||
MenuLayerDrawHeaderCallback draw_header;
|
||||
|
||||
//! Callback that gets called when the user triggers a click with the SELECT
|
||||
//! button.
|
||||
//! @note When `NULL`, click events for the SELECT button are ignored.
|
||||
MenuLayerSelectCallback select_click;
|
||||
|
||||
//! Callback that gets called when the user triggers a long click with the
|
||||
//! SELECT button.
|
||||
//! @note When `NULL`, long click events for the SELECT button are ignored.
|
||||
MenuLayerSelectCallback select_long_click;
|
||||
|
||||
//! Callback that gets called whenever the selection changes.
|
||||
//! @note When `NULL`, selection change events are ignored.
|
||||
MenuLayerSelectionChangedCallback selection_changed;
|
||||
|
||||
//! Callback that gets called to get the height of a separator
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the default height of 1 is used.
|
||||
MenuLayerGetSeparatorHeightCallback get_separator_height;
|
||||
|
||||
//! Callback that gets called to render a separator.
|
||||
//! This gets called for each separator, every time it needs to be
|
||||
//! re-rendered.
|
||||
//! @note Must be set to a valid callback, unless `.get_separator_height` is
|
||||
//! `NULL`. Causes undefined behavior otherwise.
|
||||
MenuLayerDrawSeparatorCallback draw_separator;
|
||||
} MenuLayerCallbacksLegacy2;
|
||||
|
||||
typedef struct MenuLayerCallbacksLegacy2__deprecated {
|
||||
//! Callback that gets called to get the number of sections in the menu.
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the number of sections defaults to 1.
|
||||
MenuLayerGetNumberOfSectionsCallback get_num_sections;
|
||||
|
||||
//! Callback that gets called to get the number of rows in a section. This
|
||||
//! can get called at various moments throughout the life of a menu.
|
||||
//! @note Must be set to a valid callback; `NULL` causes undefined behavior.
|
||||
MenuLayerGetNumberOfRowsInSectionsCallback get_num_rows;
|
||||
|
||||
//! Callback that gets called to get the height of a cell.
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the default height of 44 pixels is used.
|
||||
MenuLayerGetCellHeightCallback get_cell_height;
|
||||
|
||||
//! Callback that gets called to get the height of a section header.
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the defaults height of 0 pixels is used. This disables
|
||||
//! section headers.
|
||||
MenuLayerGetHeaderHeightCallback get_header_height;
|
||||
|
||||
//! Callback that gets called to render a menu item.
|
||||
//! This gets called for each menu item, every time it needs to be
|
||||
//! re-rendered.
|
||||
//! @note Must be set to a valid callback; `NULL` causes undefined behavior.
|
||||
MenuLayerDrawRowCallback draw_row;
|
||||
|
||||
//! Callback that gets called to render a section header.
|
||||
//! This gets called for each section header, every time it needs to be
|
||||
//! re-rendered.
|
||||
//! @note Must be set to a valid callback, unless `.get_header_height` is
|
||||
//! `NULL`. Causes undefined behavior otherwise.
|
||||
MenuLayerDrawHeaderCallback draw_header;
|
||||
|
||||
//! Callback that gets called when the user triggers a click with the SELECT
|
||||
//! button.
|
||||
//! @note When `NULL`, click events for the SELECT button are ignored.
|
||||
MenuLayerSelectCallback select_click;
|
||||
|
||||
//! Callback that gets called when the user triggers a long click with the
|
||||
//! SELECT button.
|
||||
//! @note When `NULL`, long click events for the SELECT button are ignored.
|
||||
MenuLayerSelectCallback select_long_click;
|
||||
|
||||
//! Callback that gets called whenever the selection changes.
|
||||
//! @note When `NULL`, selection change events are ignored.
|
||||
MenuLayerSelectionChangedCallback selection_changed;
|
||||
|
||||
//! Callback that gets called to get the height of a separator
|
||||
//! This can get called at various moments throughout the life of a menu.
|
||||
//! @note When `NULL`, the default height of 1 is used.
|
||||
MenuLayerGetSeparatorHeightCallback get_separator_height;
|
||||
|
||||
//! Callback that gets called to render a separator.
|
||||
//! This gets called for each separator, every time it needs to be
|
||||
//! re-rendered.
|
||||
//! @note Must be set to a valid callback, unless `.get_separator_height` is
|
||||
//! `NULL`. Causes undefined behavior otherwise.
|
||||
MenuLayerDrawSeparatorCallback draw_separator;
|
||||
} MenuLayerCallbacksLegacy2__deprecated;
|
||||
|
||||
void menu_layer_legacy2_init(MenuLayer *menu_layer, const GRect *frame);
|
||||
|
||||
MenuLayer* menu_layer_legacy2_create(GRect frame);
|
||||
|
||||
void menu_layer_legacy2_set_callbacks(MenuLayer *menu_layer,
|
||||
void *callback_context,
|
||||
MenuLayerCallbacksLegacy2 callbacks);
|
||||
|
||||
void menu_layer_legacy2_set_callbacks__deprecated(MenuLayer *menu_layer,
|
||||
void *callback_context,
|
||||
MenuLayerCallbacksLegacy2__deprecated callbacks);
|
169
src/fw/applib/legacy2/ui/property_animation_legacy2.c
Normal file
169
src/fw/applib/legacy2/ui/property_animation_legacy2.c
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "property_animation_legacy2.h"
|
||||
#include "applib/ui/property_animation.h"
|
||||
|
||||
#include "applib/ui/animation_timing.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
/////////////////////
|
||||
// Property Animation
|
||||
//
|
||||
|
||||
static inline int16_t distance_interpolate(const int32_t normalized,
|
||||
int16_t from, int16_t to) {
|
||||
return from + ((normalized * (to - from)) / ANIMATION_NORMALIZED_MAX);
|
||||
}
|
||||
|
||||
void property_animation_legacy2_update_int16(PropertyAnimationLegacy2 *property_animation,
|
||||
const uint32_t distance_normalized) {
|
||||
int16_t result = distance_interpolate(distance_normalized,
|
||||
property_animation->values.from.int16,
|
||||
property_animation->values.to.int16);
|
||||
((PropertyAnimationLegacy2Implementation*)property_animation->animation.implementation)
|
||||
->accessors.setter.int16(property_animation->subject, result);
|
||||
}
|
||||
|
||||
void property_animation_legacy2_update_gpoint(PropertyAnimationLegacy2 *property_animation,
|
||||
const uint32_t distance_normalized) {
|
||||
GPoint result;
|
||||
result.x = distance_interpolate(distance_normalized,
|
||||
property_animation->values.from.gpoint.x,
|
||||
property_animation->values.to.gpoint.x);
|
||||
result.y = distance_interpolate(distance_normalized,
|
||||
property_animation->values.from.gpoint.y,
|
||||
property_animation->values.to.gpoint.y);
|
||||
((PropertyAnimationLegacy2Implementation*)
|
||||
property_animation->animation.implementation)
|
||||
->accessors.setter.gpoint(property_animation->subject, result);
|
||||
}
|
||||
|
||||
void property_animation_legacy2_update_grect(PropertyAnimationLegacy2 *property_animation,
|
||||
const uint32_t distance_normalized) {
|
||||
GRect result;
|
||||
result.origin.x = distance_interpolate(
|
||||
distance_normalized,
|
||||
property_animation->values.from.grect.origin.x,
|
||||
property_animation->values.to.grect.origin.x);
|
||||
result.origin.y = distance_interpolate(
|
||||
distance_normalized,
|
||||
property_animation->values.from.grect.origin.y,
|
||||
property_animation->values.to.grect.origin.y);
|
||||
result.size.w = distance_interpolate(
|
||||
distance_normalized,
|
||||
property_animation->values.from.grect.size.w,
|
||||
property_animation->values.to.grect.size.w);
|
||||
result.size.h = distance_interpolate(
|
||||
distance_normalized,
|
||||
property_animation->values.from.grect.size.h,
|
||||
property_animation->values.to.grect.size.h);
|
||||
((PropertyAnimationLegacy2Implementation*)
|
||||
property_animation->animation.implementation)
|
||||
->accessors.setter.grect(property_animation->subject, result);
|
||||
}
|
||||
|
||||
void property_animation_legacy2_init(
|
||||
PropertyAnimationLegacy2 *property_animation,
|
||||
const PropertyAnimationLegacy2Implementation *implementation,
|
||||
void *subject, void *from_value, void *to_value) {
|
||||
animation_legacy2_init(&property_animation->animation);
|
||||
memset(&property_animation->values, 0xff, sizeof(property_animation->values));
|
||||
property_animation->animation.implementation =
|
||||
(AnimationLegacy2Implementation*) implementation;
|
||||
property_animation->subject = subject;
|
||||
|
||||
// NOTE: we are also comparing against the 3.0 animation update handlers so that modules
|
||||
// like scroll_layer and menu_layer can use the legacy 2.0 animation when interfacing with a
|
||||
// 2.x app.
|
||||
if (property_animation->animation.implementation->update
|
||||
== (AnimationLegacy2UpdateImplementation) property_animation_legacy2_update_int16
|
||||
|| property_animation->animation.implementation->update
|
||||
== (AnimationLegacy2UpdateImplementation) property_animation_update_int16) {
|
||||
property_animation->values.to.int16 = to_value ? *((int16_t*)to_value)
|
||||
: implementation->accessors.getter.int16(subject);
|
||||
property_animation->values.from.int16 = from_value ? *((int16_t*)from_value)
|
||||
: implementation->accessors.getter.int16(subject);
|
||||
} else if (property_animation->animation.implementation->update
|
||||
== (AnimationLegacy2UpdateImplementation) property_animation_legacy2_update_gpoint
|
||||
|| property_animation->animation.implementation->update
|
||||
== (AnimationLegacy2UpdateImplementation) property_animation_update_gpoint) {
|
||||
property_animation->values.to.gpoint = to_value ? *((GPoint*)to_value)
|
||||
: implementation->accessors.getter.gpoint(subject);
|
||||
property_animation->values.from.gpoint =
|
||||
from_value ? *((GPoint*)from_value) : implementation->accessors.getter.gpoint(subject);
|
||||
} else if (property_animation->animation.implementation->update
|
||||
== (AnimationLegacy2UpdateImplementation) property_animation_legacy2_update_grect
|
||||
|| property_animation->animation.implementation->update
|
||||
== (AnimationLegacy2UpdateImplementation) property_animation_update_grect) {
|
||||
property_animation->values.to.grect = to_value ? *((GRect*)to_value)
|
||||
: implementation->accessors.getter.grect(subject);
|
||||
property_animation->values.from.grect = from_value ? *((GRect*)from_value)
|
||||
: implementation->accessors.getter.grect(subject);
|
||||
}
|
||||
}
|
||||
|
||||
struct PropertyAnimationLegacy2* property_animation_legacy2_create(
|
||||
const struct PropertyAnimationLegacy2Implementation *implementation,
|
||||
void *subject, void *from_value, void *to_value) {
|
||||
struct PropertyAnimationLegacy2* property_animation =
|
||||
task_malloc(sizeof(struct PropertyAnimationLegacy2));
|
||||
if (property_animation) {
|
||||
property_animation_legacy2_init(property_animation, implementation, subject,
|
||||
from_value, to_value);
|
||||
}
|
||||
return property_animation;
|
||||
}
|
||||
|
||||
void property_animation_legacy2_destroy(struct PropertyAnimationLegacy2* property_animation) {
|
||||
if (property_animation == NULL) {
|
||||
return;
|
||||
}
|
||||
animation_legacy2_unschedule(&property_animation->animation);
|
||||
task_free(property_animation);
|
||||
}
|
||||
|
||||
void property_animation_legacy2_init_layer_frame(
|
||||
PropertyAnimationLegacy2 *property_animation, struct Layer *layer,
|
||||
GRect *from_frame, GRect *to_frame) {
|
||||
static const PropertyAnimationLegacy2Implementation implementation = {
|
||||
.base = {
|
||||
.update = (AnimationLegacy2UpdateImplementation) property_animation_legacy2_update_grect,
|
||||
},
|
||||
.accessors = {
|
||||
.setter = { .grect = (const GRectSetter) layer_set_frame_by_value, },
|
||||
.getter = { .grect = (const GRectGetter) layer_get_frame_by_value, },
|
||||
},
|
||||
};
|
||||
property_animation_legacy2_init(property_animation, &implementation, layer,
|
||||
from_frame, to_frame);
|
||||
}
|
||||
|
||||
struct PropertyAnimationLegacy2* property_animation_legacy2_create_layer_frame(
|
||||
struct Layer *layer, GRect *from_frame, GRect *to_frame) {
|
||||
struct PropertyAnimationLegacy2* property_animation =
|
||||
task_malloc(sizeof(struct PropertyAnimationLegacy2));
|
||||
if (property_animation) {
|
||||
property_animation_legacy2_init_layer_frame(property_animation, layer, from_frame,
|
||||
to_frame);
|
||||
}
|
||||
return property_animation;
|
||||
}
|
||||
|
242
src/fw/applib/legacy2/ui/property_animation_legacy2.h
Normal file
242
src/fw/applib/legacy2/ui/property_animation_legacy2.h
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* 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 "animation_legacy2.h"
|
||||
#include "applib/ui/property_animation.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
//////////////////////
|
||||
// Legacy2 Property Animations
|
||||
//
|
||||
|
||||
//! @file property_animation_legacy2_legacy2.h
|
||||
|
||||
//! @addtogroup UI
|
||||
//! @{
|
||||
//! @addtogroup AnimationLegacy2
|
||||
//! @{
|
||||
//! @addtogroup PropertyAnimationLegacy2
|
||||
//! \brief Concrete animations to move a layer around over time
|
||||
//!
|
||||
//! Actually, property animations do more than just moving a Layer around over time.
|
||||
//! PropertyAnimationLegacy2 is a concrete class of animations and is built around the Animation
|
||||
//! subsystem, which covers anything timing related, but does not move anything around.
|
||||
//! A ProperyAnimation animates a "property" of a "subject".
|
||||
//!
|
||||
//! <h3>Animating a Layer's frame property</h3>
|
||||
//! Currently there is only one specific type of property animation offered off-the-shelf, namely
|
||||
//! one to change the frame (property) of a layer (subject), see
|
||||
//! \ref property_animation_legacy2_create_layer_frame().
|
||||
//!
|
||||
//! <h3>Implementing a custom PropertyAnimationLegacy2</h3>
|
||||
//! It is fairly simple to create your own variant of a PropertyAnimationLegacy2.
|
||||
//!
|
||||
//! Please refer to \htmlinclude UiFramework.html (chapter "Property Animations") for a conceptual
|
||||
//! overview
|
||||
//! of the animation framework and make sure you understand the underlying \ref Animation, in case
|
||||
//! you are not familiar with it, before trying to implement a variation on
|
||||
//! PropertyAnimationLegacy2.
|
||||
//!
|
||||
//! To implement a custom property animation, use \ref property_animation_legacy2_create() and
|
||||
//! provide a
|
||||
//! function pointers to the accessors (getter and setter) and setup, update and teardown callbacks
|
||||
//! in the implementation argument.
|
||||
//! Note that the type of property to animate with \ref PropertyAnimationLegacy2 is limited to
|
||||
//! int16_t, GPoint or GRect.
|
||||
//!
|
||||
//! For each of these types, there are implementations provided
|
||||
//! for the necessary `.update` handler of the animation: see
|
||||
//! \ref property_animation_legacy2_update_int16(), \ref property_animation_legacy2_update_gpoint()
|
||||
//! and \ref property_animation_legacy2_update_grect().
|
||||
//! These update functions expect the `.accessors` to conform to the following interface:
|
||||
//! Any getter needs to have the following function signature: `__type__ getter(void *subject);`
|
||||
//! Any setter needs to have to following function signature: `void setter(void *subject,
|
||||
//! __type__ value);`
|
||||
//! See \ref Int16Getter, \ref Int16Setter, \ref GPointGetter, \ref GPointSetter, \ref GRectGetter,
|
||||
//! \ref GRectSetter
|
||||
//! for the typedefs that accompany the update fuctions.
|
||||
//!
|
||||
//! \code{.c}
|
||||
//! static const PropertyAnimationLegacy2Implementation my_implementation = {
|
||||
//! .base = {
|
||||
//! // using the "stock" update callback:
|
||||
//! .update = (AnimationUpdateImplementation) property_animation_legacy2_update_gpoint,
|
||||
//! },
|
||||
//! .accessors = {
|
||||
//! // my accessors that get/set a GPoint from/onto my subject:
|
||||
//! .setter = { .gpoint = my_layer_set_corner_point, },
|
||||
//! .getter = { .gpoint = (const GPointGetter) my_layer_get_corner_point, },
|
||||
//! },
|
||||
//! };
|
||||
//! static PropertyAnimationLegacy2* s_my_animation_ptr = NULL;
|
||||
//! static GPoint s_to_point = GPointZero;
|
||||
//! ...
|
||||
//! // Use NULL as 'from' value, this will make the animation framework call the getter
|
||||
//! // to get the current value of the property and use that as the 'from' value:
|
||||
//! s_my_animation_ptr = property_animation_legacy2_create(&my_implementation, my_layer, NULL,
|
||||
//! &s_to_point);
|
||||
//! animation_schedule(s_my_animation_ptr->animation);
|
||||
//! \endcode
|
||||
//! @{
|
||||
|
||||
struct PropertyAnimationLegacy2;
|
||||
struct Layer;
|
||||
struct PropertyAnimationLegacy2Implementation;
|
||||
|
||||
//! @internal
|
||||
void property_animation_legacy2_init_layer_frame(
|
||||
struct PropertyAnimationLegacy2 *property_animation, struct Layer *layer, GRect *from_frame,
|
||||
GRect *to_frame);
|
||||
|
||||
//! Convenience function to create and initialize a property animation that animates the frame of a
|
||||
//! Layer. It sets up the PropertyAnimationLegacy2 to use \ref layer_set_frame() and
|
||||
//! \ref layer_get_frame() as accessors and uses the `layer` parameter as the subject for the
|
||||
//! animation. The same defaults are used as with \ref animation_create().
|
||||
//! @param layer the layer that will be animated
|
||||
//! @param from_frame the frame that the layer should animate from
|
||||
//! @param to_frame the frame that the layer should animate to
|
||||
//! @note Pass in `NULL` as one of the frame arguments to have it set automatically to the layer's
|
||||
//! current frame. This will result in a call to \ref layer_get_frame() to get the current frame of
|
||||
//! the layer.
|
||||
//! @return A pointer to the property animation. `NULL` if animation could not
|
||||
//! be created
|
||||
struct PropertyAnimationLegacy2* property_animation_legacy2_create_layer_frame(struct Layer *layer,
|
||||
GRect *from_frame, GRect *to_frame);
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Implementing custom Property Animations
|
||||
//
|
||||
|
||||
//! @internal
|
||||
//! See \ref property_animation_legacy2_create() for a description of the parameter list
|
||||
void property_animation_legacy2_init(struct PropertyAnimationLegacy2 *property_animation,
|
||||
const struct PropertyAnimationLegacy2Implementation *implementation, void *subject,
|
||||
void *from_value, void *to_value);
|
||||
|
||||
//! Creates a new PropertyAnimationLegacy2 on the heap and and initializes it with the specified
|
||||
//! values. The same defaults are used as with \ref animation_create().
|
||||
//! If the `from_value` or the `to_value` is `NULL`, the getter accessor will be called to get the
|
||||
//! current value of the property and be used instead.
|
||||
//! @param implementation Pointer to the implementation of the animation. In most cases, it makes
|
||||
//! sense to pass in a `static const` struct pointer.
|
||||
//! @param subject Pointer to the "subject" being animated. This will be passed in when the
|
||||
//! getter/setter accessors are called,
|
||||
//! see \ref PropertyAnimationLegacy2Accessors, \ref GPointSetter, and friends. The value of this
|
||||
//! pointer will be copied into the `.subject` field of the PropertyAnimationLegacy2 struct.
|
||||
//! @param from_value Pointer to the value that the subject should animate from
|
||||
//! @param to_value Pointer to the value that the subject should animate to
|
||||
//! @note Pass in `NULL` as one of the value arguments to have it set automatically to the
|
||||
//! subject's current property value, as returned by the getter function. Also note that passing in
|
||||
//! `NULL` for both `from_value` and `to_value`, will result in the animation having the same from-
|
||||
//! and to- values, effectively not doing anything.
|
||||
//! @return A pointer to the property animation. `NULL` if animation could not
|
||||
//! be created
|
||||
struct PropertyAnimationLegacy2* property_animation_legacy2_create(
|
||||
const struct PropertyAnimationLegacy2Implementation *implementation, void *subject,
|
||||
void *from_value, void *to_value);
|
||||
|
||||
//! Free a dynamically allocated property animation
|
||||
//! @param property_animation The property animation to be freed.
|
||||
void property_animation_legacy2_destroy(struct PropertyAnimationLegacy2* property_animation);
|
||||
|
||||
//! Default update callback for a property animations to update a property of type int16_t.
|
||||
//! Assign this function to the `.base.update` callback field of your
|
||||
//! PropertyAnimationLegacy2Implementation,
|
||||
//! in combination with a `.getter` and `.setter` accessors of types \ref Int16Getter and
|
||||
//! \ref Int16Setter.
|
||||
//! The implementation of this function will calculate the next value of the animation and call the
|
||||
//! setter to set the new value upon the subject.
|
||||
//! @param property_animation The property animation for which the update is requested.
|
||||
//! @param distance_normalized The current normalized distance. See
|
||||
//! \ref AnimationUpdateImplementation
|
||||
//! @note This function is not supposed to be called "manually", but will be called automatically
|
||||
//! when the animation is being run.
|
||||
void property_animation_legacy2_update_int16(struct PropertyAnimationLegacy2 *property_animation,
|
||||
const uint32_t distance_normalized);
|
||||
|
||||
//! Default update callback for a property animations to update a property of type GPoint.
|
||||
//! Assign this function to the `.base.update` callback field of your
|
||||
//! PropertyAnimationLegacy2Implementation, in combination with a `.getter` and `.setter` accessors
|
||||
//! of types \ref GPointGetter and \ref GPointSetter.
|
||||
//! The implementation of this function will calculate the next point of the animation and call the
|
||||
//! setter to set the new point upon the subject.
|
||||
//! @param property_animation The property animation for which the update is requested.
|
||||
//! @param distance_normalized The current normalized distance. See
|
||||
//! \ref AnimationUpdateImplementation
|
||||
//! @note This function is not supposed to be called "manually", but will be called automatically
|
||||
//! when the animation is being run.
|
||||
void property_animation_legacy2_update_gpoint(struct PropertyAnimationLegacy2 *property_animation,
|
||||
const uint32_t distance_normalized);
|
||||
|
||||
//! Default update callback for a property animations to update a property of type GRect.
|
||||
//! Assign this function to the `.base.update` callback field of your
|
||||
//! PropertyAnimationLegacy2Implementation,
|
||||
//! in combination with a `.getter` and `.setter` accessors of types \ref GRectGetter and
|
||||
//! \ref GRectSetter.
|
||||
//! The implementation of this function will calculate the next rectangle of the animation and call
|
||||
//! the setter to set the new rectangle upon the subject.
|
||||
//! @param property_animation The property animation for which the update is requested.
|
||||
//! @param distance_normalized The current normalized distance. See
|
||||
//! \ref AnimationUpdateImplementation
|
||||
//! @note This function is not supposed to be called "manually", but will be called automatically
|
||||
//! when the animation is being run.
|
||||
void property_animation_legacy2_update_grect(struct PropertyAnimationLegacy2 *property_animation,
|
||||
const uint32_t distance_normalized);
|
||||
|
||||
//! Data structure containing a collection of function pointers that form the implementation of the
|
||||
//! property animation.
|
||||
//! See the code example at the top (\ref PropertyAnimationLegacy2).
|
||||
typedef struct PropertyAnimationLegacy2Implementation {
|
||||
//! The "inherited" fields from the Animation "base class".
|
||||
AnimationLegacy2Implementation base;
|
||||
//! The accessors to set/get the property to be animated.
|
||||
PropertyAnimationAccessors accessors;
|
||||
} PropertyAnimationLegacy2Implementation;
|
||||
|
||||
//! The data structure of a property animation that contains all its state.
|
||||
typedef struct PropertyAnimationLegacy2 {
|
||||
//! The "inherited" state from the "base class", \ref Animation.
|
||||
AnimationLegacy2 animation;
|
||||
//! The values of the property that the animation should animated from and to.
|
||||
struct {
|
||||
//! The value of the property that the animation should animate to.
|
||||
//! When the animation completes, this value will be the final value that is set.
|
||||
union {
|
||||
//! Valid when the property being animated is of type GRect
|
||||
GRect grect;
|
||||
//! Valid when the property being animated is of type GPoint
|
||||
GPoint gpoint;
|
||||
//! Valid when the property being animated is of type int16_t
|
||||
int16_t int16;
|
||||
} to;
|
||||
//! The value of the property that the animation should animate to.
|
||||
//! When the animation starts, this value will be the initial value that is set.
|
||||
union {
|
||||
//! Valid when the property being animated is of type GRect
|
||||
GRect grect;
|
||||
//! Valid when the property being animated is of type GPoint
|
||||
GPoint gpoint;
|
||||
//! Valid when the property being animated is of type int16_t
|
||||
int16_t int16;
|
||||
} from;
|
||||
} values; //!< See detail table
|
||||
void *subject; //!< The subject of the animation of which the property should be animated.
|
||||
} PropertyAnimationLegacy2;
|
||||
|
||||
//! @} // group PropertyAnimationLegacy2
|
||||
//! @} // group Animation
|
||||
//! @} // group UI
|
28
src/fw/applib/legacy2/ui/status_bar_legacy2.h
Normal file
28
src/fw/applib/legacy2/ui/status_bar_legacy2.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
//! @file status_bar_legacy2.h
|
||||
//!
|
||||
//! This file implements a 2.x status bar for backwards compatibility with apps compiled with old
|
||||
//! firmwares.
|
||||
|
||||
#define STATUS_BAR_HEIGHT 16
|
||||
|
||||
#define STATUS_BAR_FRAME GRect(0, 0, DISP_COLS, STATUS_BAR_HEIGHT)
|
217
src/fw/applib/legacy2/ui/text_layer_legacy2.c
Normal file
217
src/fw/applib/legacy2/ui/text_layer_legacy2.c
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "text_layer_legacy2.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/fonts/fonts.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static GTextLayoutCacheRef prv_text_layer_legacy2_get_cache_handle(TextLayerLegacy2 *text_layer) {
|
||||
if (text_layer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return text_layer->should_cache_layout ? text_layer->layout_cache : NULL;
|
||||
}
|
||||
|
||||
void text_layer_legacy2_update_proc(TextLayerLegacy2 *text_layer, GContext* ctx) {
|
||||
if (text_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
const GColor bg_color = get_native_color(text_layer->background_color);
|
||||
if (!(gcolor_equal(bg_color, GColorClear))) {
|
||||
graphics_context_set_fill_color(ctx, bg_color);
|
||||
graphics_fill_rect(ctx, &text_layer->layer.bounds);
|
||||
}
|
||||
if (text_layer->text && strlen(text_layer->text) > 0) {
|
||||
graphics_context_set_text_color(ctx, get_native_color(text_layer->text_color));
|
||||
graphics_draw_text(ctx, text_layer->text, text_layer->font, text_layer->layer.bounds,
|
||||
text_layer->overflow_mode, text_layer->text_alignment,
|
||||
prv_text_layer_legacy2_get_cache_handle(text_layer));
|
||||
}
|
||||
}
|
||||
|
||||
void text_layer_legacy2_init(TextLayerLegacy2 *text_layer, const GRect *frame) {
|
||||
PBL_ASSERTN(text_layer);
|
||||
*text_layer = (TextLayerLegacy2){};
|
||||
text_layer->layer.frame = *frame;
|
||||
text_layer->layer.bounds = (GRect){{0, 0}, frame->size};
|
||||
text_layer->layer.update_proc = (LayerUpdateProc)text_layer_legacy2_update_proc;
|
||||
text_layer->text_color = GColor2Black;
|
||||
text_layer->background_color = GColor2White;
|
||||
text_layer->overflow_mode = GTextOverflowModeTrailingEllipsis;
|
||||
layer_set_clips(&text_layer->layer, true);
|
||||
|
||||
text_layer->text_alignment = GTextAlignmentLeft;
|
||||
text_layer->font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD);
|
||||
|
||||
layer_mark_dirty(&(text_layer->layer));
|
||||
}
|
||||
|
||||
TextLayerLegacy2* text_layer_legacy2_create(GRect frame) {
|
||||
TextLayerLegacy2* layer = task_malloc(sizeof(TextLayerLegacy2));
|
||||
if (layer) {
|
||||
text_layer_legacy2_init(layer, &frame);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
void text_layer_legacy2_destroy(TextLayerLegacy2* text_layer) {
|
||||
if (!text_layer) {
|
||||
return;
|
||||
}
|
||||
text_layer_legacy2_deinit(text_layer);
|
||||
task_free(text_layer);
|
||||
}
|
||||
|
||||
void text_layer_legacy2_deinit(TextLayerLegacy2 *text_layer) {
|
||||
if (text_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
layer_deinit(&text_layer->layer);
|
||||
graphics_text_layout_cache_deinit(&text_layer->layout_cache);
|
||||
text_layer->layout_cache = NULL;
|
||||
}
|
||||
|
||||
Layer* text_layer_legacy2_get_layer(TextLayerLegacy2 *text_layer) {
|
||||
if (text_layer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return &text_layer->layer;
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_size(TextLayerLegacy2 *text_layer, const GSize max_size) {
|
||||
if (text_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
layer_set_frame(&text_layer->layer, &(GRect) { text_layer->layer.frame.origin, max_size });
|
||||
layer_mark_dirty(&text_layer->layer);
|
||||
}
|
||||
|
||||
GSize text_layer_legacy2_get_size(TextLayerLegacy2* text_layer) {
|
||||
if (text_layer == NULL) {
|
||||
return GSizeZero;
|
||||
}
|
||||
return text_layer->layer.frame.size;
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_text(TextLayerLegacy2 *text_layer, const char *text) {
|
||||
if (text_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
text_layer->text = text;
|
||||
layer_mark_dirty(&text_layer->layer);
|
||||
}
|
||||
|
||||
const char* text_layer_legacy2_get_text(TextLayerLegacy2 *text_layer) {
|
||||
if (text_layer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return text_layer->text;
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_background_color_2bit(TextLayerLegacy2 *text_layer, GColor2 color) {
|
||||
if (text_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
GColor native_color = get_native_color(color);
|
||||
const GColor bg_color = get_native_color(text_layer->background_color);
|
||||
if (gcolor_equal(native_color, bg_color)) {
|
||||
return;
|
||||
}
|
||||
text_layer->background_color = get_closest_gcolor2(native_color);
|
||||
layer_mark_dirty(&(text_layer->layer));
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_text_color_2bit(TextLayerLegacy2 *text_layer, GColor2 color) {
|
||||
if (text_layer == NULL) {
|
||||
return;
|
||||
}
|
||||
GColor8 native_color = get_native_color(color);
|
||||
const GColor text_color = get_native_color(text_layer->text_color);
|
||||
if (gcolor_equal(native_color, text_color)) {
|
||||
return;
|
||||
}
|
||||
text_layer->text_color = get_closest_gcolor2(native_color);
|
||||
layer_mark_dirty(&(text_layer->layer));
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_text_alignment(TextLayerLegacy2 *text_layer,
|
||||
GTextAlignment text_alignment) {
|
||||
if (text_layer == NULL || text_alignment == text_layer->text_alignment) {
|
||||
return;
|
||||
}
|
||||
text_layer->text_alignment = text_alignment;
|
||||
layer_mark_dirty(&(text_layer->layer));
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_overflow_mode(TextLayerLegacy2 *text_layer,
|
||||
GTextOverflowMode overflow_mode) {
|
||||
if (text_layer == NULL || overflow_mode == text_layer->overflow_mode) {
|
||||
return;
|
||||
}
|
||||
text_layer->overflow_mode = overflow_mode;
|
||||
layer_mark_dirty(&(text_layer->layer));
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_font(TextLayerLegacy2 *text_layer, GFont font) {
|
||||
if (text_layer == NULL || font == text_layer->font) {
|
||||
return;
|
||||
}
|
||||
text_layer->font = font;
|
||||
layer_mark_dirty(&(text_layer->layer));
|
||||
}
|
||||
|
||||
void text_layer_legacy2_set_should_cache_layout(TextLayerLegacy2 *text_layer,
|
||||
bool should_cache_layout) {
|
||||
if (text_layer == NULL || should_cache_layout == text_layer->should_cache_layout) {
|
||||
return;
|
||||
}
|
||||
|
||||
text_layer->should_cache_layout = should_cache_layout;
|
||||
|
||||
if (text_layer->should_cache_layout) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Init layout");
|
||||
graphics_text_layout_cache_init(&text_layer->layout_cache);
|
||||
} else {
|
||||
graphics_text_layout_cache_deinit(&text_layer->layout_cache);
|
||||
text_layer->layout_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GSize text_layer_legacy2_get_content_size(GContext* ctx, TextLayerLegacy2 *text_layer) {
|
||||
if (text_layer == NULL) {
|
||||
return GSizeZero;
|
||||
} else if (!text_layer->should_cache_layout) {
|
||||
text_layer_legacy2_set_should_cache_layout(text_layer, true);
|
||||
}
|
||||
GTextLayoutCacheRef layout = prv_text_layer_legacy2_get_cache_handle(text_layer);
|
||||
PBL_ASSERTN(layout);
|
||||
return graphics_text_layout_get_max_used_size(ctx, text_layer->text, text_layer->font,
|
||||
text_layer->layer.bounds, text_layer->overflow_mode, text_layer->text_alignment, layout);
|
||||
}
|
||||
|
||||
GSize app_text_layer_legacy2_get_content_size(TextLayerLegacy2 *text_layer) {
|
||||
GContext* ctx = app_state_get_graphics_context();
|
||||
return text_layer_legacy2_get_content_size(ctx, text_layer);
|
||||
}
|
195
src/fw/applib/legacy2/ui/text_layer_legacy2.h
Normal file
195
src/fw/applib/legacy2/ui/text_layer_legacy2.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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/ui/layer.h"
|
||||
#include "applib/graphics/text.h"
|
||||
|
||||
//! @file text_layer_legacy2.h
|
||||
//! @addtogroup UI
|
||||
//! @{
|
||||
//! @addtogroup Layer Layers
|
||||
//! @{
|
||||
//! @addtogroup TextLayerLegacy2
|
||||
//! \brief Layer that displays and formats a text string.
|
||||
//!
|
||||
//! 
|
||||
//! The geometric information (bounds, frame) of the Layer
|
||||
//! is used as the "box" in which the text is drawn. The \ref TextLayerLegacy2 also has a number of
|
||||
//! other properties that influence how the text is drawn. Most important of these properties are:
|
||||
//! a pointer to the string to draw itself, the font, the text color, the background color of the
|
||||
//! layer, the overflow mode and alignment of the text inside the layer.
|
||||
//! @see Layer
|
||||
//! @see TextDrawing
|
||||
//! @see Fonts
|
||||
//! @{
|
||||
|
||||
//! The data structure of a TextLayerLegacy2.
|
||||
//! @note a `TextLayerLegacy2 *` can safely be casted to a `Layer *` and can thus be used
|
||||
//! with all other functions that take a `Layer *` as an argument.
|
||||
//! <br/>For example, the following is legal:
|
||||
//! \code{.c}
|
||||
//! TextLayerLegacy2 text_layer;
|
||||
//! ...
|
||||
//! layer_set_hidden((Layer *)&text_layer, true);
|
||||
//! \endcode
|
||||
typedef struct TextLayerLegacy2 {
|
||||
Layer layer;
|
||||
const char* text;
|
||||
GFont font;
|
||||
GTextLayoutCacheRef layout_cache;
|
||||
GColor2 text_color:2;
|
||||
GColor2 background_color:2;
|
||||
GTextOverflowMode overflow_mode:2;
|
||||
GTextAlignment text_alignment:2;
|
||||
bool should_cache_layout:1;
|
||||
} TextLayerLegacy2;
|
||||
|
||||
//! Initializes the TextLayerLegacy2 with given frame
|
||||
//! All previous contents are erased and the following default values are set:
|
||||
//! * Font: Raster Gothic 14-point Boldface (system font)
|
||||
//! * Text Alignment: \ref GTextAlignmentLeft
|
||||
//! * Text color: \ref GColorBlack
|
||||
//! * Background color: \ref GColorWhite
|
||||
//! * Clips: `true`
|
||||
//! * Hidden: `false`
|
||||
//! * Caching: `false`
|
||||
//!
|
||||
//! The text layer is automatically marked dirty after this operation.
|
||||
//! @param text_layer The TextLayerLegacy2 to initialize
|
||||
//! @param frame The frame with which to initialze the TextLayerLegacy2
|
||||
void text_layer_legacy2_init(TextLayerLegacy2 *text_layer, const GRect *frame);
|
||||
|
||||
//! Creates a new TextLayerLegacy2 on the heap and initializes it with the default values.
|
||||
//!
|
||||
//! * Font: Raster Gothic 14-point Boldface (system font)
|
||||
//! * Text Alignment: \ref GTextAlignmentLeft
|
||||
//! * Text color: \ref GColorBlack
|
||||
//! * Background color: \ref GColorWhite
|
||||
//! * Clips: `true`
|
||||
//! * Hidden: `false`
|
||||
//! * Caching: `false`
|
||||
//!
|
||||
//! The text layer is automatically marked dirty after this operation.
|
||||
//! @param frame The frame with which to initialze the TextLayerLegacy2
|
||||
//! @return A pointer to the TextLayerLegacy2. `NULL` if the TextLayerLegacy2 could not
|
||||
//! be created
|
||||
TextLayerLegacy2* text_layer_legacy2_create(GRect frame);
|
||||
|
||||
//! Destroys a TextLayerLegacy2 previously created by text_layer_legacy2_create.
|
||||
void text_layer_legacy2_destroy(TextLayerLegacy2* text_layer);
|
||||
|
||||
//! Deinitializes the TextLayerLegacy2 and frees any caches.
|
||||
//! @param text_layer The TextLayerLegacy2 to deinitialize
|
||||
//! @internal
|
||||
//! @see text_layer_legacy2_set_should_cache_layout
|
||||
//! @note This MUST be called after discarding the text layer when using layout caching.
|
||||
void text_layer_legacy2_deinit(TextLayerLegacy2 *text_layer);
|
||||
|
||||
//! Gets the "root" Layer of the text layer, which is the parent for the sub-
|
||||
//! layers used for its implementation.
|
||||
//! @param text_layer Pointer to the TextLayerLegacy2 for which to get the "root" Layer
|
||||
//! @return The "root" Layer of the text layer.
|
||||
//! @internal
|
||||
//! @note The result is always equal to `(Layer *) text_layer`.
|
||||
Layer* text_layer_legacy2_get_layer(TextLayerLegacy2 *text_layer);
|
||||
|
||||
//! Sets the pointer to the string where the TextLayerLegacy2 is supposed to find the text
|
||||
//! at a later point in time, when it needs to draw itself.
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the text
|
||||
//! @param text The new text to set onto the TextLayerLegacy2. This must be a null-terminated and
|
||||
//! valid UTF-8 string!
|
||||
//! @note The string is not copied, so its buffer most likely cannot be stack allocated,
|
||||
//! but is recommended to be a buffer that is long-lived, at least as long as the TextLayerLegacy2
|
||||
//! is part of a visible Layer hierarchy.
|
||||
//! @see text_layer_legacy2_get_text
|
||||
void text_layer_legacy2_set_text(TextLayerLegacy2 *text_layer, const char *text);
|
||||
|
||||
//! Gets the pointer to the string that the TextLayerLegacy2 is using.
|
||||
//! @param text_layer The TextLayerLegacy2 for which to get the text
|
||||
//! @see text_layer_legacy2_set_text
|
||||
const char* text_layer_legacy2_get_text(TextLayerLegacy2 *text_layer);
|
||||
|
||||
//! Sets the background color of bounding box that will be drawn behind the text
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the background color
|
||||
//! @param color The new \ref GColor to set the background to
|
||||
//! @see text_layer_legacy2_set_text_color
|
||||
void text_layer_legacy2_set_background_color_2bit(TextLayerLegacy2 *text_layer, GColor2 color);
|
||||
|
||||
//! Sets the color of text that will be drawn
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the text color
|
||||
//! @param color The new \ref GColor to set the text color to
|
||||
//! @see text_layer_legacy2_set_background_color
|
||||
void text_layer_legacy2_set_text_color_2bit(TextLayerLegacy2 *text_layer, GColor2 color);
|
||||
|
||||
//! Sets the line break mode of the TextLayerLegacy2
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the overflow mode
|
||||
//! @param line_mode The new \ref GTextOverflowMode to set
|
||||
void text_layer_legacy2_set_overflow_mode(TextLayerLegacy2 *text_layer,
|
||||
GTextOverflowMode line_mode);
|
||||
|
||||
//! Sets the font of the TextLayerLegacy2
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the font
|
||||
//! @param font The new \ref GFont for the TextLayerLegacy2
|
||||
//! @see fonts_get_system_font
|
||||
//! @see fonts_load_custom_font
|
||||
void text_layer_legacy2_set_font(TextLayerLegacy2 *text_layer, GFont font);
|
||||
|
||||
//! Sets the alignment of the TextLayerLegacy2
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the alignment
|
||||
//! @param text_alignment The new text alignment for the TextLayerLegacy2
|
||||
//! @see GTextAlignment
|
||||
void text_layer_legacy2_set_text_alignment(TextLayerLegacy2 *text_layer,
|
||||
GTextAlignment text_alignment);
|
||||
|
||||
//! @internal
|
||||
//! Sets whether or not the text layer should cache text layout information.
|
||||
//! By default, layout caching is off (false). Layout caches store the max used
|
||||
//! height and width of a text layer.
|
||||
//! NOTE: when using cached layouts, text_layer_legacy2_deinit() MUST be called at some
|
||||
//! point in time to prevent memory leaks from occuring.
|
||||
void text_layer_legacy2_set_should_cache_layout(TextLayerLegacy2 *text_layer,
|
||||
bool should_cache_layout);
|
||||
|
||||
//! @internal
|
||||
//! Calculates the size occupied by the current text of the TextLayerLegacy2
|
||||
//! @param ctx the current graphics context
|
||||
//! @param text_layer the TextLayerLegacy2 for which to calculate the text's size
|
||||
//! @return The size occupied by the current text of the TextLayerLegacy2. If the text
|
||||
//! string is not valid UTF-8 or NULL, the size returned will be (0,0).
|
||||
//! @note Because of an implementation detail, it is necessary to pass in the current graphics
|
||||
//! context, even though this function does not draw anything.
|
||||
//! @internal
|
||||
//! @see \ref app_get_current_graphics_context()
|
||||
GSize text_layer_legacy2_get_content_size(GContext* ctx, TextLayerLegacy2 *text_layer);
|
||||
|
||||
//! Calculates the size occupied by the current text of the TextLayerLegacy2
|
||||
//! @param text_layer the TextLayerLegacy2 for which to calculate the text's size
|
||||
//! @return The size occupied by the current text of the TextLayerLegacy2
|
||||
GSize app_text_layer_legacy2_get_content_size(TextLayerLegacy2 *text_layer);
|
||||
|
||||
//! Update the size of the text layer
|
||||
//! This is a convenience function to update the frame of the TextLayerLegacy2.
|
||||
//! @param text_layer The TextLayerLegacy2 of which to set the size
|
||||
//! @param max_size The new size for the TextLayerLegacy2
|
||||
void text_layer_legacy2_set_size(TextLayerLegacy2 *text_layer, const GSize max_size);
|
||||
|
||||
GSize text_layer_legacy2_get_size(TextLayerLegacy2* text_layer);
|
||||
|
||||
//! @} // end addtogroup TextLayerLegacy2
|
||||
//! @} // end addtogroup Layer
|
||||
//! @} // end addtogroup UI
|
Loading…
Add table
Add a link
Reference in a new issue