Skip to content

Commit

Permalink
Working layout...
Browse files Browse the repository at this point in the history
  • Loading branch information
treeman committed Oct 30, 2023
1 parent 46dedfa commit 73fff1c
Show file tree
Hide file tree
Showing 22 changed files with 2,092 additions and 0 deletions.
230 changes: 230 additions & 0 deletions keyboards/ferris/keymaps/treeman/casemodes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/* Copyright 2021 Andrew Rae [email protected] @andrewjrae
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "casemodes.h"
#include "status.h"
#include "keycodes.h"

/* The caps word concept started with @iaap on splitkb.com discord.
* However it has been implemented and extended by many splitkb.com users:
* - @theol0403 made many improvements to initial implementation
* - @precondition used caps lock rather than shifting
* - @dnaq his own implementation which also used caps lock
* - @sevanteri added underscores on spaces
* - @metheon extended on @sevanteri's work and added specific modes for
* snake_case and SCREAMING_SNAKE_CASE
* - @baffalop came up with the idea for xcase, which he implements in his own
* repo, however this is implemented by @iaap with support also for one-shot-shift.
* - @sevanteri
* - fixed xcase waiting mode to allow more modified keys and keys from other layers.
* - Added @baffalop's separator defaulting on first keypress, with a
* configurable default separator and overrideable function to determine
* if the default should be used.
*/

#ifndef DEFAULT_XCASE_SEPARATOR
# define DEFAULT_XCASE_SEPARATOR KC_UNDS
#endif

#define IS_OSM(keycode) (keycode >= QK_ONE_SHOT_MOD && keycode <= QK_ONE_SHOT_MOD_MAX)

// enum for the xcase states
enum xcase_state {
XCASE_OFF = 0, // xcase is off
XCASE_ON, // xcase is actively on
XCASE_WAIT, // xcase is waiting for the delimiter input
};

// bool to keep track of the caps word state
static bool caps_word_on = false;

// enum to keep track of the xcase state
static enum xcase_state xcase_state = XCASE_OFF;
// the keycode of the xcase delimiter
static uint16_t xcase_delimiter;

// Check whether caps word is on
bool caps_word_enabled(void) {
return caps_word_on;
}

// Enable caps word
void enable_caps_word(void) {
caps_word_on = true;
if (!host_keyboard_led_state().caps_lock) {
tap_code(is_caps_swapped() ? KC_ESC : KC_CAPS);
}
}

// Disable caps word
void disable_caps_word(void) {
caps_word_on = false;
if (host_keyboard_led_state().caps_lock) {
tap_code(is_caps_swapped() ? KC_ESC : KC_CAPS);
}
}

// Toggle caps word
void toggle_caps_word(void) {
if (caps_word_on) {
disable_caps_word();
} else {
enable_caps_word();
}
}

// Check whether xcase is on
bool xcase_enabled(void) {
return xcase_state == XCASE_ON;
}

// Check whether xcase is waiting for a keypress
bool xcase_waiting(void) {
return xcase_state == XCASE_WAIT;
}

// Enable xcase and pickup the next keystroke as the delimiter
void enable_xcase(void) {
xcase_state = XCASE_WAIT;
}

// Enable xcase with the specified delimiter
void enable_xcase_with(uint16_t delimiter) {
xcase_state = XCASE_ON;
xcase_delimiter = delimiter;
}

// Disable xcase
void disable_xcase(void) {
xcase_state = XCASE_OFF;
}

// Place the current xcase delimiter
static void place_delimiter(void) {
switch (xcase_delimiter) {
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: {
// apparently set_oneshot_mods() is dumb and doesn't deal with handedness for you
uint8_t mods = xcase_delimiter & 0x10 ? (xcase_delimiter & 0x0F) << 4 : xcase_delimiter & 0xFF;
set_oneshot_mods(mods);
break;
}
default:
tap_code16(xcase_delimiter);
break;
}
}

// overrideable function to determine whether the case mode should stop
__attribute__((weak)) bool terminate_case_modes(uint16_t keycode, const keyrecord_t *record) {
switch (keycode) {
// Keycodes to ignore (don't disable caps word)
case KC_A ... KC_Z:
case KC_1 ... KC_0:
case KC_MINS:
case KC_UNDS:
case KC_BSPC:
// If mod chording disable the mods
if (record->event.pressed && (get_mods() != 0)) {
return true;
}
break;
default:
if (record->event.pressed) {
return true;
}
break;
}
return false;
}

/* overrideable function to determine whether to use the default separator on
* first keypress when waiting for the separator. */
__attribute__((weak)) bool use_default_xcase_separator(uint16_t keycode, const keyrecord_t *record) {
// for example:
/* switch (keycode) { */
/* case KC_A ... KC_Z: */
/* case KC_1 ... KC_0: */
/* return true; */
/* } */
return false;
}

bool process_case_modes(uint16_t keycode, const keyrecord_t *record) {
if (xcase_state == XCASE_WAIT) {
// grab the next input to be the delimiter
if (use_default_xcase_separator(keycode, record)) {
enable_xcase_with(DEFAULT_XCASE_SEPARATOR);
} else if (record->event.pressed) {
if (keycode > QK_MODS_MAX || IS_MODIFIER_KEYCODE(keycode)) {
// let special keys and normal modifiers go through
return true;
} else {
// factor in mods
if (get_mods() & MOD_MASK_SHIFT) {
keycode = LSFT(keycode);
} else if (get_mods() & MOD_BIT(KC_RALT)) {
keycode = RALT(keycode);
}
enable_xcase_with(keycode);
return false;
}
return false;
} else {
if (IS_OSM(keycode)) {
// this catches the OSM release if no other key was pressed
set_oneshot_mods(0);
enable_xcase_with(keycode);
}
// let other special keys go through
return true;
}
}

if (caps_word_on || xcase_state) {
// Get the base keycode of a mod or layer tap key
switch (keycode) {
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
// Earlier return if this has not been considered tapped yet
if (record->tap.count == 0) return true;
keycode = keycode & 0xFF;
break;
default:
break;
}

if (record->event.pressed) {
// handle xcase mode
if (xcase_state == XCASE_ON) {
// place the delimiter if needed
if (keycode == XCASE_DELIMITER_KEY) {
place_delimiter();
return false;
}
} // end XCASE_ON

// check if the case modes have been terminated
if (terminate_case_modes(keycode, record)) {
disable_caps_word();
disable_xcase();
}
} // end if event.pressed

return true;
}
return true;
}
26 changes: 26 additions & 0 deletions keyboards/ferris/keymaps/treeman/casemodes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include QMK_KEYBOARD_H

// Check whether caps word is on
bool caps_word_enabled(void);
// Enable caps word
void enable_caps_word(void);
// Disable caps word
void disable_caps_word(void);
// Toggle caps word
void toggle_caps_word(void);

// Check whether xcase is on
bool xcase_enabled(void);
// Check whether xcase is waiting for a keypress
bool xcase_waiting(void);
// Enable xcase and pickup the next keystroke as the delimiter
void enable_xcase(void);
// Enable xcase with the specified delimiter
void enable_xcase_with(uint16_t delimiter);
// Disable xcase
void disable_xcase(void);

// Function to be put in process user
bool process_case_modes(uint16_t keycode, const keyrecord_t *record);
109 changes: 109 additions & 0 deletions keyboards/ferris/keymaps/treeman/combos.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Left top row
COMB(q_comb, SE_Q, SE_J, SE_C)
COMB(qu_comb, QU, SE_C, SE_Y)
COMB(z_comb, SE_Z, SE_Y, SE_F)
COMB(ctrl_w, C(SE_W), SE_C, SE_Y, SE_F)
COMB(clear, CLEAR, SE_F, SE_P)
// Right top row
SUBS(el_str_int, "#{}"SS_TAP(X_LEFT), SE_X, SE_W)
COMB(eql, SE_EQL, SE_W, SE_O)
COMB(backsp, KC_BSPC, SE_O, SE_U)
COMB(ctrl_w2, C(SE_W), SE_W, SE_O, SE_U)
COMB(rev_rep, REV_REP, SE_U, SE_DOT)

// Left home row
COMB(ctrl_combo_l, OS_CTRL, SE_S, SE_T)
COMB(escape_sym, ESC_SYM, SE_T, SE_H)
COMB(dquo, SE_DQUO, SE_S, SE_H)
COMB(tab_mod, TAB_MOD, SE_S, SE_T, SE_H)
COMB(del, KC_DEL, SE_H, SE_K)
// Right home row
COMB(coln_sym, COLN_SYM, SE_N, SE_A)
COMB(ctrl_combo_r, OS_CTRL, SE_A, SE_I)
COMB(quot, SE_QUOT, SE_N, SE_I)
COMB(ent, KC_ENT, SE_N, SE_A, SE_I)
COMB(save_vim, SAVE_VIM, SE_N, SE_A, SE_I, REPEAT)
// Mixed
COMB(capsword, CAPSWORD, SE_T, SE_A)

// Left bottom row
COMB(vsp, VIM_VS, SE_V, SE_G)
COMB(gui_combo_l, OS_GUI, SE_G, SE_D)
COMB(lalt, MY_LALT, SE_V, SE_D)
COMB(close_win, CLOSE_WIN, SE_V, SE_G, SE_D)
COMB(shift_combo_l, KC_LSFT, SE_D, SE_B)
// Right bottom row
COMB(shift_combo_r, KC_RSFT, SE_SLSH, SE_L)
COMB(gui_combo_r, OS_GUI, SE_L, SE_LPRN)
COMB(dlr, SE_DLR, SE_LPRN, SE_RPRN)
COMB(leader, LEADER, SE_L, SE_RPRN)
COMB(swe, TG_SWE, SE_L, SE_LPRN, SE_RPRN)

// Mixed home row + other
COMB(scln, SE_SCLN, SE_T, SE_D)
COMB(win_alt, WIN_ALT, SE_L, SE_A)

// Vertical top left
SUBS(small_left_arrow, "<-", SE_Y, SE_T)
SUBS(lt_eq, "<=", SE_F, SE_H)
// Vertical top right
SUBS(large_right_arrow, "=>", SE_W, SE_N)
SUBS(small_right_arrow, "->", SE_O, SE_A)
SUBS(pipe_to, "|>", SE_U, SE_I)
// Vertical bottom left
COMB(sp, VIM_SP, SE_S, SE_V)
SUBS(gt_eq, ">=", SE_H, SE_D)

// Thumbs
COMB(num, NUMWORD, MT_SPC, SE_E)

// Numbers with cross side thumb
COMB(comb_6, SE_6, SE_E, SE_R)
COMB(comb_4, SE_4, SE_E, SE_S)
COMB(comb_0, SE_0, SE_E, SE_T)
COMB(comb_2, SE_2, SE_E, SE_H)
COMB(comb_8, SE_8, SE_E, SE_D)
COMB(comb_9, SE_9, MT_SPC, SE_L)
COMB(comb_3, SE_3, MT_SPC, SE_N)
COMB(comb_1, SE_1, MT_SPC, SE_A)
COMB(comb_5, SE_5, MT_SPC, SE_I)
COMB(comb_7, SE_7, MT_SPC, REPEAT)
// é
COMB(comb_e_acut, E_ACUT, SE_E, SE_K)

// Symbols with same side thumb
// Upper
COMB(comb_tild, TILD, MT_SPC, SE_J)
COMB(comb_plus, SE_PLUS, MT_SPC, SE_C)
COMB(comb_astr, SE_ASTR, MT_SPC, SE_Y)
COMB(comb_exlm, SE_EXLM, MT_SPC, SE_F)
COMB(comb_hash, SE_HASH, SE_E, SE_W)
COMB(comb_at, SE_AT, SE_E, SE_O)
COMB(comb_circ, CIRC, SE_E, SE_U)
// Home-row
COMB(comb_pipe, SE_PIPE, MT_SPC, SE_R)
COMB(comb_lcbr, SE_LCBR, MT_SPC, SE_S)
COMB(comb_rcbr, SE_RCBR, MT_SPC, SE_T)
COMB(comb_mins, SE_MINS, MT_SPC, SE_H)
COMB(comb_bsls, SE_BSLS, MT_SPC, SE_K)
COMB(comb_grv, GRV, SE_E, SE_M)
COMB(comb_ques, SE_QUES, SE_E, SE_N)
COMB(comb_lbrc, SE_LBRC, SE_E, SE_A)
COMB(comb_rbrc, SE_RBRC, SE_E, SE_I)
// Lower
COMB(comb_labk, SE_LABK, MT_SPC, SE_V)
COMB(comb_rabk, SE_RABK, MT_SPC, SE_G)
COMB(comb_perc, SE_PERC, MT_SPC, SE_D)
COMB(comb_ampr, SE_AMPR, SE_E, SE_L)
COMB(comb_lprn, SE_LPRN, SE_E, SE_LPRN)
COMB(comb_rprn, SE_RPRN, SE_E, SE_RPRN)
COMB(comb_unds, SE_UNDS, SE_E, SE_UNDS)

// Swedish letters with cross side thumb
COMB(arng, SE_ARNG, MT_SPC, SE_LPRN)
COMB(adia, SE_ADIA, MT_SPC, SE_RPRN)
COMB(odia, SE_ODIA, MT_SPC, SE_UNDS)

// Specials
SUBS(https, "https://", MT_SPC, SE_SLSH)
COMB(ctrl_gui_space, C(G(KC_SPACE)), MT_SPC, SE_N, SE_A)
Loading

0 comments on commit 73fff1c

Please sign in to comment.