From 01f2c66f482d8a50cb65c2f719fa0f8668b8363a Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 10 Jul 2026 15:11:12 +0000 Subject: [PATCH] Refine terminal input controls --- .../neatstudio/tmuxandroid/MainActivity.java | 245 +++++++++++------- 1 file changed, 155 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/com/neatstudio/tmuxandroid/MainActivity.java b/app/src/main/java/com/neatstudio/tmuxandroid/MainActivity.java index 00afc3d..831443f 100644 --- a/app/src/main/java/com/neatstudio/tmuxandroid/MainActivity.java +++ b/app/src/main/java/com/neatstudio/tmuxandroid/MainActivity.java @@ -66,7 +66,7 @@ public final class MainActivity extends Activity { private static final int MAX_TERMINAL_COLS = 140; private static final int MIN_TERMINAL_ROWS = 8; private static final int MAX_TERMINAL_ROWS = 80; - private static final int TERMINAL_KEYS_HEIGHT_DP = 44; + private static final int TERMINAL_KEYS_HEIGHT_DP = 92; private static final int STATUS_NORMAL = 0; private static final int STATUS_BUSY = 1; private static final int STATUS_SUCCESS = 2; @@ -126,7 +126,7 @@ public final class MainActivity extends Activity { private TextView terminalMetaText; private ScrollView terminalScroll; private EditText inputField; - private Button terminalKeyPageButton; + private View terminalAccessoryBar; private View terminalComposerBar; private LinearLayout terminalGroupRow; private String activeSessionName; @@ -1634,12 +1634,13 @@ public final class MainActivity extends Activity { 0, 1 )); - terminalComposerBar = createComposerBar(); - root.addView(terminalComposerBar, matchWrap()); - root.addView(createAccessoryBar(), new LinearLayout.LayoutParams( + terminalAccessoryBar = createAccessoryBar(); + root.addView(terminalAccessoryBar, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(TERMINAL_KEYS_HEIGHT_DP) )); + terminalComposerBar = createComposerBar(); + root.addView(terminalComposerBar, matchWrap()); terminalScroll.post(() -> { resizeTerminalToViewport(false); connectTerminal(sessionName); @@ -2061,7 +2062,7 @@ public final class MainActivity extends Activity { private LinearLayout createComposerBar() { LinearLayout bar = new LinearLayout(this); bar.setOrientation(LinearLayout.VERTICAL); - bar.setPadding(dp(9), dp(7), dp(9), dp(8)); + bar.setPadding(dp(8), dp(5), dp(8), dp(7)); bar.setBackgroundColor(COLOR_BAR); LinearLayout row = new LinearLayout(this); @@ -2071,7 +2072,7 @@ public final class MainActivity extends Activity { inputField = new EditText(this); inputField.setTextColor(COLOR_TEXT); inputField.setHintTextColor(COLOR_TEXT_DIM); - inputField.setHint("type a command…"); + inputField.setHint("type command or text"); inputField.setSingleLine(false); inputField.setMinLines(1); inputField.setMaxLines(2); @@ -2099,21 +2100,20 @@ public final class MainActivity extends Activity { } return false; }); - Button image = terminalToolButton("▧", view -> pickImageForSession(activeSessionName)); + Button image = composerButton("Img", view -> pickImageForSession(activeSessionName)); image.setContentDescription("Upload image"); - row.addView(image, new LinearLayout.LayoutParams(dp(40), dp(40))); + row.addView(image, new LinearLayout.LayoutParams(dp(38), dp(38))); LinearLayout.LayoutParams inputParams = new LinearLayout.LayoutParams( 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1 ); - inputParams.leftMargin = dp(6); - inputParams.rightMargin = dp(6); + inputParams.leftMargin = dp(5); + inputParams.rightMargin = dp(5); row.addView(inputField, inputParams); - Button send = primaryButton("↵", view -> sendLine()); - send.setTextSize(18); + Button send = composerPrimaryButton("Send", view -> sendLine()); send.setContentDescription("Send input"); - row.addView(send, new LinearLayout.LayoutParams(dp(40), dp(40))); + row.addView(send, new LinearLayout.LayoutParams(dp(48), dp(38))); bar.addView(row, matchWrap()); return bar; } @@ -2676,19 +2676,41 @@ public final class MainActivity extends Activity { return input; } - private HorizontalScrollView createAccessoryBar() { - HorizontalScrollView scroller = new HorizontalScrollView(this); - scroller.setHorizontalScrollBarEnabled(false); - scroller.setBackgroundColor(COLOR_PANEL); + private LinearLayout createAccessoryBar() { + LinearLayout panel = new LinearLayout(this); + panel.setOrientation(LinearLayout.VERTICAL); + panel.setPadding(dp(6), dp(4), dp(6), dp(4)); + panel.setBackgroundColor(COLOR_PANEL); - LinearLayout row = terminalKeyRow(); - row.setPadding(dp(8), dp(5), dp(8), dp(5)); - addAccessoryPageKeys(row); - scroller.addView(row, new HorizontalScrollView.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.MATCH_PARENT + LinearLayout tabs = terminalKeyRow(); + tabs.setGravity(Gravity.CENTER_VERTICAL); + addAccessoryTab(tabs, "Edit", 0); + addAccessoryTab(tabs, "Ctrl", 1); + addAccessoryTab(tabs, "Nav", 2); + addAccessoryTab(tabs, "Sym", 3); + panel.addView(tabs, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + dp(24) )); - return scroller; + + LinearLayout firstRow = terminalKeyRow(); + LinearLayout secondRow = terminalKeyRow(); + addAccessoryPageKeys(firstRow, secondRow); + LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + 0, + 1 + ); + rowParams.topMargin = dp(3); + panel.addView(firstRow, rowParams); + LinearLayout.LayoutParams secondParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + 0, + 1 + ); + secondParams.topMargin = dp(3); + panel.addView(secondRow, secondParams); + return panel; } private LinearLayout terminalKeyRow() { @@ -2698,36 +2720,71 @@ public final class MainActivity extends Activity { return row; } - private void addAccessoryPageKeys(LinearLayout row) { - addAccessoryButton(row, "ctrl", view -> sendTerminalInput("\u0002")); - addSoftKey(row, "esc", "\u001b"); - addSoftKey(row, "tab", "\t"); - addTextKey(row, "~", "~"); - addTextKey(row, "/", "/"); - addTextKey(row, "-", "-"); - addTextKey(row, "|", "|"); - addSoftKey(row, "C-c", "\u0003"); - addSoftKey(row, "C-d", "\u0004"); - addSoftKey(row, "C-z", "\u001a"); - addSoftKey(row, "C-l", "\u000c"); - addComposerButton(row, "←", () -> moveComposerCursor(-1)); - addComposerButton(row, "→", () -> moveComposerCursor(1)); - addSoftKey(row, "↑", "\u001b[A"); - addSoftKey(row, "↓", "\u001b[B"); - addSoftButton(row, "Paste", view -> pasteClipboard()); - } - - private String accessoryPageName() { + private void addAccessoryPageKeys(LinearLayout firstRow, LinearLayout secondRow) { switch (terminalKeyPage) { case 1: - return "CTRL"; + addSoftKey(firstRow, "Esc", "\u001b"); + addSoftKey(firstRow, "Tab", "\t"); + addSoftKey(firstRow, "C-c", "\u0003"); + addSoftKey(firstRow, "C-d", "\u0004"); + addSoftKey(firstRow, "C-z", "\u001a"); + addSoftKey(firstRow, "C-l", "\u000c"); + addSoftKey(secondRow, "C-r", "\u0012"); + addSoftKey(secondRow, "C-a", "\u0001"); + addSoftKey(secondRow, "C-e", "\u0005"); + addSoftKey(secondRow, "C-u", "\u0015"); + addSoftKey(secondRow, "C-k", "\u000b"); + addSoftKey(secondRow, "C-b", "\u0002"); + break; case 2: - return "NAV"; + addSoftKey(firstRow, "←", "\u001b[D"); + addSoftKey(firstRow, "→", "\u001b[C"); + addSoftKey(firstRow, "↑", "\u001b[A"); + addSoftKey(firstRow, "↓", "\u001b[B"); + addSoftKey(firstRow, "Home", "\u001b[H"); + addSoftKey(firstRow, "End", "\u001b[F"); + addSoftKey(secondRow, "Pg↑", "\u001b[5~"); + addSoftKey(secondRow, "Pg↓", "\u001b[6~"); + addComposerButton(secondRow, "Prev", () -> setTerminalKeyPage(terminalKeyPage - 1)); + addComposerButton(secondRow, "Next", () -> setTerminalKeyPage(terminalKeyPage + 1)); + addSoftKey(secondRow, "Clear", "\u000c"); + addSoftKey(secondRow, "Detach", "\u0002d"); + break; case 3: - return "SYM"; + addTextKey(firstRow, "~", "~"); + addTextKey(firstRow, "/", "/"); + addTextKey(firstRow, "-", "-"); + addTextKey(firstRow, "_", "_"); + addTextKey(firstRow, ".", "."); + addTextKey(firstRow, "|", "|"); + addTextKey(secondRow, "&", "&"); + addTextKey(secondRow, ";", ";"); + addTextKey(secondRow, "$", "$"); + addTextKey(secondRow, "\"", "\""); + addTextKey(secondRow, "'", "'"); + addTextKey(secondRow, "Space", " "); + break; case 0: default: - return "EDIT"; + addComposerButton(firstRow, "←", () -> moveComposerCursor(-1)); + addComposerButton(firstRow, "→", () -> moveComposerCursor(1)); + addSoftKey(firstRow, "↑", "\u001b[A"); + addSoftKey(firstRow, "↓", "\u001b[B"); + addSoftKey(firstRow, "Esc", "\u001b"); + addSoftKey(firstRow, "Tab", "\t"); + addSoftKey(secondRow, "Enter", TERMINAL_ENTER); + addSoftButton(secondRow, "Paste", view -> pasteClipboard()); + addComposerButton(secondRow, "⌫", this::backspaceComposerText); + addTextKey(secondRow, "NL", "\n"); + addComposerButton(secondRow, "Bottom", this::scrollTerminalBottom); + addComposerButton(secondRow, "Select", () -> { + terminalSelectionEnabled = !terminalSelectionEnabled; + if (terminalText != null) { + terminalText.setTextIsSelectable(terminalSelectionEnabled); + } + setStatus(terminalSelectionEnabled ? "Terminal selection on" : "Terminal selection off"); + }); + break; } } @@ -2997,19 +3054,19 @@ public final class MainActivity extends Activity { private void setTerminalKeyPage(int page) { terminalKeyPage = (page + 4) % 4; - updateTerminalPageButton(); if (activeSessionName != null) { renderTerminalControlsOnly(); } } private void renderTerminalControlsOnly() { - int composerIndex = terminalComposerBar == null ? -1 : root.indexOfChild(terminalComposerBar); - if (composerIndex < 0 || composerIndex + 1 >= root.getChildCount()) { + int accessoryIndex = terminalAccessoryBar == null ? -1 : root.indexOfChild(terminalAccessoryBar); + if (accessoryIndex < 0) { return; } - root.removeViewAt(composerIndex + 1); - root.addView(createAccessoryBar(), composerIndex + 1, new LinearLayout.LayoutParams( + root.removeViewAt(accessoryIndex); + terminalAccessoryBar = createAccessoryBar(); + root.addView(terminalAccessoryBar, accessoryIndex, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(TERMINAL_KEYS_HEIGHT_DP) )); @@ -3162,29 +3219,21 @@ public final class MainActivity extends Activity { return button; } - private Button terminalPageButton() { - Button button = toolbarButton("", view -> setTerminalKeyPage(terminalKeyPage + 1)); - button.setTextSize(9); - button.setPadding(dp(4), 0, dp(4), 0); - button.setMinWidth(dp(42)); - button.setMinimumWidth(dp(42)); - button.setOnLongClickListener(view -> { - view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - setTerminalKeyPage(terminalKeyPage - 1); - return true; - }); - updateTerminalPageButton(button); + private Button composerButton(String label, View.OnClickListener listener) { + Button button = toolbarButton(label, listener); + button.setTextSize(10); + button.setPadding(dp(5), 0, dp(5), 0); + button.setMinWidth(0); + button.setMinimumWidth(0); return button; } - private void updateTerminalPageButton() { - updateTerminalPageButton(terminalKeyPageButton); - } - - private void updateTerminalPageButton(Button button) { - if (button != null) { - button.setText(accessoryPageName()); - } + private Button composerPrimaryButton(String label, View.OnClickListener listener) { + Button button = composerButton(label, listener); + button.setTextColor(Color.rgb(14, 38, 24)); + button.setTextSize(11); + button.setBackground(rounded(COLOR_ACCENT, 7, COLOR_ACCENT, 1)); + return button; } private void styleInput(EditText input) { @@ -3199,8 +3248,9 @@ public final class MainActivity extends Activity { private void styleComposerInput(EditText input) { input.setTextColor(COLOR_TEXT); input.setHintTextColor(COLOR_TEXT_DIM); - input.setTextSize(14); - input.setPadding(dp(9), dp(6), dp(9), dp(6)); + input.setTextSize(13); + input.setMinHeight(dp(38)); + input.setPadding(dp(8), dp(5), dp(8), dp(5)); input.setBackground(inputBackground()); } @@ -3252,10 +3302,6 @@ public final class MainActivity extends Activity { addSoftButton(row, label, view -> insertComposerText(text)); } - private void addAccessoryButton(LinearLayout row, String label, View.OnClickListener listener) { - addSoftButton(row, label, listener); - } - private void addComposerButton(LinearLayout row, String label, Runnable action) { addSoftButton(row, label, view -> action.run()); } @@ -3263,18 +3309,37 @@ public final class MainActivity extends Activity { private void addSoftButton(LinearLayout row, String label, View.OnClickListener listener) { Button button = toolbarButton(label, listener); button.setTextSize(isArrowLabel(label) ? 15 : 9); - button.setPadding(dp(4), 0, dp(4), 0); - int width = "Space".equals(label) ? 56 : (isArrowLabel(label) ? 32 : 38); - button.setMinWidth(dp(width)); - button.setMinimumWidth(dp(width)); + button.setPadding(dp(2), 0, dp(2), 0); + button.setMinWidth(0); + button.setMinimumWidth(0); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - dp(27) + 0, + ViewGroup.LayoutParams.MATCH_PARENT, + 1 ); - params.leftMargin = dp(1); - params.rightMargin = dp(1); - params.topMargin = dp(2); - params.bottomMargin = dp(1); + params.leftMargin = dp(2); + params.rightMargin = dp(2); + row.addView(button, params); + } + + private void addAccessoryTab(LinearLayout row, String label, int page) { + Button button = toolbarButton(label, view -> setTerminalKeyPage(page)); + boolean selected = terminalKeyPage == page; + button.setTextSize(9); + button.setPadding(dp(2), 0, dp(2), 0); + button.setMinWidth(0); + button.setMinimumWidth(0); + button.setTextColor(selected ? Color.rgb(14, 38, 24) : COLOR_TEXT_MUTED); + button.setBackground(selected + ? rounded(COLOR_ACCENT, 7, COLOR_ACCENT, 1) + : rounded(COLOR_CARD_ALT, 7, COLOR_BORDER_SOFT, 1)); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + 0, + ViewGroup.LayoutParams.MATCH_PARENT, + 1 + ); + params.leftMargin = dp(2); + params.rightMargin = dp(2); row.addView(button, params); }