diff --git a/src/base/base.cgh b/src/base/base.cgh index db64b312..5ac0e16c 100644 --- a/src/base/base.cgh +++ b/src/base/base.cgh @@ -114,23 +114,11 @@ // TODO: Eventually hard-code to something like 128 if we ever decide to support Apple M-series #define IsolationSize 64 -//- Windows NTDDI version -// TODO: Remove this -#if 0 -#if IsCompilerMsvc - #define NTDDI_WIN11_DT 0x0C0A0000 - #define NTDDI_VERSION 0x0A000000 - #if IsRtcEnabled - #define _ALLOW_RTCc_IN_STL 1 - #endif -#endif -#endif - //////////////////////////////////////////////////////////// //~ C headers #if IsCpu - // C standard library + //- C standard library #include #include #include @@ -150,7 +138,7 @@ // -wmmintrin.h AES // -immintrin.h AVX, AVX2, FMA #include - #include // SSE4.2 + #include #endif #endif diff --git a/src/base/base_controller.h b/src/base/base_controller.h index 495e4240..e884d294 100644 --- a/src/base/base_controller.h +++ b/src/base/base_controller.h @@ -139,7 +139,8 @@ Struct(ControllerEvent) b32 is_repeat; // ControllerEventKind_Text - u32 text_codepoint; + u32 text_chars_count; + u8 text_chars[4]; // ControllerEventKind_CursorMove Vec2 cursor_pos; diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 6d4a4090..3fbe0590 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -258,6 +258,213 @@ void V_DrawPoint(Vec2 p, Vec4 srgb) V_DrawShape(ui_shape, AffineIdentity, srgb, 24, V_DrawFlag_None); } +//////////////////////////////////////////////////////////// +//~ Text box + +void V_ApplyTextBoxDelta(V_TextBoxState *tb, V_TextBoxDelta delta) +{ + V_TextBoxDeltaNode tbd_node = { .delta = delta }; + V_ApplyTextBoxDeltas(tb, (V_TextBoxDeltaList) { .first = &tbd_node, .last = &tbd_node }); +} + +void V_ApplyTextBoxDeltas(V_TextBoxState *tb, V_TextBoxDeltaList deltas) +{ + for (V_TextBoxDeltaNode *tbd_node = deltas.first; tbd_node; tbd_node = tbd_node->next) + { + V_TextBoxDelta delta = tbd_node->delta; + + // if (!(delta.flags & V_TextBoxDeltaFlag_DontUpdateCursor)) + // { + // tb->start = ClampI64(delta.start, 0, tb->len); + // tb->end = ClampI64(delta.end, 0, tb->len); + // } + + + + + // Navigate + { + { + if (delta.flags & V_TextBoxDeltaFlag_NavDirect) + { + tb->start = delta.direct_start; + tb->end = delta.direct_end; + } + if (delta.flags & V_TextBoxDeltaFlag_NavLeft) + { + if (delta.flags & V_TextBoxDeltaFlag_NavLine) + { + // FIXME: Real line start + tb->end = 0; + } + else if (delta.flags & V_TextBoxDeltaFlag_NavWord) + { + for (i64 pos = ClampI64(tb->end - 1, 0, tb->len); pos >= 0; --pos) + { + tb->end = pos - 1; + if (tb->text[pos] == ' ') + { + break; + } + } + } + else + { + tb->end -= 1; + } + } + if (delta.flags & V_TextBoxDeltaFlag_NavRight) + { + if (delta.flags & V_TextBoxDeltaFlag_NavLine) + { + // FIXME: Real line end + tb->end = I64Max; + } + else if (delta.flags & V_TextBoxDeltaFlag_NavWord) + { + for (i64 pos = ClampI64(tb->end + 1, 0, tb->len); pos < tb->len; ++pos) + { + tb->end = pos + 1; + if (tb->text[pos] == ' ') + { + break; + } + } + } + else + { + tb->end += 1; + } + } + } + if (!(delta.flags & V_TextBoxDeltaFlag_NavSelect)) + { + tb->start = tb->end; + } + tb->start = ClampI64(tb->start, 0, tb->len); + tb->end = ClampI64(tb->end, 0, tb->len); + } + + + + + + // Update text + if (delta.flags & V_TextBoxDeltaFlag_UpdateText) + { + RngI64 replace = Zi; + + // Remove selected text + { + replace = RNGI64(MinI64(tb->start, tb->end), MaxI64(tb->start, tb->end)); + if (replace.max > replace.min) + { + // FIXME: Truncate + i64 move_len = ClampI64(tb->len - replace.max, 0, countof(tb->text) - replace.max); + for (i64 src_pos = replace.max; src_pos < (replace.max + move_len); ++src_pos) + { + i64 dst_pos = src_pos + move_len; + tb->text[dst_pos] = tb->text[src_pos]; + } + } + tb->len -= replace.max - replace.min; + } + + // Insert new text + if (delta.text.len > 0) + { + i64 insert_len = ClampI64(delta.text.len, 0, countof(tb->text) - replace.min); + + // Make room + // FIXME: Truncate + i64 move_len = ClampI64(tb->len - replace.min, 0, countof(tb->text) - replace.min); + for (i64 src_pos = replace.min; src_pos < (replace.min + move_len); ++src_pos) + { + i64 dst_pos = src_pos + move_len; + tb->text[dst_pos] = tb->text[src_pos]; + } + + // Copy from delta + for (i64 src_pos = 0; src_pos < insert_len; ++src_pos) + { + i64 dst_pos = replace.min + src_pos; + tb->text[dst_pos] = delta.text.text[src_pos]; + } + tb->len += insert_len; + } + + // FIXME: Expand selection on insert + // tb->start = ClampI64(tb->start, 0, tb->len); + // tb->end = ClampI64(tb->end, 0, tb->len); + + tb->end = ClampI64(tb->end, 0, tb->len); + tb->start = tb->end; + } + + + + + + + + + + + + + // if (!(delta.flags & V_TextBoxDeltaFlag_DontUpdateText)) + // { + // RngI64 replace = Zi; + + // // Remove selected text + // { + // replace = RNGI64(MinI64(delta.start, delta.end), MaxI64(delta.start, delta.end)); + // replace.min = MaxI64(replace.min, 0); + // replace.max = MinI64(replace.max, tb->len); + // if (replace.max > replace.min) + // { + // // FIXME: Truncate + // i64 move_len = ClampI64(tb->len - replace.max, 0, countof(tb->text) - replace.max); + // for (i64 src_pos = replace.max; src_pos < (replace.max + move_len); ++src_pos) + // { + // i64 dst_pos = src_pos + move_len; + // tb->text[dst_pos] = tb->text[src_pos]; + // } + // } + // tb->len -= replace.max - replace.min; + // } + + // // Insert new text + // if (delta.text.len > 0) + // { + // i64 insert_len = ClampI64(delta.text.len, 0, countof(tb->text) - replace.min); + + // // Make room + // // FIXME: Truncate + // i64 move_len = ClampI64(tb->len - replace.min, 0, countof(tb->text) - replace.min); + // for (i64 src_pos = replace.min; src_pos < (replace.min + move_len); ++src_pos) + // { + // i64 dst_pos = src_pos + move_len; + // tb->text[dst_pos] = tb->text[src_pos]; + // } + + // // Copy from delta + // for (i64 src_pos = 0; src_pos < insert_len; ++src_pos) + // { + // i64 dst_pos = replace.min + src_pos; + // tb->text[dst_pos] = delta.text.text[src_pos]; + // } + // tb->len += insert_len; + // } + // } + } +} + +String V_StringFromTextBox(V_TextBoxState *tb) +{ + return STRING(tb->len, tb->text); +} + //////////////////////////////////////////////////////////// //~ Timeline helpers @@ -802,7 +1009,9 @@ void V_TickForever(WaveLaneCtx *lane) NET_PipeStatistics vis_pipe_stats = NET_StatsFromPipe(net_pipe); ////////////////////////////// - //- Process controller events into vis cmds + //- Process controller events + + V_TextBoxDeltaList text_input_deltas = Zi; frame->has_mouse_focus = UI_IsKeyNil(ui_frame->hot_box) || UI_MatchKey(ui_frame->hot_box, vis_box); frame->has_keyboard_focus = 1; @@ -839,6 +1048,7 @@ void V_TickForever(WaveLaneCtx *lane) ControllerEvent cev = window_frame.controller_events.events[cev_idx]; b32 down = cev.kind == ControllerEventKind_ButtonDown; b32 up = cev.kind == ControllerEventKind_ButtonUp; + String text = cev.kind == ControllerEventKind_Text ? STRING(cev.text_chars_count, cev.text_chars) : STRING(0, 0); b32 ignore = 0; if (down) @@ -877,10 +1087,70 @@ void V_TickForever(WaveLaneCtx *lane) break; } } - if (shortcut != 0 && down) + if (down && shortcut) { V_PushVisCmd(shortcut->cmd_name); } + else + { + V_TextBoxDelta delta = Zi; + { + if (down && cev.button == Button_Left) + { + delta.flags |= V_TextBoxDeltaFlag_NavLeft; + } + if (down && cev.button == Button_Right) + { + delta.flags |= V_TextBoxDeltaFlag_NavRight; + } + if (down && cev.button == Button_Home) + { + delta.flags |= V_TextBoxDeltaFlag_NavLeft | V_TextBoxDeltaFlag_NavLine; + } + if (down && cev.button == Button_End) + { + delta.flags |= V_TextBoxDeltaFlag_NavRight | V_TextBoxDeltaFlag_NavLine; + } + if (down && cev.button == Button_A && frame->held_buttons[Button_Ctrl]) + { + delta.flags |= V_TextBoxDeltaFlag_NavSelect | V_TextBoxDeltaFlag_NavDirect; + delta.direct_start = 0; + delta.direct_end = I64Max; + } + if (down && cev.button == Button_Backspace) + { + delta.text = Lit(""); + delta.flags |= V_TextBoxDeltaFlag_NavSelect | V_TextBoxDeltaFlag_NavLeft | V_TextBoxDeltaFlag_UpdateText; + } + if (down && cev.button == Button_Delete) + { + delta.text = Lit(""); + delta.flags |= V_TextBoxDeltaFlag_NavSelect | V_TextBoxDeltaFlag_NavRight | V_TextBoxDeltaFlag_UpdateText; + } + if (text.len > 0) + { + delta.flags |= V_TextBoxDeltaFlag_UpdateText; + delta.text = text; + } + if (delta.flags) + { + if (frame->held_buttons[Button_Shift]) + { + delta.flags |= V_TextBoxDeltaFlag_NavSelect; + } + if (frame->held_buttons[Button_Ctrl]) + { + delta.flags |= V_TextBoxDeltaFlag_NavWord; + } + } + } + if (delta.flags) + { + V_TextBoxDeltaNode *tbd_node = PushStruct(frame->arena, V_TextBoxDeltaNode); + tbd_node->delta = delta; + SllQueuePush(text_input_deltas.first, text_input_deltas.last, tbd_node); + } + } } frame->held_buttons[hotkey.button] = down; } @@ -3990,6 +4260,9 @@ void V_TickForever(WaveLaneCtx *lane) if (palette->is_showing || palette->show > 0.001) { + f32 item_size_px = UI_FNT(1.5, 1).v; + f32 tweak_size_px = UI_FNT(1.25, 1).v; + palette->key = UI_KeyF("command palette"); UI_Checkpoint palette_cp = UI_PushCP(UI_NilKey); { @@ -4061,6 +4334,257 @@ void V_TickForever(WaveLaneCtx *lane) UI_PopCP(UI_TopCP()); } + ////////////////////////////// + //- Build searchbox + + b32 is_searching = 0; + String search_text = Zi; + { + UI_Size search_height = UI_PIX(item_size_px * 1.25, 1); + + UI_SetNext(ChildAlignment, UI_Region_Left); + UI_PushCP(UI_BuildRow()); + { + //- Search icon + { + f32 size_px = UI_Top(FontSize) * 1.25; + + UI_SetNext(Rounding, UI_RGROW(0.75 * theme.rounding)); + UI_SetNext(ChildAlignment, UI_Region_Center); + UI_SetNext(BackgroundColor, 0); + // UI_SetNext(BorderColor, reset_bd); + UI_SetNext(BorderSize, 0); + UI_SetNext(Rounding, 0); + UI_SetNext(TextColor, theme.col.hint); + UI_SetNext(Width, UI_PIX(size_px * 1.5, 1)); + UI_SetNext(Height, UI_PIX(size_px * 1.5, 1)); + // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); + UI_SetNext(FontSize, size_px * theme.h6); + UI_BuildIcon(theme.icon_font, UI_Icon_Search); + // UI_BuildRow(); + } + + //- Search box + { + UI_Key search_box = UI_KeyF("search box"); + UI_BoxReport search_report = UI_ReportsFromKey(search_box).draw; + V_TextBoxState *search_state = &palette->search_state; + + // if (search_report.m1.downs) + if (search_report.m1.downs || !UI_MatchKey(search_box, V.keyboard_focus_box)) + { + V.keyboard_focus_box = search_box; + V.text_input_ns = frame->time_ns; + } + b32 has_focus = UI_MatchKey(search_box, V.keyboard_focus_box); + + if (search_report.is_hot) + { + WND_SetCursor(window_frame, WND_CursorKind_Text); + } + + if (has_focus && text_input_deltas.first) + { + V_ApplyTextBoxDeltas(search_state, text_input_deltas); + } + + + + + + // FIXME: Remove this + if (frame->held_buttons[Button_B] && !prev_frame->held_buttons[Button_B]) + { + V_TextBoxDelta delta = Zi; + // delta.kind = V_DeltaKind_ + delta.text = Lit("Hi"); + // delta.range = RNGI64(I64Max, I64Max); + + // delta.start = search_state->start; + // delta.end = search_state->end; + + // delta.start = 0; + // delta.end = 1; + + delta.flags |= V_TextBoxDeltaFlag_UpdateText; + + V_ApplyTextBoxDelta(search_state, delta); + } + + + // { + // V_TextBoxDelta delta = Zi; + + // // FIXME: Remove this + // if (frame->held_buttons[Button_Left] && !prev_frame->held_buttons[Button_Left]) + // { + // delta.flags |= V_TextBoxDeltaFlag_NavLeft; + // } + // if (frame->held_buttons[Button_Right] && !prev_frame->held_buttons[Button_Right]) + // { + // delta.flags |= V_TextBoxDeltaFlag_NavRight; + // } + + // if (delta.flags) + // { + // if (frame->held_buttons[Button_Shift]) + // { + // delta.flags |= V_TextBoxDeltaFlag_NavSelect; + // } + // if (frame->held_buttons[Button_Ctrl]) + // { + // delta.flags |= V_TextBoxDeltaFlag_NavWord; + // } + // V_ApplyTextBoxDelta(search_state, delta); + // V.text_input_ns = frame->time_ns; + // } + // } + + + + + + + + + + search_text = V_StringFromTextBox(search_state); + is_searching = search_text.len != 0; + + + String display_text = search_text; + Vec4 display_text_color = Color_White; + if (!is_searching) + { + display_text_color = theme.col.hint; + display_text = Lit(" Search..."); + } + + // Vec4 raah_color = Color_Black; + Vec4 raah_color = Zi; + + UI_SetNext(Height, search_height); + UI_SetNext(Width, UI_GROW(1, 0)); + UI_SetNext(BackgroundColor, raah_color); + // UI_SetNext(BorderColor, item_border_color); + UI_SetNext(BorderSize, 0); + // UI_SetNext(Rounding, UI_RPIX(5)); + UI_SetNext(ChildAlignment, UI_Region_Left); + UI_SetNext(TextColor, display_text_color); + UI_SetNext(Text, display_text); + UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse); + UI_PushCP(UI_BuildRowEx(search_box)); + { + f32 font_size = UI_Top(FontSize); + GC_FontKey font = UI_Top(Font); + + //- Text caret/selection + if (has_focus) + { + UI_Key caret_box = UI_KeyF("search caret"); + UI_Key selection_box = UI_KeyF("search selection"); + + + UI_Size caret_width = UI_PIX(1, 1); + UI_Size caret_height = UI_PIX(search_height.v, 1); + + + Vec4 selection_color = VEC4(0, 0.25, 0.75, 0.5); + + Vec4 caret_color = VEC4(1, 1, 1, 0.75); + // caret_color.a = AbsF32(SinF32(SecondsFromNs(frame->time_ns - V.text_input_ns) * 4)); + // caret_color.a = SinF32(SecondsFromNs(frame->time_ns - V.text_input_ns)) > 0.5 ? 1 : 0; + // caret_color.a = (CosF32(SecondsFromNs(frame->time_ns - V.text_input_ns) * 5) + 1) * 0.5; + caret_color.a *= AbsF32(CosF32(SecondsFromNs(frame->time_ns - V.text_input_ns) * 3)); + + f32 start_offset_px = 0; + f32 end_offset_px = 0; + + // caret_offset_px = TweakFloat("RAAAAAAAAAAAAAAAAAAAH", 0, 0, 100); + + // caret_offset_px = + + + + + // FIXME: Remove this, use UI computed run + if (search_text.len > 0) + { + String32 codepoints = String32FromString(frame->arena, search_text); + GC_Run run = GC_RunFromString32(frame->arena, codepoints, font, font_size); + + // FIXME: Don't use rect idx + // FIXME: Selection + // FIXME: Offset & scissor + for (i64 rect_idx = 0; rect_idx < (i64)run.rects_count; ++rect_idx) + { + if (rect_idx < search_state->start) + { + GC_RunRect rect = run.rects[rect_idx]; + start_offset_px = rect.baseline_pos + rect.advance; + } + if (rect_idx < search_state->end) + { + GC_RunRect rect = run.rects[rect_idx]; + end_offset_px = rect.baseline_pos + rect.advance; + } + if (rect_idx >= search_state->start && rect_idx >= search_state->end) + { + break; + } + } + } + + + + + + + + + // { + // i64 offset_ + // } + + // Selection + { + f32 min = MinF32(start_offset_px, end_offset_px); + f32 max = MaxF32(start_offset_px, end_offset_px); + + UI_SetNext(Width, UI_PIX(max - min, 1)); + UI_SetNext(Height, caret_height); + UI_SetNext(FloatingPos, VEC2(min, 0)); + UI_SetNext(Anchor, UI_Region_Left); + UI_SetNext(Flags, UI_BoxFlag_Floating, UI_BoxFlag_CaptureMouse); + UI_SetNext(BorderSize, 0); + UI_SetNext(BackgroundColor, selection_color); + UI_SetNext(FontSize, font_size); + UI_BuildBoxEx(selection_box); + } + + // Caret + { + UI_SetNext(Width, caret_width); + UI_SetNext(Height, caret_height); + UI_SetNext(FloatingPos, VEC2(end_offset_px, 0)); + UI_SetNext(Anchor, UI_Region_Left); + UI_SetNext(Flags, UI_BoxFlag_Floating, UI_BoxFlag_CaptureMouse); + UI_SetNext(BorderSize, 0); + UI_SetNext(BackgroundColor, caret_color); + UI_SetNext(FontSize, font_size); + UI_BuildBoxEx(caret_box); + } + } + + + + } + UI_PopCP(UI_TopCP()); + } + } + UI_PopCP(UI_TopCP()); + } + ////////////////////////////// //- Build palette items list @@ -4178,9 +4702,6 @@ void V_TickForever(WaveLaneCtx *lane) item_border_color = LerpSrgb(item_border_color, theme.col.button_active, item_rep.hot); } - f32 item_size_px = UI_FNT(1.5, 1).v; - f32 tweak_size_px = UI_FNT(1.25, 1).v; - UI_SetNext(Tint, 0); UI_PushCP(UI_BuildRow()); { diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 277844d2..b41c9b26 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -138,7 +138,7 @@ Struct(V_CmdNode) Global Readonly V_CmdDesc V_cmd_descs[V_CmdKind_COUNT] = { #define X(_name, _display_name, _flags, ...) { .name = CompLit(#_name), .display_name = CompLit(#_display_name), .flags = _flags, .default_hotkeys = { __VA_ARGS__ } }, - V_CmdsTableXList(X) + V_CmdsTableXList(X) #undef X }; @@ -154,16 +154,63 @@ Struct(V_Notif) String msg; }; +//////////////////////////////////////////////////////////// +//~ Text box types + +#define V_MaxTextBoxLen 1024 + +Enum(V_TextBoxDeltaFlag) +{ + V_TextBoxDeltaFlag_None = 0, + V_TextBoxDeltaFlag_UpdateText = (1 << 0), + V_TextBoxDeltaFlag_NavDirect = (1 << 1), + V_TextBoxDeltaFlag_NavLeft = (1 << 2), + V_TextBoxDeltaFlag_NavRight = (1 << 3), + V_TextBoxDeltaFlag_NavWord = (1 << 4), + V_TextBoxDeltaFlag_NavLine = (1 << 5), + V_TextBoxDeltaFlag_NavSelect = (1 << 6), +}; + +Struct(V_TextBoxDelta) +{ + V_TextBoxDeltaFlag flags; + String text; + i64 direct_start; + i64 direct_end; +}; + +Struct(V_TextBoxDeltaNode) +{ + V_TextBoxDeltaNode *next; + V_TextBoxDelta delta; +}; + +Struct(V_TextBoxDeltaList) +{ + V_TextBoxDeltaNode *first; + V_TextBoxDeltaNode *last; +}; + +Struct(V_TextBoxState) +{ + i64 len; + u8 text[V_MaxTextBoxLen]; + + i64 start; + i64 end; +}; + //////////////////////////////////////////////////////////// //~ Palette types Struct(V_Palette) { - // Persistent state Vec2 pos; UI_Key key; b32 is_showing; f32 show; + + V_TextBoxState search_state; }; //////////////////////////////////////////////////////////// @@ -303,6 +350,9 @@ Struct(V_Ctx) V_Panel *root_panel; V_Window *dragging_window; + UI_Key keyboard_focus_box; + i64 text_input_ns; + i64 connect_try_ns; V_ObservationBin observation_bins[V_ObservationBinsCount]; @@ -349,6 +399,14 @@ void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb); void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags); void V_DrawPoint(Vec2 p, Vec4 srgb); +//////////////////////////////////////////////////////////// +//~ Text box + +void V_ApplyTextBoxDelta(V_TextBoxState *tb, V_TextBoxDelta delta); +void V_ApplyTextBoxDeltas(V_TextBoxState *tb, V_TextBoxDeltaList deltas); + +String V_StringFromTextBox(V_TextBoxState *tb); + //////////////////////////////////////////////////////////// //~ Timeline helpers diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 92084a91..e65620ec 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -1536,6 +1536,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) GC_Run raw_run_unscaled = box->glyph_run; GC_Run raw_run = UI_ScaleRun(frame->arena, raw_run_unscaled, box->solved_scale); + // TODO: Check that sprite image is not empty b32 is_visible = 1; is_visible = is_visible && (box->desc.tint.w >= 0.0025); is_visible = is_visible && (box->screen_rect.p1.x - box->screen_rect.p0.x > 0.0025); diff --git a/src/window/window_win32/window_win32.c b/src/window/window_win32/window_win32.c index 17781879..db6ed55f 100644 --- a/src/window/window_win32/window_win32.c +++ b/src/window/window_win32/window_win32.c @@ -328,9 +328,11 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l } if ((codepoint >= 32 && codepoint != 127) || codepoint == '\t' || codepoint == '\n') { + Utf8EncodeResult encoded = EncodeUtf8(codepoint); ControllerEvent event = Zi; event.kind = ControllerEventKind_Text; - event.text_codepoint = codepoint; + event.text_chars_count = MinU32(encoded.count8, countof(event.text_chars)); + CopyBytes(event.text_chars, encoded.chars8, event.text_chars_count); WND_W32_PushEvent(window, event); } } break;