diff --git a/components/smart_display/runtime_tiles.h b/components/smart_display/runtime_tiles.h index b761479..169d288 100644 --- a/components/smart_display/runtime_tiles.h +++ b/components/smart_display/runtime_tiles.h @@ -99,7 +99,7 @@ inline void refresh_detail(unsigned index); inline const char *weather_icon(const std::string &condition); inline const char *weather_text(const std::string &condition); inline std::string timer_text(const Tile &t); -inline std::string last_run_text(uint32_t epoch); +inline std::string last_run_text(uint32_t epoch, bool compact = false); // "07:12": a time of day as Home Assistant and ESP Screens send it, for screen_text::clock_text. inline std::string hhmm(const esphome::ESPTime &time) { char b[8]; snprintf(b, sizeof(b), "%02d:%02d", time.hour, time.minute); @@ -1204,6 +1204,31 @@ inline lv_obj_t *detail_shape(lv_obj_t *parent,int x,int y,int w,int h,theme::Ro inline int text_width(const std::string &text,const lv_font_t *font){ lv_point_t size;lv_text_get_size(&size,text.c_str(),font,0,0,LV_COORD_MAX,LV_TEXT_FLAG_NONE);return size.x; } +// The status line under a tile's name, once its room is known. LVGL cuts the end of a line that doesn't fit, which +// is where this line carries its news, so (firmware 0.2.76+): +// `shorter` another wording of the whole line, taken when the full one doesn't fit ("Yest. 9:15 PM"); +// `tail` the end that has to stay readable (a robot's battery): the words in front of it take the dots, +// "Angedockt / 10 %" becomes "Angedo… / 10 %". +// A line that fits, or a room too small for the tail itself, is left to LVGL as before. +inline void fit_value(lv_obj_t *obj,const std::string &value,const std::string &shorter,const std::string &tail,int room){ + const lv_font_t *font=lv_obj_get_style_text_font(obj,LV_PART_MAIN); + if(!font||room<=0||text_width(value,font)<=room)return; + if(!shorter.empty()&&shorter!=value&&text_width(shorter,font)<=room){label(obj,shorter);return;} + if(tail.empty()||value.size()<=tail.size())return; + static const std::string dots="…"; + const int head_room=room-text_width(dots+tail,font); + if(head_room<=0)return; + const std::string head=value.substr(0,value.size()-tail.size()); + std::string cut; + for(size_t i=0;i(head[next])&0xC0)==0x80)++next; + if(text_width(head.substr(0,next),font)>head_room)break; + cut=head.substr(0,next);i=next; + } + while(!cut.empty()&&cut.back()==' ')cut.pop_back(); + label(obj,cut+dots+tail); +} // The state colour, as Home Assistant colours a vacuum: teal while cleaning, blue on the way back, amber // when paused, red on an error; a docked or idle robot keeps the card's own blue. `tint` is the halo. struct VacuumLook { uint32_t accent, tint; }; @@ -2187,16 +2212,18 @@ inline std::string countdown(uint32_t seconds) { return b; } // "Last 14:32" today, "Yesterday 14:32", else "Last 13 Sep", in the screen's language; scripts and scenes have no -// useful on/off. +// useful on/off. `compact` takes the short wording of yesterday a tile has room for ("Yest. 9:15 PM"); every +// language that needs no shorter word keeps the same text there. inline std::string month_short(const esphome::ESPTime &now); -inline std::string last_run_text(uint32_t epoch) { +inline std::string last_run_text(uint32_t epoch, bool compact) { if (!epoch) return tr(txt::script_never_run); auto when = esphome::ESPTime::from_epoch_local(epoch); auto now = now_time ? now_time() : esphome::ESPTime{}; if (!when.is_valid()) return tr(txt::script_never_run); std::string clock = screen_text::clock_text(hhmm(when), screen_settings::current.clock_24h != 0); if (now.is_valid() && now.year == when.year && now.day_of_year == when.day_of_year) return fill(txt::script_last_time, "time", clock); - if (now.is_valid() && now.year == when.year && now.day_of_year == when.day_of_year + 1) return fill(txt::script_yesterday_time, "time", clock); + if (now.is_valid() && now.year == when.year && now.day_of_year == when.day_of_year + 1) + return fill(compact ? txt::script_yesterday_time_short : txt::script_yesterday_time, "time", clock); std::string date = fill(fill(txt::date_day_month, "day", std::to_string(when.day_of_month)), "month", month_short(when)); return fill(txt::script_last_date, "date", date); } @@ -3168,10 +3195,14 @@ inline void render_slot(size_t slot) { else if (!t.unit.empty() || d == "number" || d == "input_number" || d == "counter") value = screen_text::localize(value); bool pending=t.loading(esphome::millis()); if(d=="weather" && std::isfinite(t.current)) {value=screen_text::decimal(t.current,1);if(!watch)value=screen_text::with_unit(value,t.unit);} - if(d=="vacuum" && std::isfinite(t.battery))value += " / "+screen_text::percent((int)t.battery); + // What a narrow tile falls back to once its room is known (fit_value): another wording of the whole line, and the + // end that must stay readable whatever happens to the words in front of it. + std::string value_short,value_tail; + if((d=="script"||d=="scene"||d=="button"||d=="input_button") && t.state!="on")value_short=last_run_text(t.last_run,true); + if(d=="vacuum" && std::isfinite(t.battery)){value_tail=" / "+screen_text::percent((int)t.battery);value+=value_tail;} // Direct controls: only a wide card in the standard layout has room for the panel. bool with_panel=w.wide && !t.controls.empty() && !t.builtin() && !watch && t.inline_control!="slider" && fresh() && t.available(); - if(with_panel){std::string status=tile_controls::status_text(t);if(!status.empty())value=status;} + if(with_panel){std::string status=tile_controls::status_text(t);if(!status.empty()){value=status;value_short.clear();value_tail.clear();}} label(w.value, value); bool mini=t.inline_control=="slider" && !watch && t.available(); bool large_tile=tile_height(w)>80; @@ -3239,7 +3270,9 @@ inline void render_slot(size_t slot) { int chevron_w=t.is_page() && !watch?lv_font_get_line_height(chevron_font):0; if(chevron_w)text_room-=chevron_w+(large_tile?8:4); lv_obj_set_width(w.title,std::max(1,text_room-text_x)); - lv_obj_set_width(w.value,std::max(1,text_room-(watch?0:(mini||graph_strip)?text_x:w.value_x))); + int value_room=std::max(1,text_room-(watch?0:(mini||graph_strip)?text_x:w.value_x)); + lv_obj_set_width(w.value,value_room); + fit_value(w.value,value,value_short,value_tail,value_room); if(watch){ int gap=large_tile?6:2,header=std::max(circle_size,title_height); int group_y=std::max(0,(content_h-header-gap-value_height)/2); diff --git a/components/smart_display/screen_text_keys.h b/components/smart_display/screen_text_keys.h index 88395fe..a882d7b 100644 --- a/components/smart_display/screen_text_keys.h +++ b/components/smart_display/screen_text_keys.h @@ -144,144 +144,145 @@ constexpr uint16_t script_running = 179; constexpr uint16_t script_never_run = 180; constexpr uint16_t script_last_time = 181; constexpr uint16_t script_yesterday_time = 182; -constexpr uint16_t script_last_date = 183; -constexpr uint16_t camera_no_image = 184; -constexpr uint16_t camera_loading = 185; -constexpr uint16_t camera_live = 186; -constexpr uint16_t camera_recording = 187; -constexpr uint16_t camera_tap_to_view = 188; -constexpr uint16_t camera_no_image_yet = 189; -constexpr uint16_t calibration_title = 190; -constexpr uint16_t calibration_touch_check = 191; -constexpr uint16_t calibration_hold = 192; -constexpr uint16_t calibration_crosshair = 193; -constexpr uint16_t calibration_gently = 194; -constexpr uint16_t calibration_same = 195; -constexpr uint16_t calibration_rejected = 196; -constexpr uint16_t calibration_save_failed = 197; -constexpr uint16_t ha_on = 198; -constexpr uint16_t ha_off = 199; -constexpr uint16_t ha_unavailable = 200; -constexpr uint16_t ha_climate_off = 201; -constexpr uint16_t ha_climate_heat = 202; -constexpr uint16_t ha_climate_cool = 203; -constexpr uint16_t ha_climate_heat_cool = 204; -constexpr uint16_t ha_climate_auto = 205; -constexpr uint16_t ha_climate_dry = 206; -constexpr uint16_t ha_climate_fan_only = 207; -constexpr uint16_t ha_hvac_action_heating = 208; -constexpr uint16_t ha_hvac_action_cooling = 209; -constexpr uint16_t ha_hvac_action_idle = 210; -constexpr uint16_t ha_hvac_action_off = 211; -constexpr uint16_t ha_hvac_action_drying = 212; -constexpr uint16_t ha_hvac_action_fan = 213; -constexpr uint16_t ha_hvac_action_preheating = 214; -constexpr uint16_t ha_hvac_action_defrosting = 215; -constexpr uint16_t ha_cover_open = 216; -constexpr uint16_t ha_cover_closed = 217; -constexpr uint16_t ha_cover_opening = 218; -constexpr uint16_t ha_cover_closing = 219; -constexpr uint16_t ha_media_playing = 220; -constexpr uint16_t ha_media_paused = 221; -constexpr uint16_t ha_media_idle = 222; -constexpr uint16_t ha_media_standby = 223; -constexpr uint16_t ha_person_home = 224; -constexpr uint16_t ha_person_not_home = 225; -constexpr uint16_t ha_sun_above_horizon = 226; -constexpr uint16_t ha_sun_below_horizon = 227; -constexpr uint16_t ha_vacuum_docked = 228; -constexpr uint16_t ha_vacuum_cleaning = 229; -constexpr uint16_t ha_vacuum_paused = 230; -constexpr uint16_t ha_vacuum_returning = 231; -constexpr uint16_t ha_vacuum_idle = 232; -constexpr uint16_t ha_weather_sunny = 233; -constexpr uint16_t ha_weather_clear_night = 234; -constexpr uint16_t ha_weather_cloudy = 235; -constexpr uint16_t ha_weather_partlycloudy = 236; -constexpr uint16_t ha_weather_rainy = 237; -constexpr uint16_t ha_weather_pouring = 238; -constexpr uint16_t ha_weather_snowy = 239; -constexpr uint16_t ha_weather_snowy_rainy = 240; -constexpr uint16_t ha_weather_fog = 241; -constexpr uint16_t ha_weather_hail = 242; -constexpr uint16_t ha_weather_lightning = 243; -constexpr uint16_t ha_weather_lightning_rainy = 244; -constexpr uint16_t ha_weather_windy = 245; -constexpr uint16_t ha_weather_exceptional = 246; -constexpr uint16_t ha_binary_battery_on = 247; -constexpr uint16_t ha_binary_battery_off = 248; -constexpr uint16_t ha_binary_battery_charging_on = 249; -constexpr uint16_t ha_binary_battery_charging_off = 250; -constexpr uint16_t ha_binary_carbon_monoxide_on = 251; -constexpr uint16_t ha_binary_carbon_monoxide_off = 252; -constexpr uint16_t ha_binary_cold_on = 253; -constexpr uint16_t ha_binary_cold_off = 254; -constexpr uint16_t ha_binary_connectivity_on = 255; -constexpr uint16_t ha_binary_connectivity_off = 256; -constexpr uint16_t ha_binary_door_on = 257; -constexpr uint16_t ha_binary_door_off = 258; -constexpr uint16_t ha_binary_garage_door_on = 259; -constexpr uint16_t ha_binary_garage_door_off = 260; -constexpr uint16_t ha_binary_gas_on = 261; -constexpr uint16_t ha_binary_gas_off = 262; -constexpr uint16_t ha_binary_heat_on = 263; -constexpr uint16_t ha_binary_heat_off = 264; -constexpr uint16_t ha_binary_light_on = 265; -constexpr uint16_t ha_binary_light_off = 266; -constexpr uint16_t ha_binary_lock_on = 267; -constexpr uint16_t ha_binary_lock_off = 268; -constexpr uint16_t ha_binary_moisture_on = 269; -constexpr uint16_t ha_binary_moisture_off = 270; -constexpr uint16_t ha_binary_motion_on = 271; -constexpr uint16_t ha_binary_motion_off = 272; -constexpr uint16_t ha_binary_moving_on = 273; -constexpr uint16_t ha_binary_moving_off = 274; -constexpr uint16_t ha_binary_occupancy_on = 275; -constexpr uint16_t ha_binary_occupancy_off = 276; -constexpr uint16_t ha_binary_opening_on = 277; -constexpr uint16_t ha_binary_opening_off = 278; -constexpr uint16_t ha_binary_plug_on = 279; -constexpr uint16_t ha_binary_plug_off = 280; -constexpr uint16_t ha_binary_power_on = 281; -constexpr uint16_t ha_binary_power_off = 282; -constexpr uint16_t ha_binary_presence_on = 283; -constexpr uint16_t ha_binary_presence_off = 284; -constexpr uint16_t ha_binary_problem_on = 285; -constexpr uint16_t ha_binary_problem_off = 286; -constexpr uint16_t ha_binary_running_on = 287; -constexpr uint16_t ha_binary_running_off = 288; -constexpr uint16_t ha_binary_safety_on = 289; -constexpr uint16_t ha_binary_safety_off = 290; -constexpr uint16_t ha_binary_smoke_on = 291; -constexpr uint16_t ha_binary_smoke_off = 292; -constexpr uint16_t ha_binary_sound_on = 293; -constexpr uint16_t ha_binary_sound_off = 294; -constexpr uint16_t ha_binary_tamper_on = 295; -constexpr uint16_t ha_binary_tamper_off = 296; -constexpr uint16_t ha_binary_update_on = 297; -constexpr uint16_t ha_binary_update_off = 298; -constexpr uint16_t ha_binary_vibration_on = 299; -constexpr uint16_t ha_binary_vibration_off = 300; -constexpr uint16_t ha_binary_window_on = 301; -constexpr uint16_t ha_binary_window_off = 302; -constexpr uint16_t ha_button_activate = 303; -constexpr uint16_t ha_button_run = 304; -constexpr uint16_t ha_button_press = 305; -constexpr uint16_t ha_climate_fan_auto = 306; -constexpr uint16_t ha_climate_fan_low = 307; -constexpr uint16_t ha_climate_fan_medium = 308; -constexpr uint16_t ha_climate_fan_high = 309; -constexpr uint16_t ha_climate_fan_middle = 310; -constexpr uint16_t ha_climate_fan_focus = 311; -constexpr uint16_t ha_climate_fan_diffuse = 312; -constexpr uint16_t ha_climate_fan_top = 313; -constexpr uint16_t ha_climate_fan_on = 314; -constexpr uint16_t ha_climate_fan_off = 315; -constexpr uint16_t ha_climate_swing_on = 316; -constexpr uint16_t ha_climate_swing_off = 317; -constexpr uint16_t ha_climate_swing_both = 318; -constexpr uint16_t ha_climate_swing_vertical = 319; -constexpr uint16_t ha_climate_swing_horizontal = 320; +constexpr uint16_t script_yesterday_time_short = 183; +constexpr uint16_t script_last_date = 184; +constexpr uint16_t camera_no_image = 185; +constexpr uint16_t camera_loading = 186; +constexpr uint16_t camera_live = 187; +constexpr uint16_t camera_recording = 188; +constexpr uint16_t camera_tap_to_view = 189; +constexpr uint16_t camera_no_image_yet = 190; +constexpr uint16_t calibration_title = 191; +constexpr uint16_t calibration_touch_check = 192; +constexpr uint16_t calibration_hold = 193; +constexpr uint16_t calibration_crosshair = 194; +constexpr uint16_t calibration_gently = 195; +constexpr uint16_t calibration_same = 196; +constexpr uint16_t calibration_rejected = 197; +constexpr uint16_t calibration_save_failed = 198; +constexpr uint16_t ha_on = 199; +constexpr uint16_t ha_off = 200; +constexpr uint16_t ha_unavailable = 201; +constexpr uint16_t ha_climate_off = 202; +constexpr uint16_t ha_climate_heat = 203; +constexpr uint16_t ha_climate_cool = 204; +constexpr uint16_t ha_climate_heat_cool = 205; +constexpr uint16_t ha_climate_auto = 206; +constexpr uint16_t ha_climate_dry = 207; +constexpr uint16_t ha_climate_fan_only = 208; +constexpr uint16_t ha_hvac_action_heating = 209; +constexpr uint16_t ha_hvac_action_cooling = 210; +constexpr uint16_t ha_hvac_action_idle = 211; +constexpr uint16_t ha_hvac_action_off = 212; +constexpr uint16_t ha_hvac_action_drying = 213; +constexpr uint16_t ha_hvac_action_fan = 214; +constexpr uint16_t ha_hvac_action_preheating = 215; +constexpr uint16_t ha_hvac_action_defrosting = 216; +constexpr uint16_t ha_cover_open = 217; +constexpr uint16_t ha_cover_closed = 218; +constexpr uint16_t ha_cover_opening = 219; +constexpr uint16_t ha_cover_closing = 220; +constexpr uint16_t ha_media_playing = 221; +constexpr uint16_t ha_media_paused = 222; +constexpr uint16_t ha_media_idle = 223; +constexpr uint16_t ha_media_standby = 224; +constexpr uint16_t ha_person_home = 225; +constexpr uint16_t ha_person_not_home = 226; +constexpr uint16_t ha_sun_above_horizon = 227; +constexpr uint16_t ha_sun_below_horizon = 228; +constexpr uint16_t ha_vacuum_docked = 229; +constexpr uint16_t ha_vacuum_cleaning = 230; +constexpr uint16_t ha_vacuum_paused = 231; +constexpr uint16_t ha_vacuum_returning = 232; +constexpr uint16_t ha_vacuum_idle = 233; +constexpr uint16_t ha_weather_sunny = 234; +constexpr uint16_t ha_weather_clear_night = 235; +constexpr uint16_t ha_weather_cloudy = 236; +constexpr uint16_t ha_weather_partlycloudy = 237; +constexpr uint16_t ha_weather_rainy = 238; +constexpr uint16_t ha_weather_pouring = 239; +constexpr uint16_t ha_weather_snowy = 240; +constexpr uint16_t ha_weather_snowy_rainy = 241; +constexpr uint16_t ha_weather_fog = 242; +constexpr uint16_t ha_weather_hail = 243; +constexpr uint16_t ha_weather_lightning = 244; +constexpr uint16_t ha_weather_lightning_rainy = 245; +constexpr uint16_t ha_weather_windy = 246; +constexpr uint16_t ha_weather_exceptional = 247; +constexpr uint16_t ha_binary_battery_on = 248; +constexpr uint16_t ha_binary_battery_off = 249; +constexpr uint16_t ha_binary_battery_charging_on = 250; +constexpr uint16_t ha_binary_battery_charging_off = 251; +constexpr uint16_t ha_binary_carbon_monoxide_on = 252; +constexpr uint16_t ha_binary_carbon_monoxide_off = 253; +constexpr uint16_t ha_binary_cold_on = 254; +constexpr uint16_t ha_binary_cold_off = 255; +constexpr uint16_t ha_binary_connectivity_on = 256; +constexpr uint16_t ha_binary_connectivity_off = 257; +constexpr uint16_t ha_binary_door_on = 258; +constexpr uint16_t ha_binary_door_off = 259; +constexpr uint16_t ha_binary_garage_door_on = 260; +constexpr uint16_t ha_binary_garage_door_off = 261; +constexpr uint16_t ha_binary_gas_on = 262; +constexpr uint16_t ha_binary_gas_off = 263; +constexpr uint16_t ha_binary_heat_on = 264; +constexpr uint16_t ha_binary_heat_off = 265; +constexpr uint16_t ha_binary_light_on = 266; +constexpr uint16_t ha_binary_light_off = 267; +constexpr uint16_t ha_binary_lock_on = 268; +constexpr uint16_t ha_binary_lock_off = 269; +constexpr uint16_t ha_binary_moisture_on = 270; +constexpr uint16_t ha_binary_moisture_off = 271; +constexpr uint16_t ha_binary_motion_on = 272; +constexpr uint16_t ha_binary_motion_off = 273; +constexpr uint16_t ha_binary_moving_on = 274; +constexpr uint16_t ha_binary_moving_off = 275; +constexpr uint16_t ha_binary_occupancy_on = 276; +constexpr uint16_t ha_binary_occupancy_off = 277; +constexpr uint16_t ha_binary_opening_on = 278; +constexpr uint16_t ha_binary_opening_off = 279; +constexpr uint16_t ha_binary_plug_on = 280; +constexpr uint16_t ha_binary_plug_off = 281; +constexpr uint16_t ha_binary_power_on = 282; +constexpr uint16_t ha_binary_power_off = 283; +constexpr uint16_t ha_binary_presence_on = 284; +constexpr uint16_t ha_binary_presence_off = 285; +constexpr uint16_t ha_binary_problem_on = 286; +constexpr uint16_t ha_binary_problem_off = 287; +constexpr uint16_t ha_binary_running_on = 288; +constexpr uint16_t ha_binary_running_off = 289; +constexpr uint16_t ha_binary_safety_on = 290; +constexpr uint16_t ha_binary_safety_off = 291; +constexpr uint16_t ha_binary_smoke_on = 292; +constexpr uint16_t ha_binary_smoke_off = 293; +constexpr uint16_t ha_binary_sound_on = 294; +constexpr uint16_t ha_binary_sound_off = 295; +constexpr uint16_t ha_binary_tamper_on = 296; +constexpr uint16_t ha_binary_tamper_off = 297; +constexpr uint16_t ha_binary_update_on = 298; +constexpr uint16_t ha_binary_update_off = 299; +constexpr uint16_t ha_binary_vibration_on = 300; +constexpr uint16_t ha_binary_vibration_off = 301; +constexpr uint16_t ha_binary_window_on = 302; +constexpr uint16_t ha_binary_window_off = 303; +constexpr uint16_t ha_button_activate = 304; +constexpr uint16_t ha_button_run = 305; +constexpr uint16_t ha_button_press = 306; +constexpr uint16_t ha_climate_fan_auto = 307; +constexpr uint16_t ha_climate_fan_low = 308; +constexpr uint16_t ha_climate_fan_medium = 309; +constexpr uint16_t ha_climate_fan_high = 310; +constexpr uint16_t ha_climate_fan_middle = 311; +constexpr uint16_t ha_climate_fan_focus = 312; +constexpr uint16_t ha_climate_fan_diffuse = 313; +constexpr uint16_t ha_climate_fan_top = 314; +constexpr uint16_t ha_climate_fan_on = 315; +constexpr uint16_t ha_climate_fan_off = 316; +constexpr uint16_t ha_climate_swing_on = 317; +constexpr uint16_t ha_climate_swing_off = 318; +constexpr uint16_t ha_climate_swing_both = 319; +constexpr uint16_t ha_climate_swing_vertical = 320; +constexpr uint16_t ha_climate_swing_horizontal = 321; constexpr uint16_t date_weekdays = 18; constexpr uint16_t date_weekdays_count = 7; constexpr uint16_t date_weekdays_short = 25; @@ -293,6 +294,6 @@ constexpr uint16_t date_months_count = 12; constexpr uint16_t date_months_short = 51; constexpr uint16_t date_months_short_count = 12; } // namespace txt -constexpr uint16_t KEY_COUNT = 321; -constexpr uint32_t KEYS_HASH = 0x8FB53847u; +constexpr uint16_t KEY_COUNT = 322; +constexpr uint32_t KEYS_HASH = 0x6D3912FFu; } // namespace screen_text diff --git a/screen_manager/translations/de.json b/screen_manager/translations/de.json index a35d522..7031623 100644 --- a/screen_manager/translations/de.json +++ b/screen_manager/translations/de.json @@ -238,6 +238,7 @@ "never_run": "Nie ausgeführt", "last_time": "Zuletzt {time}", "yesterday_time": "Gestern {time}", + "yesterday_time_short": "Gestern {time}", "last_date": "Zuletzt {date}" }, "camera": { diff --git a/screen_manager/translations/en.json b/screen_manager/translations/en.json index 561ffc4..8c8e6b5 100644 --- a/screen_manager/translations/en.json +++ b/screen_manager/translations/en.json @@ -238,6 +238,7 @@ "never_run": "Never run", "last_time": "Last {time}", "yesterday_time": "Yesterday {time}", + "yesterday_time_short": "Yest. {time}", "last_date": "Last {date}" }, "camera": { diff --git a/screen_manager/translations/es.json b/screen_manager/translations/es.json index 2652bd2..7c16d5d 100644 --- a/screen_manager/translations/es.json +++ b/screen_manager/translations/es.json @@ -238,6 +238,7 @@ "never_run": "Nunca ejecutado", "last_time": "Última {time}", "yesterday_time": "Ayer {time}", + "yesterday_time_short": "Ayer {time}", "last_date": "Última {date}" }, "camera": { diff --git a/screen_manager/translations/fr.json b/screen_manager/translations/fr.json index 8e74665..e867b2a 100644 --- a/screen_manager/translations/fr.json +++ b/screen_manager/translations/fr.json @@ -238,6 +238,7 @@ "never_run": "Jamais lancé", "last_time": "Dernier {time}", "yesterday_time": "Hier {time}", + "yesterday_time_short": "Hier {time}", "last_date": "Dernier {date}" }, "camera": { diff --git a/screen_manager/translations/it.json b/screen_manager/translations/it.json index 0a5623a..ddd5617 100644 --- a/screen_manager/translations/it.json +++ b/screen_manager/translations/it.json @@ -238,6 +238,7 @@ "never_run": "Mai eseguito", "last_time": "Ultima {time}", "yesterday_time": "Ieri {time}", + "yesterday_time_short": "Ieri {time}", "last_date": "Ultima {date}" }, "camera": { diff --git a/screen_manager/translations/nl.json b/screen_manager/translations/nl.json index b501146..51e4453 100644 --- a/screen_manager/translations/nl.json +++ b/screen_manager/translations/nl.json @@ -238,6 +238,7 @@ "never_run": "Nooit uitgevoerd", "last_time": "Laatst {time}", "yesterday_time": "Gisteren {time}", + "yesterday_time_short": "Gisteren {time}", "last_date": "Laatst {date}" }, "camera": { diff --git a/screen_manager/translations/pl.json b/screen_manager/translations/pl.json index 8a6efe5..7ce46e6 100644 --- a/screen_manager/translations/pl.json +++ b/screen_manager/translations/pl.json @@ -238,6 +238,7 @@ "never_run": "Nie uruchamiano", "last_time": "Ostatnio {time}", "yesterday_time": "Wczoraj {time}", + "yesterday_time_short": "Wczoraj {time}", "last_date": "Ostatnio {date}" }, "camera": { diff --git a/screen_manager/translations/pt.json b/screen_manager/translations/pt.json index 66b91df..297a885 100644 --- a/screen_manager/translations/pt.json +++ b/screen_manager/translations/pt.json @@ -238,6 +238,7 @@ "never_run": "Nunca executado", "last_time": "Última {time}", "yesterday_time": "Ontem {time}", + "yesterday_time_short": "Ontem {time}", "last_date": "Última {date}" }, "camera": { diff --git a/tests/screen_text_en.h b/tests/screen_text_en.h index 4d009ff..684b31c 100644 --- a/tests/screen_text_en.h +++ b/tests/screen_text_en.h @@ -3,7 +3,7 @@ #pragma once #include "components/smart_display/screen_text.h" -static_assert(screen_text::KEYS_HASH == 0x8FB53847u && screen_text::KEY_COUNT == 321, +static_assert(screen_text::KEYS_HASH == 0x6D3912FFu && screen_text::KEY_COUNT == 322, "screen_text_keys.h does not match screen_manager/translations/en.json: run tools/i18n.py header"); const char *const screen_text::TABLE[] = { ".", @@ -189,6 +189,7 @@ const char *const screen_text::TABLE[] = { "Never run", "Last {time}", "Yesterday {time}", + "Yest. {time}", "Last {date}", "No image from this camera", "Loading image", diff --git a/tools/i18n.py b/tools/i18n.py index 0bfafb1..51b76a3 100644 --- a/tools/i18n.py +++ b/tools/i18n.py @@ -133,10 +133,13 @@ def check(): if ours and code != 'en' and len(text) > max(8, len(source) * 1.6) and not plural: notes.append(f'{code}: {key} is much longer than the English ({len(text)} vs {len(source)})') if width and key in TILE_LINES: + shown_text = text + for name, sample in TILE_SAMPLES.items(): + shown_text = shown_text.replace('{' + name + '}', sample) for board, (size, room) in TILE_LINES[key].items(): - if width(size, text) > room: - problems.append(f'{code}: {key} "{text}" is too wide for a tile on the {board} ' - f'({width(size, text):.0f} of {room} px); make it shorter') + if width(size, shown_text) > room: + problems.append(f'{code}: {key} "{shown_text}" is too wide for a tile on the {board} ' + f'({width(size, shown_text):.0f} of {room} px); make it shorter') # The weather columns of the CYD hold a day of two letters beside its icon. if key.startswith('screen.date.weekdays_min.') and len(text) > 2: problems.append(f'{code}: {key} "{text}" is longer than two letters; the CYD\'s weather columns cut it') @@ -154,8 +157,12 @@ TILE_ROOM = {'CYD': (11, 88), 'Guition': (16, 128)} # Camera tiles are the Guition's alone. TILE_LINES = {'screen.tile.tap_to_open': TILE_ROOM, 'screen.script.never_run': TILE_ROOM, 'screen.script.running': TILE_ROOM, 'screen.media.not_playing': TILE_ROOM, 'screen.timer.paused': TILE_ROOM, + 'screen.script.last_time': TILE_ROOM, 'screen.script.yesterday_time_short': TILE_ROOM, 'screen.camera.tap_to_view': {'Guition': TILE_ROOM['Guition']}, 'screen.camera.no_image_yet': {'Guition': TILE_ROOM['Guition']}} +# What a placeholder stands for while a tile line is measured: the widest clock a screen writes is a 12-hour one +# with its AM/PM ("Yesterday 9:15 PM" is why screen.script.yesterday_time_short exists). +TILE_SAMPLES = {'time': '9:15 PM'} def text_width():