Split layout in landscape mode (#1334)
* Automatically split layouts in landscape This inserts empty space in the center of layouts when in landscape orientation. The keys are smaller and pushed to the edges of the screen. This way, the fingers don't have to reach the center of the screen, which is inconvenient in landscape orientation. * Duplicate keys that are on the middle Keys that are almost aligned on the middle of the screen are duplicated on both sides. These keys are most probably hit with both thumbs. This is especially useful for large keys like the space bar on the bottom row. * Split the bottom row * Add an option to control layout splitting
This commit is contained in:
@@ -142,4 +142,12 @@
|
||||
<item>hide</item>
|
||||
<item>show</item>
|
||||
</string-array>
|
||||
<string-array name="pref_split_layout_entries">
|
||||
<item>@string/pref_split_layout_landscape</item>
|
||||
<item>@string/pref_split_layout_never</item>
|
||||
</string-array>
|
||||
<string-array name="pref_split_layout_values">
|
||||
<item>landscape</item>
|
||||
<item>never</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -165,4 +165,7 @@
|
||||
<string name="pref_physical_keyboard_behavior">When a physical keyboard is connected</string>
|
||||
<string name="pref_physical_keyboard_behavior_hide">Hide everything</string>
|
||||
<string name="pref_physical_keyboard_behavior_show">Show everything</string>
|
||||
<string name="pref_split_layout">Split layout</string>
|
||||
<string name="pref_split_layout_never">Never</string>
|
||||
<string name="pref_split_layout_landscape">In landscape orientation</string>
|
||||
</resources>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<ListPreference android:key="number_row" android:title="@string/pref_number_row_title" android:summary="%s" android:defaultValue="no_number_row" android:entries="@array/pref_show_number_row_entries" android:entryValues="@array/pref_show_number_row_values"/>
|
||||
<ListPreference android:key="show_numpad" android:title="@string/pref_show_numpad_title" android:summary="%s" android:defaultValue="1" android:entries="@array/pref_show_numpad_entries" android:entryValues="@array/pref_show_numpad_values"/>
|
||||
<ListPreference android:key="numpad_layout" android:title="@string/pref_numpad_layout" android:summary="%s" android:defaultValue="high_first" android:entries="@array/pref_numpad_layout_entries" android:entryValues="@array/pref_numpad_layout_values"/>
|
||||
<ListPreference android:key="split_layout" android:title="@string/pref_split_layout" android:summary="%s" android:defaultValue="landscape" android:entries="@array/pref_split_layout_entries" android:entryValues="@array/pref_split_layout_values"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/pref_category_suggestions">
|
||||
<CheckBoxPreference android:key="suggestions" android:title="@string/pref_suggestions_title" android:summary="@string/pref_suggestions_summary" android:defaultValue="true"/>
|
||||
|
||||
@@ -95,6 +95,8 @@ public final class Config
|
||||
[get_current_layout()] and [set_current_layout()]. */
|
||||
int current_layout_narrow;
|
||||
int current_layout_wide;
|
||||
/** Whether to automatically split the layout. */
|
||||
public boolean split_layout;
|
||||
|
||||
private Config(SharedPreferences prefs, Resources res,
|
||||
Boolean foldableUnfolded, Dictionaries dicts)
|
||||
@@ -132,10 +134,12 @@ public final class Config
|
||||
show_numpad = true;
|
||||
keyboardHeightPercent = _prefs.getInt(foldable_unfolded ? "keyboard_height_landscape_unfolded" : "keyboard_height_landscape", 50);
|
||||
characterSizeScale = 1.25f;
|
||||
split_layout = _prefs.getString("split_layout", "landscape").equals("landscape");
|
||||
}
|
||||
else
|
||||
{
|
||||
keyboardHeightPercent = _prefs.getInt(foldable_unfolded ? "keyboard_height_unfolded" : "keyboard_height", 35);
|
||||
split_layout = false;
|
||||
}
|
||||
layouts = LayoutsPreference.load_from_preferences(res, _prefs);
|
||||
inverse_numpad = _prefs.getString("numpad_layout", "default").equals("low_first");
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class KeyboardData
|
||||
ArrayList<Row> rows_ = new ArrayList<Row>();
|
||||
for (Row r : rows)
|
||||
rows_.add(r.mapKeys(f));
|
||||
return new KeyboardData(this, rows_);
|
||||
return with_rows(rows_);
|
||||
}
|
||||
|
||||
/** Add keys from the given iterator into the keyboard. Preferred position is
|
||||
@@ -61,7 +61,7 @@ public final class KeyboardData
|
||||
}
|
||||
for (KeyValue kv : unplaced_keys)
|
||||
add_key_to_preferred_pos(rows, kv, PreferredPos.ANYWHERE);
|
||||
return new KeyboardData(this, rows);
|
||||
return with_rows(rows);
|
||||
}
|
||||
|
||||
/** Place a key on the keyboard according to its preferred position. Mutates
|
||||
@@ -141,9 +141,9 @@ public final class KeyboardData
|
||||
keys.add(nps.get(i));
|
||||
}
|
||||
}
|
||||
extendedRows.add(new Row(keys, row.height, row.shift));
|
||||
extendedRows.add(row.with_keys(keys));
|
||||
}
|
||||
return new KeyboardData(this, extendedRows);
|
||||
return with_rows(extendedRows);
|
||||
}
|
||||
|
||||
/** Insert the given row at the given indice. The row is scaled so that the
|
||||
@@ -152,7 +152,7 @@ public final class KeyboardData
|
||||
{
|
||||
ArrayList<Row> rows_ = new ArrayList<Row>(this.rows);
|
||||
rows_.add(i, row.updateWidth(keysWidth));
|
||||
return new KeyboardData(this, rows_);
|
||||
return with_rows(rows_);
|
||||
}
|
||||
|
||||
public Key findKeyWithValue(KeyValue kv)
|
||||
@@ -304,10 +304,11 @@ public final class KeyboardData
|
||||
}
|
||||
|
||||
/** Copies the fields of a keyboard, with rows changed. */
|
||||
protected KeyboardData(KeyboardData src, List<Row> rows)
|
||||
public KeyboardData with_rows(List<Row> rows_)
|
||||
{
|
||||
this(rows, compute_max_width(rows), src.modmap, src.script,
|
||||
src.numpad_script, src.name, src.bottom_row, src.embedded_number_row, src.locale_extra_keys);
|
||||
return new KeyboardData(rows_, compute_max_width(rows_), modmap, script,
|
||||
numpad_script, name, bottom_row, embedded_number_row,
|
||||
locale_extra_keys);
|
||||
}
|
||||
|
||||
public static class Row
|
||||
@@ -347,7 +348,14 @@ public final class KeyboardData
|
||||
|
||||
public Row copy()
|
||||
{
|
||||
return new Row(new ArrayList<Key>(keys), height, shift);
|
||||
return with_keys(new ArrayList<Key>(keys));
|
||||
}
|
||||
|
||||
/** Copy the row with the keys changed. The list [keys] is not copied and
|
||||
should be immutable. */
|
||||
public Row with_keys(List<Key> keys)
|
||||
{
|
||||
return new Row(keys, height, shift);
|
||||
}
|
||||
|
||||
public void getKeys(Map<KeyValue, KeyPos> dst, int row)
|
||||
@@ -368,7 +376,7 @@ public final class KeyboardData
|
||||
ArrayList<Key> keys_ = new ArrayList<Key>();
|
||||
for (Key k : keys)
|
||||
keys_.add(f.apply(k));
|
||||
return new Row(keys_, height, shift);
|
||||
return with_keys(keys_);
|
||||
}
|
||||
|
||||
/** Change the width of every keys so that the row is 's' units wide. */
|
||||
@@ -533,9 +541,19 @@ public final class KeyboardData
|
||||
return new Key(ks, anticircle, flags, width, shift, indication, role);
|
||||
}
|
||||
|
||||
public Key withWidth(float w)
|
||||
{
|
||||
return withWidthAndShift(w, shift);
|
||||
}
|
||||
|
||||
public Key withShift(float s)
|
||||
{
|
||||
return new Key(keys, anticircle, keysflags, width, s, indication, role);
|
||||
return withWidthAndShift(width, s);
|
||||
}
|
||||
|
||||
public Key withWidthAndShift(float w, float s)
|
||||
{
|
||||
return new Key(keys, anticircle, keysflags, w, s, indication, role);
|
||||
}
|
||||
|
||||
public boolean hasValue(KeyValue kv)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class LayoutLandscapeModifier
|
||||
{
|
||||
/** Width added to the layout, in the key width unit of the layout. */
|
||||
public static final int ADDED_WIDTH = 5;
|
||||
|
||||
public static KeyboardData transform_to_landscape(KeyboardData kw)
|
||||
{
|
||||
ArrayList<KeyboardData.Row> new_rows = new ArrayList<KeyboardData.Row>();
|
||||
for (KeyboardData.Row r : kw.rows)
|
||||
new_rows.add(split_row(r));
|
||||
return kw.with_rows(new_rows);
|
||||
}
|
||||
|
||||
public static KeyboardData.Row transform_number_row(KeyboardData.Row r)
|
||||
{
|
||||
return split_row(r);
|
||||
}
|
||||
|
||||
static KeyboardData.Row split_row(KeyboardData.Row r)
|
||||
{
|
||||
if (r.keys.size() < 2)
|
||||
return r;
|
||||
// Split the row at the key that overlaps that mid region. If the mid
|
||||
// region is entirely covered by one key, it is duplicated.
|
||||
float mid_start = r.keysWidth / 2 - 0.25f;
|
||||
float mid_end = mid_start + 0.5f;
|
||||
float off = 0f;
|
||||
int i = 0;
|
||||
int end = r.keys.size() - 1; // Exclude the last key to force a split
|
||||
for (; true; i++)
|
||||
{
|
||||
KeyboardData.Key k = r.keys.get(i);
|
||||
off += k.shift + k.width;
|
||||
if (off > mid_start || i == end)
|
||||
{
|
||||
if (off > mid_end)
|
||||
return duplicate_at_index(r, i, off);
|
||||
return split_at_index(r, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Insert [ADDED_WIDTH] empty space before the key at index [i]. */
|
||||
static KeyboardData.Row split_at_index(KeyboardData.Row r, int i)
|
||||
{
|
||||
List<KeyboardData.Key> new_keys = new ArrayList<KeyboardData.Key>(r.keys);
|
||||
KeyboardData.Key k = new_keys.get(i);
|
||||
new_keys.set(i, k.withShift(k.shift + ADDED_WIDTH));
|
||||
return r.with_keys(new_keys);
|
||||
}
|
||||
|
||||
/** Duplicate the key at index [i] and insert [ADDED_WIDTH] empty space in
|
||||
between. */
|
||||
static KeyboardData.Row duplicate_at_index(KeyboardData.Row r, int i, float off)
|
||||
{
|
||||
List<KeyboardData.Key> new_keys = new ArrayList<KeyboardData.Key>(r.keys);
|
||||
KeyboardData.Key k = new_keys.get(i);
|
||||
// Reduce the size of the duplicated keys if they would add more than 1 to
|
||||
// the width.
|
||||
float k_width = (k.width + 1.f) / 2;
|
||||
// Adjust the size on each sides when the key is not totally centered.
|
||||
float mid_d = Math.min(off - (r.keysWidth + k.width) / 2, k_width - 1);
|
||||
new_keys.add(i + 1, k.withWidthAndShift(k_width + mid_d, ADDED_WIDTH - 1));
|
||||
new_keys.set(i, k.withWidth(k_width - mid_d));
|
||||
return r.with_keys(new_keys);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,11 +46,16 @@ public final class LayoutModifier
|
||||
else if (globalConfig.add_number_row && !kw.embedded_number_row) // The numpad removes the number row
|
||||
{
|
||||
added_number_row = modify_number_row(globalConfig.number_row_symbols ? number_row_symbols : number_row_no_symbols, kw);
|
||||
if (globalConfig.split_layout)
|
||||
added_number_row = LayoutLandscapeModifier.transform_number_row(added_number_row);
|
||||
remove_keys.addAll(added_number_row.getKeys(0).keySet());
|
||||
}
|
||||
// Add the bottom row before computing the extra keys
|
||||
if (kw.bottom_row)
|
||||
kw = kw.insert_row(bottom_row, kw.rows.size());
|
||||
// Split the layout in landscape orientation
|
||||
if (globalConfig.split_layout)
|
||||
kw = LayoutLandscapeModifier.transform_to_landscape(kw);
|
||||
// Compose keys to add to the layout
|
||||
// 'extra_keys_keyset' reflects changes made to 'extra_keys'
|
||||
Set<KeyValue> extra_keys_keyset = extra_keys.keySet();
|
||||
|
||||
Reference in New Issue
Block a user