<commit_before>#include "simply_window.h"

#include "simply_msg.h"
#include "simply_res.h"
#include "simply_menu.h"
#include "simply_window_stack.h"
#include "simply_voice.h"

#include "simply.h"

#include "util/graphics.h"
#include "util/scroll_layer.h"
#include "util/status_bar_layer.h"
#include "util/string.h"

#include <pebble.h>

typedef struct WindowPropsPacket WindowPropsPacket;

struct __attribute__((__packed__)) WindowPropsPacket {
  Packet packet;
  uint32_t id;
  GColor8 background_color;
  bool scrollable;
};

typedef struct WindowButtonConfigPacket WindowButtonConfigPacket;

struct __attribute__((__packed__)) WindowButtonConfigPacket {
  Packet packet;
  uint8_t button_mask;
};

typedef struct WindowStatusBarPacket WindowStatusBarPacket;

struct __attribute__((__packed__)) WindowStatusBarPacket {
  Packet packet;
  GColor8 background_color;
  GColor8 color;
  StatusBarLayerSeparatorMode separator:8;
  bool status;
};

typedef struct WindowActionBarPacket WindowActionBarPacket;

struct __attribute__((__packed__)) WindowActionBarPacket {
  Packet packet;
  uint32_t image[3];
  GColor8 background_color;
  bool action;
};

typedef struct ClickPacket ClickPacket;

struct __attribute__((__packed__)) ClickPacket {
  Packet packet;
  ButtonId button:8;
};

typedef ClickPacket LongClickPacket;


static GColor8 s_button_palette[] = { { GColorWhiteARGB8 }, { GColorClearARGB8 } };


static void prv_update_layer_placement(SimplyWindow *self, GRect *frame_out);
static void click_config_provider(void *data);

static bool prv_send_click(SimplyMsg *self, Command type, ButtonId button) {
  ClickPacket packet = {
    .packet.type = type,
    .packet.length = sizeof(packet),
    .button = button,
  };
  return simply_msg_send_packet(&packet.packet);
}

static bool prv_send_single_click(SimplyMsg *self, ButtonId button) {
  return prv_send_click(self, CommandClick, button);
}

static bool prv_send_long_click(SimplyMsg *self, ButtonId button) {
  return prv_send_click(self, CommandLongClick, button);
}

static void prv_set_scroll_layer_click_config(SimplyWindow *self) {
  if (self->scroll_layer) {
    scroll_layer_set_click_config_provider_onto_window(
        self->scroll_layer, click_config_provider, self->window, self);
  }
}

void simply_window_set_scrollable(SimplyWindow *self, bool is_scrollable, bool animated,
                                  bool reset) {
  if (!self->use_scroll_layer || (self->is_scrollable == is_scrollable && !reset)) { return; }

  self->is_scrollable = is_scrollable;

  prv_set_scroll_layer_click_config(self);

  if (!is_scrollable || reset) {
    GRect frame = GRectZero;
    prv_update_layer_placement(self, &frame);
    // TODO: Remove `animated = true` once set content offset always unschedules the animation
    const bool animated = true;
    scroll_layer_set_content_offset(self->scroll_layer, GPointZero, animated);
    scroll_layer_set_content_size(self->scroll_layer, frame.size);
  }


  if (self->layer) {
    layer_mark_dirty(self->layer);
  }
}

static void prv_update_layer_placement(SimplyWindow *self, GRect *frame_out) {
  Layer * const main_layer = self->layer ?: scroll_layer_get_layer(self->scroll_layer);
  if (!main_layer) { return; }

  GRect frame = { .size = layer_get_frame(window_get_root_layer(self->window)).size };

  if (self->status_bar_layer) {
    Layer * const status_bar_base_layer = status_bar_layer_get_layer(self->status_bar_layer);
    const bool has_status_bar = (layer_get_window(status_bar_base_layer) != NULL);
    const bool has_action_bar =
        (layer_get_window(action_bar_layer_get_layer(self->action_bar_layer)) != NULL);
    if (has_status_bar) {
      GRect status_frame = { .size = { frame.size.w, STATUS_BAR_LAYER_HEIGHT } };
      frame.origin.y = STATUS_BAR_LAYER_HEIGHT;
      frame.size.h -=
          PBL_IF_ROUND_ELSE(self->status_bar_insets_bottom, false) ? STATUS_BAR_LAYER_HEIGHT * 2 :
                                                                     STATUS_BAR_LAYER_HEIGHT;
      if (PBL_IF_RECT_ELSE(has_action_bar, false)) {
        status_frame.size.w -= ACTION_BAR_WIDTH;
      }
      layer_set_frame(status_bar_base_layer, status_frame);
    }
  }

  layer_set_frame(main_layer, frame);
  if (frame_out) {
    *frame_out = frame;
  }
}


void simply_window_set_status_bar(SimplyWindow *self, bool use_status_bar) {
  self->use_status_bar = use_status_bar;

  status_bar_layer_remove_from_window(self->window, self->status_bar_layer);

  if (use_status_bar) {
    status_bar_layer_add_to_window(self->window, self->status_bar_layer);
  }

  prv_update_layer_placement(self, NULL);

#ifdef PBL_SDK_2
  if (!window_stack_contains_window(self->window)) { return; }

  // HACK: Refresh app chrome state
  uint32_t id = self->id;
  self->id = 0;
  Window *window = window_create();
  window_stack_push(window, false);
  window_stack_remove(window, false);
  window_destroy(window);
  self->id = id;
#endif
}

void simply_window_set_background_color(SimplyWindow *self, GColor8 background_color) {
  self->background_color = background_color;
  window_set_background_color(self->window, gcolor8_get_or(background_color, GColorBlack));
  if (self->status_bar_layer) {
    status_bar_layer_set_colors(self->status_bar_layer, background_color,
                               gcolor_legible_over(background_color));
  }
}

void simply_window_set_status_bar_colors(SimplyWindow *self, GColor8 background_color,
                                         GColor8 foreground_color) {
  if (self->status_bar_layer) {
    status_bar_layer_set_colors(self->status_bar_layer, background_color, foreground_color);
  }
}

void simply_window_set_status_bar_separator_mode(SimplyWindow *self,
                                                 StatusBarLayerSeparatorMode separator) {
  if (self->status_bar_layer) {
    status_bar_layer_set_separator_mode(self->status_bar_layer, separator);
  }
}

void simply_window_set_action_bar(SimplyWindow *self, bool use_action_bar) {
  self->use_action_bar = use_action_bar;

  if (!self->action_bar_layer) { return; }<commit_msg>Remove window and status background color linking logic<commit_after>#include "simply_window.h"

#include "simply_msg.h"
#include "simply_res.h"
#include "simply_menu.h"
#include "simply_window_stack.h"
#include "simply_voice.h"

#include "simply.h"

#include "util/graphics.h"
#include "util/scroll_layer.h"
#include "util/status_bar_layer.h"
#include "util/string.h"

#include <pebble.h>

typedef struct WindowPropsPacket WindowPropsPacket;

struct __attribute__((__packed__)) WindowPropsPacket {
  Packet packet;
  uint32_t id;
  GColor8 background_color;
  bool scrollable;
};

typedef struct WindowButtonConfigPacket WindowButtonConfigPacket;

struct __attribute__((__packed__)) WindowButtonConfigPacket {
  Packet packet;
  uint8_t button_mask;
};

typedef struct WindowStatusBarPacket WindowStatusBarPacket;

struct __attribute__((__packed__)) WindowStatusBarPacket {
  Packet packet;
  GColor8 background_color;
  GColor8 color;
  StatusBarLayerSeparatorMode separator:8;
  bool status;
};

typedef struct WindowActionBarPacket WindowActionBarPacket;

struct __attribute__((__packed__)) WindowActionBarPacket {
  Packet packet;
  uint32_t image[3];
  GColor8 background_color;
  bool action;
};

typedef struct ClickPacket ClickPacket;

struct __attribute__((__packed__)) ClickPacket {
  Packet packet;
  ButtonId button:8;
};

typedef ClickPacket LongClickPacket;


static GColor8 s_button_palette[] = { { GColorWhiteARGB8 }, { GColorClearARGB8 } };


static void prv_update_layer_placement(SimplyWindow *self, GRect *frame_out);
static void click_config_provider(void *data);

static bool prv_send_click(SimplyMsg *self, Command type, ButtonId button) {
  ClickPacket packet = {
    .packet.type = type,
    .packet.length = sizeof(packet),
    .button = button,
  };
  return simply_msg_send_packet(&packet.packet);
}

static bool prv_send_single_click(SimplyMsg *self, ButtonId button) {
  return prv_send_click(self, CommandClick, button);
}

static bool prv_send_long_click(SimplyMsg *self, ButtonId button) {
  return prv_send_click(self, CommandLongClick, button);
}

static void prv_set_scroll_layer_click_config(SimplyWindow *self) {
  if (self->scroll_layer) {
    scroll_layer_set_click_config_provider_onto_window(
        self->scroll_layer, click_config_provider, self->window, self);
  }
}

void simply_window_set_scrollable(SimplyWindow *self, bool is_scrollable, bool animated,
                                  bool reset) {
  if (!self->use_scroll_layer || (self->is_scrollable == is_scrollable && !reset)) { return; }

  self->is_scrollable = is_scrollable;

  prv_set_scroll_layer_click_config(self);

  if (!is_scrollable || reset) {
    GRect frame = GRectZero;
    prv_update_layer_placement(self, &frame);
    // TODO: Remove `animated = true` once set content offset always unschedules the animation
    const bool animated = true;
    scroll_layer_set_content_offset(self->scroll_layer, GPointZero, animated);
    scroll_layer_set_content_size(self->scroll_layer, frame.size);
  }


  if (self->layer) {
    layer_mark_dirty(self->layer);
  }
}

static void prv_update_layer_placement(SimplyWindow *self, GRect *frame_out) {
  Layer * const main_layer = self->layer ?: scroll_layer_get_layer(self->scroll_layer);
  if (!main_layer) { return; }

  GRect frame = { .size = layer_get_frame(window_get_root_layer(self->window)).size };

  if (self->status_bar_layer) {
    Layer * const status_bar_base_layer = status_bar_layer_get_layer(self->status_bar_layer);
    const bool has_status_bar = (layer_get_window(status_bar_base_layer) != NULL);
    const bool has_action_bar =
        (layer_get_window(action_bar_layer_get_layer(self->action_bar_layer)) != NULL);
    if (has_status_bar) {
      GRect status_frame = { .size = { frame.size.w, STATUS_BAR_LAYER_HEIGHT } };
      frame.origin.y = STATUS_BAR_LAYER_HEIGHT;
      frame.size.h -=
          PBL_IF_ROUND_ELSE(self->status_bar_insets_bottom, false) ? STATUS_BAR_LAYER_HEIGHT * 2 :
                                                                     STATUS_BAR_LAYER_HEIGHT;
      if (PBL_IF_RECT_ELSE(has_action_bar, false)) {
        status_frame.size.w -= ACTION_BAR_WIDTH;
      }
      layer_set_frame(status_bar_base_layer, status_frame);
    }
  }

  layer_set_frame(main_layer, frame);
  if (frame_out) {
    *frame_out = frame;
  }
}


void simply_window_set_status_bar(SimplyWindow *self, bool use_status_bar) {
  self->use_status_bar = use_status_bar;

  status_bar_layer_remove_from_window(self->window, self->status_bar_layer);

  if (use_status_bar) {
    status_bar_layer_add_to_window(self->window, self->status_bar_layer);
  }

  prv_update_layer_placement(self, NULL);

#ifdef PBL_SDK_2
  if (!window_stack_contains_window(self->window)) { return; }

  // HACK: Refresh app chrome state
  uint32_t id = self->id;
  self->id = 0;
  Window *window = window_create();
  window_stack_push(window, false);
  window_stack_remove(window, false);
  window_destroy(window);
  self->id = id;
#endif
}

void simply_window_set_background_color(SimplyWindow *self, GColor8 background_color) {
  self->background_color = background_color;
  window_set_background_color(self->window, gcolor8_get_or(background_color, GColorBlack));
}

void simply_window_set_status_bar_colors(SimplyWindow *self, GColor8 background_color,
                                         GColor8 foreground_color) {
  if (self->status_bar_layer) {
    status_bar_layer_set_colors(self->status_bar_layer, background_color, foreground_color);
  }
}

void simply_window_set_status_bar_separator_mode(SimplyWindow *self,
                                                 StatusBarLayerSeparatorMode separator) {
  if (self->status_bar_layer) {
    status_bar_layer_set_separator_mode(self->status_bar_layer, separator);
  }
}

void simply_window_set_action_bar(SimplyWindow *self, bool use_action_bar) {
  self->use_action_bar = use_action_bar;

  if (!self->action_bar_layer) { return; }
{'fraction_non_alphanumeric': 0.09602456635405428, 'fraction_numerical': 0.0039837330898829775, 'mean_word_length': 4.578703703703703, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2330709', 'n_tokens_mistral': 4028, 'n_tokens_neox': 3936, 'n_words': 720}