/* * 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 "applib/ui/scroll_layer.h" #include "clar.h" // Stubs //////////////////////////////////// #include "stubs_app_state.h" #include "stubs_compiled_with_legacy2_sdk.h" #include "stubs_content_indicator.h" #include "stubs_graphics_context.h" #include "stubs_heap.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_pebble_tasks.h" #include "stubs_resources.h" #include "stubs_syscalls.h" #include "stubs_unobstructed_area.h" #define DEFAULT_SCROLL_HEIGHT 32 // Stubs //////////////////////////////////// static GRect s_graphics_draw_bitmap_in_rect__rect = GRectZero; void graphics_draw_bitmap_in_rect(GContext* ctx, const GBitmap *src_bitmap, const GRect *rect) { s_graphics_draw_bitmap_in_rect__rect = *rect; } bool graphics_release_frame_buffer(GContext *ctx, GBitmap *buffer) {return false;} void window_schedule_render(struct Window *window) {} void window_set_click_config_provider_with_context( struct Window *window, ClickConfigProvider click_config_provider, void *context) {} void window_set_click_context(ButtonId button_id, void *context) {} void window_single_repeating_click_subscribe( ButtonId button_id, uint16_t repeat_interval_ms, ClickHandler handler) {} // Internal definitions //////////////////////////////////// extern bool prv_scroll_layer_is_paging_enabled(ScrollLayer *scroll_layer); extern void prv_scroll_layer_set_content_offset_internal(ScrollLayer *scroll_layer, GPoint offset); extern uint16_t prv_scroll_layer_get_paging_height(ScrollLayer *scroll_layer); // Setup //////////////////////////////////// void test_scroll_layer__initialize(void) {} void test_scroll_layer__cleanup(void) {} // Tests //////////////////////////////////// void test_scroll_layer__enable_paging(void) { GRect scroll_bounds = GRect(0,0,180,180); ScrollLayer *scroll_layer = scroll_layer_create(scroll_bounds); // Verify paging is disabled by default cl_assert_equal_b(false, prv_scroll_layer_is_paging_enabled(scroll_layer)); // Verify shadow_layer is not hidden when paging disabled cl_assert_equal_b(false, scroll_layer_get_shadow_hidden(scroll_layer)); scroll_layer_set_paging(scroll_layer, true); // Verify paging is enabled cl_assert_equal_b(true, prv_scroll_layer_is_paging_enabled(scroll_layer)); // Verify shadow_layer is hidden now that paging enabled cl_assert_equal_b(true, scroll_layer_get_shadow_hidden(scroll_layer)); // verify disable paging works scroll_layer_set_paging(scroll_layer, false); cl_assert_equal_b(false, prv_scroll_layer_is_paging_enabled(scroll_layer)); // verify shadow layer is hidden on paging disabled cl_assert_equal_b(true, scroll_layer_get_shadow_hidden(scroll_layer)); } void test_scroll_layer__paging_vs_shadow_bits(void) { ScrollLayer *scroll_layer = scroll_layer_create(GRect(0,0,180,180)); // Validate that paging_disabled is same position as shadow clips scroll_layer->shadow_sublayer.clips = true; cl_assert_equal_b(true, scroll_layer->paging.paging_disabled); cl_assert_equal_b(false, scroll_layer->paging.shadow_hidden); scroll_layer->shadow_sublayer.clips = false; cl_assert_equal_b(false, scroll_layer->paging.paging_disabled); cl_assert_equal_b(false, scroll_layer->paging.shadow_hidden); // Validate that shadow_hidden is same position as layer hidden in shadow sublayer scroll_layer->shadow_sublayer.hidden = true; cl_assert_equal_b(false, scroll_layer->paging.paging_disabled); cl_assert_equal_b(true, scroll_layer->paging.shadow_hidden); scroll_layer->shadow_sublayer.hidden = false; cl_assert_equal_b(false, scroll_layer->paging.paging_disabled); cl_assert_equal_b(false, scroll_layer->paging.shadow_hidden); } void test_scroll_layer__scrolling(void) { GRect scroll_bounds = GRect(0,0,180,180); ScrollLayer *scroll_layer = scroll_layer_create(scroll_bounds); GSize content_size = GSize(180, 2000); scroll_layer_set_content_size(scroll_layer, content_size); int32_t scroll_height = DEFAULT_SCROLL_HEIGHT; int32_t offset = 0; for (offset = 0; offset < content_size.h - scroll_bounds.size.h; offset += scroll_height) { // scroll offset for scroll down is negative, so invert offset.y cl_assert_equal_i(offset, -((int32_t)scroll_layer_get_content_offset(scroll_layer).y)); scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); } // can only scroll to content offset == content_size.h - bounds.size.h // so the last scroll from the above loop is expected to have stopped short cl_assert(offset > -((int32_t)scroll_layer_get_content_offset(scroll_layer).y)); } void test_scroll_layer__paging_with_scroll(void) { ScrollLayer *scroll_layer = scroll_layer_create(GRect(0,0,180,180)); int16_t page_height = 0; page_height = scroll_layer->layer.frame.size.h; scroll_layer_set_paging(scroll_layer, true); cl_assert_equal_i(page_height, prv_scroll_layer_get_paging_height(scroll_layer)); // paging should force < page_height offsets to ceil of modulo page height scroll_layer_set_content_size(scroll_layer, GSize(180, 2000)); scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); // scroll offset for scroll down is negative, so invert offset.y cl_assert_equal_i(page_height, -((int32_t)scroll_layer_get_content_offset(scroll_layer).y)); } void test_scroll_layer__paging_last_pages_content(void) { uint16_t page_height = 86; ScrollLayer *scroll_layer = scroll_layer_create(GRect(0,0,180,page_height)); // validate enable paging works for paging height scroll_layer_set_paging(scroll_layer, true); cl_assert_equal_i(page_height, prv_scroll_layer_get_paging_height(scroll_layer)); int pages = 2; int offset = 0; // setup content size to be slightly more than 2 pages GSize content_size = GSize(180, page_height * pages + 10); scroll_layer_set_content_size(scroll_layer, content_size); // paging should force full contents of last page to show // so content size rounded up to the next modulo of page_height cl_assert_equal_i(offset, -scroll_layer_get_content_offset(scroll_layer).y); scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); offset += page_height; cl_assert_equal_i(offset, -scroll_layer_get_content_offset(scroll_layer).y); // we expect to scroll to the end of content padded to the last full page scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); offset += page_height; cl_assert_equal_i(offset, -scroll_layer_get_content_offset(scroll_layer).y); cl_assert_equal_i(page_height * pages, -scroll_layer_get_content_offset(scroll_layer).y); // once the last full page of content has been displayed // another scroll down shouldn't advance the offset scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); cl_assert_equal_i(page_height * pages, -scroll_layer_get_content_offset(scroll_layer).y); } void test_scroll_layer__fullscreen_paging(void) { GRect scroll_bounds = GRect(0,0,180,180); ScrollLayer *scroll_layer = scroll_layer_create(scroll_bounds); int16_t page_height = scroll_bounds.size.h; scroll_layer_set_paging(scroll_layer, true); cl_assert_equal_i(page_height, prv_scroll_layer_get_paging_height(scroll_layer)); int pages = 22; int offset = 0; // setup content size to be slightly more than the pages GSize content_size = GSize(scroll_bounds.size.w, page_height * pages + 24); scroll_layer_set_content_size(scroll_layer, content_size); // paging should force full contents of last page to show // so content size rounded up to the next modulo of page_height cl_assert_equal_i(offset, -scroll_layer_get_content_offset(scroll_layer).y); // we expect to scroll to the end of content padded to the last full page for (int i = 0; i < pages; i++) { scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); offset += page_height; cl_assert_equal_i(offset, -scroll_layer_get_content_offset(scroll_layer).y); } // once the last full page of content has been displayed // another scroll down shouldn't advance the offset scroll_layer_scroll(scroll_layer, ScrollDirectionDown, false); cl_assert_equal_i(page_height * pages, -scroll_layer_get_content_offset(scroll_layer).y); }