Patch by Matt Ebb: upgraded usablitiy of text button.
Textbuttons now allow a selection too (like any textbutton in other UIs). By default, on activating a textbutton, the entire button text is selected when you enter the button. A single arrowkey or LMB click reveils the cursor then. Here's more user notes: LMB click: If inside the button, places the text cursor at the clicked position. If outside the button, confirms/finishes editing LMB drag: Selects the text between the start and end point of the drag. Backspace: Deletes selected text, or backspaces a character Shift Backspace: Deletes all, as before. Delete: Deletes selected text or forward deletes a character Shift LeftArrow: Extends the selection left Shift RightArrow: Extends the selection right LeftArrow: If there's a selection, move the cursor to the left edge of the selection, otherwise move the cursor left a character. RightArrow: If there's a selection, move the cursor to the right edge of the selection, otherwise move the cursor right a character. UpArrow/Home: Move the cursor to the beginning of the line DownArrow/End: Move the cursor to the end of the line Ctrl Left arrow and Ctrl Right arrow to jump between directory separators
This commit is contained in:
@@ -409,6 +409,8 @@ enum {
|
||||
TH_REDALERT,
|
||||
TH_CUSTOM,
|
||||
|
||||
TH_BUT_TEXTFIELD_HI,
|
||||
|
||||
TH_THEMEUI,
|
||||
// common colors among spaces
|
||||
|
||||
|
||||
@@ -63,7 +63,11 @@
|
||||
#define PNL_TABBED 8
|
||||
#define PNL_OVERLAP 16
|
||||
|
||||
|
||||
/* Button text selection:
|
||||
* extension direction, selextend, inside ui_do_but_TEX */
|
||||
#define EXTEND_LEFT 1
|
||||
#define EXTEND_RIGHT 2
|
||||
#define SELWIDTH (but->selend - but->selsta)
|
||||
|
||||
typedef struct {
|
||||
short xim, yim;
|
||||
@@ -103,7 +107,7 @@ typedef struct {
|
||||
|
||||
struct uiBut {
|
||||
uiBut *next, *prev;
|
||||
short type, pointype, bit, bitnr, retval, strwidth, ofs, pos;
|
||||
short type, pointype, bit, bitnr, retval, strwidth, ofs, pos, selsta, selend;
|
||||
int flag;
|
||||
|
||||
char *str;
|
||||
|
||||
@@ -48,6 +48,7 @@ typedef struct ThemeUI {
|
||||
char setting2[4];
|
||||
char num[4];
|
||||
char textfield[4];
|
||||
char textfield_hi[4];
|
||||
char popup[4];
|
||||
char text[4];
|
||||
char text_hi[4];
|
||||
@@ -57,9 +58,8 @@ typedef struct ThemeUI {
|
||||
char menu_text[4];
|
||||
char menu_text_hi[4];
|
||||
|
||||
char but_drawtype, pad;
|
||||
short pad1;
|
||||
int pad2;
|
||||
char but_drawtype;
|
||||
char pad1[3];
|
||||
|
||||
} ThemeUI;
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ class Theme:
|
||||
@ivar setting2: theme rgba var.
|
||||
@ivar num: theme rgba var.
|
||||
@ivar textfield: theme rgba var.
|
||||
@ivar textfield_hi: theme rgba var.
|
||||
@ivar popup: theme rgba var.
|
||||
@ivar text: theme rgba var.
|
||||
@ivar text_hi: theme rgba var.
|
||||
|
||||
@@ -361,6 +361,7 @@ static PyObject *ThemeUI_getAttr( BPy_ThemeUI * self, char *name )
|
||||
ELSEIF_TUI_RGBA( setting2 )
|
||||
ELSEIF_TUI_RGBA( num )
|
||||
ELSEIF_TUI_RGBA( textfield )
|
||||
ELSEIF_TUI_RGBA( textfield_hi )
|
||||
ELSEIF_TUI_RGBA( popup )
|
||||
ELSEIF_TUI_RGBA( text )
|
||||
ELSEIF_TUI_RGBA( text_hi )
|
||||
@@ -375,7 +376,7 @@ static PyObject *ThemeUI_getAttr( BPy_ThemeUI * self, char *name )
|
||||
attrib = Py_BuildValue( "[ssssssssssssssssss]", "theme",
|
||||
"outline", "neutral", "action",
|
||||
"setting", "setting1", "setting2",
|
||||
"num", "textfield", "popup", "text",
|
||||
"num", "textfield", "textfield_hi", "popup", "text",
|
||||
"text_hi", "menu_back", "menu_item",
|
||||
"menu_hilite", "menu_text",
|
||||
"menu_text_hi", "drawType" );
|
||||
@@ -401,6 +402,7 @@ static int ThemeUI_setAttr( BPy_ThemeUI * self, char *name, PyObject * value )
|
||||
ELSEIF_TUI_RGBA( setting2 )
|
||||
ELSEIF_TUI_RGBA( num )
|
||||
ELSEIF_TUI_RGBA( textfield )
|
||||
ELSEIF_TUI_RGBA( textfield_hi )
|
||||
ELSEIF_TUI_RGBA( popup )
|
||||
ELSEIF_TUI_RGBA( text )
|
||||
ELSEIF_TUI_RGBA( text_hi )
|
||||
|
||||
@@ -1283,6 +1283,51 @@ static uiBut *ui_but_last(uiBlock *block)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* ************* IN-BUTTON TEXT SELECTION/EDITING ************* */
|
||||
|
||||
static short ui_delete_selection_edittext(uiBut *but)
|
||||
{
|
||||
int x;
|
||||
short deletedwidth=0;
|
||||
char *str;
|
||||
|
||||
str= (char *)but->poin;
|
||||
|
||||
deletedwidth = SELWIDTH;
|
||||
|
||||
for(x=0; x< strlen(str); x++) {
|
||||
if (but->selend + x <= strlen(str) ) {
|
||||
str[but->selsta + x]= str[but->selend + x];
|
||||
} else {
|
||||
str[but->selsta + x]= '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
but->pos = but->selend = but->selsta;
|
||||
|
||||
return deletedwidth;
|
||||
}
|
||||
|
||||
static void ui_set_cursor_pos_edittext(uiBut *but, short sx)
|
||||
{
|
||||
char backstr[UI_MAX_DRAW_STR];
|
||||
|
||||
BLI_strncpy(backstr, but->drawstr, UI_MAX_DRAW_STR);
|
||||
but->pos= strlen(backstr)-but->ofs;
|
||||
|
||||
while((but->aspect*BIF_GetStringWidth(but->font, backstr+but->ofs, 0) + but->x1) > sx) {
|
||||
if (but->pos <= 0) break;
|
||||
but->pos--;
|
||||
backstr[but->pos+but->ofs] = 0;
|
||||
}
|
||||
|
||||
but->pos -= strlen(but->str);
|
||||
but->pos += but->ofs;
|
||||
if(but->pos<0) but->pos= 0;
|
||||
}
|
||||
|
||||
|
||||
/* ************* EVENTS ************* */
|
||||
|
||||
void uiGetMouse(int win, short *adr)
|
||||
@@ -1495,9 +1540,9 @@ static int ui_do_but_ROW(uiBlock *block, uiBut *but)
|
||||
static int ui_do_but_TEX(uiBut *but)
|
||||
{
|
||||
unsigned short dev;
|
||||
short x, mval[2], len=0, dodraw;
|
||||
short x, mval[2], len=0, dodraw, selextend=0;
|
||||
char *str, backstr[UI_MAX_DRAW_STR];
|
||||
short capturing;
|
||||
short capturing, sx, sy, prevx;
|
||||
|
||||
str= (char *)but->poin;
|
||||
|
||||
@@ -1505,19 +1550,13 @@ static int ui_do_but_TEX(uiBut *but)
|
||||
|
||||
uiGetMouse(mywinget(), mval);
|
||||
|
||||
/* calculate cursor pos with current mousecoords */
|
||||
BLI_strncpy(backstr, but->drawstr, UI_MAX_DRAW_STR);
|
||||
but->pos= strlen(backstr)-but->ofs;
|
||||
|
||||
while((but->aspect*BIF_GetStringWidth(but->font, backstr+but->ofs, 0) + but->x1) > mval[0]) {
|
||||
if (but->pos <= 0) break;
|
||||
but->pos--;
|
||||
backstr[but->pos+but->ofs] = 0;
|
||||
}
|
||||
/* set cursor pos to the end of the text */
|
||||
but->pos = strlen(but->str);
|
||||
|
||||
but->pos -= strlen(but->str);
|
||||
but->pos += but->ofs;
|
||||
if(but->pos<0) but->pos= 0;
|
||||
but->selsta = 0;
|
||||
but->selend = strlen(but->drawstr) - strlen(but->str);
|
||||
|
||||
/* backup */
|
||||
BLI_strncpy(backstr, but->poin, UI_MAX_DRAW_STR);
|
||||
@@ -1538,13 +1577,53 @@ static int ui_do_but_TEX(uiBut *but)
|
||||
dev = extern_qread_ext(&val, &ascii);
|
||||
|
||||
if(dev==INPUTCHANGE) break;
|
||||
else if(get_mbut() & L_MOUSE) break;
|
||||
else if(get_mbut() & R_MOUSE) break;
|
||||
else if(get_mbut() & L_MOUSE) {
|
||||
uiGetMouse(mywinget(), mval);
|
||||
sx = mval[0]; sy = mval[1];
|
||||
|
||||
if ((but->y1 <= sy) && (sy <= but->y2) && (but->x1 <= sx) && (sx <= but->x2)) {
|
||||
ui_set_cursor_pos_edittext(but, mval[0]);
|
||||
|
||||
but->selsta = but->selend = but->pos;
|
||||
|
||||
/* drag text select */
|
||||
prevx= mval[0];
|
||||
while (get_mbut() & L_MOUSE) {
|
||||
uiGetMouse(mywinget(), mval);
|
||||
|
||||
if(prevx!=mval[0]) {
|
||||
|
||||
if (mval[0] > sx) selextend = EXTEND_RIGHT;
|
||||
else if (mval[0] < sx) selextend = EXTEND_LEFT;
|
||||
|
||||
ui_set_cursor_pos_edittext(but, mval[0]);
|
||||
|
||||
if (selextend == EXTEND_RIGHT) but->selend = but->pos;
|
||||
if (selextend == EXTEND_LEFT) but->selsta = but->pos;
|
||||
|
||||
ui_check_but(but);
|
||||
ui_draw_but(but);
|
||||
ui_block_flush_back(but->block);
|
||||
}
|
||||
PIL_sleep_ms(10);
|
||||
}
|
||||
dodraw= 1;
|
||||
} else break;
|
||||
}
|
||||
else if(dev==ESCKEY) break;
|
||||
else if(dev==MOUSEX) val= 0;
|
||||
else if(dev==MOUSEY) val= 0;
|
||||
|
||||
if(ascii) {
|
||||
if(len <= but->max) {
|
||||
|
||||
/* type over the current selection */
|
||||
if (SELWIDTH > 0) {
|
||||
len -= ui_delete_selection_edittext(but);
|
||||
}
|
||||
|
||||
/* add ascii characters */
|
||||
if(len < but->max) {
|
||||
for(x= but->max; x>but->pos; x--)
|
||||
str[x]= str[x-1];
|
||||
@@ -1555,30 +1634,131 @@ static int ui_do_but_TEX(uiBut *but)
|
||||
dodraw= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(val) {
|
||||
|
||||
switch (dev) {
|
||||
|
||||
case RIGHTARROWKEY:
|
||||
if(G.qual & LR_SHIFTKEY) but->pos= strlen(str);
|
||||
else but->pos++;
|
||||
/* if there's a selection */
|
||||
if (SELWIDTH > 0) {
|
||||
/* extend the selection based on the first direction taken */
|
||||
if(G.qual & LR_SHIFTKEY) {
|
||||
if (!selextend) {
|
||||
selextend = EXTEND_RIGHT;
|
||||
}
|
||||
if (selextend == EXTEND_RIGHT) {
|
||||
but->selend++;
|
||||
if (but->selend > len) but->selend = len;
|
||||
} else if (selextend == EXTEND_LEFT) {
|
||||
but->selsta++;
|
||||
/* if the selection start has gone past the end,
|
||||
* flip them so they're in sync again */
|
||||
if (but->selsta == but->selend) {
|
||||
but->pos = but->selsta;
|
||||
selextend = EXTEND_RIGHT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
but->selsta = but->pos = but->selend;
|
||||
selextend = 0;
|
||||
}
|
||||
} else {
|
||||
if(G.qual & LR_SHIFTKEY) {
|
||||
/* make a selection, starting from the cursor position */
|
||||
but->selsta = but->pos;
|
||||
|
||||
but->pos++;
|
||||
if(but->pos>strlen(str)) but->pos= strlen(str);
|
||||
|
||||
but->selend = but->pos;
|
||||
} else if(G.qual & LR_CTRLKEY) {
|
||||
while(but->pos < len){
|
||||
but->pos++;
|
||||
#ifdef WIN32
|
||||
if(str[but->pos]=='\\') break;
|
||||
#else
|
||||
if(str[but->pos]=='/') break;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
but->pos++;
|
||||
if(but->pos>strlen(str)) but->pos= strlen(str);
|
||||
}
|
||||
}
|
||||
dodraw= 1;
|
||||
break;
|
||||
|
||||
case LEFTARROWKEY:
|
||||
if(G.qual & LR_SHIFTKEY) but->pos= 0;
|
||||
else if(but->pos>0) but->pos--;
|
||||
/* if there's a selection */
|
||||
if (SELWIDTH > 0) {
|
||||
/* extend the selection based on the first direction taken */
|
||||
if(G.qual & LR_SHIFTKEY) {
|
||||
if (!selextend) {
|
||||
selextend = EXTEND_LEFT;
|
||||
}
|
||||
if (selextend == EXTEND_LEFT) {
|
||||
but->selsta--;
|
||||
if (but->selsta < 0) but->selsta = 0;
|
||||
} else if (selextend == EXTEND_RIGHT) {
|
||||
but->selend--;
|
||||
/* if the selection start has gone past the end,
|
||||
* flip them so they're in sync again */
|
||||
if (but->selsta == but->selend) {
|
||||
but->pos = but->selsta;
|
||||
selextend = EXTEND_LEFT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
but->pos = but->selend = but->selsta;
|
||||
selextend = 0;
|
||||
}
|
||||
} else {
|
||||
if(G.qual & LR_SHIFTKEY) {
|
||||
/* make a selection, starting from the cursor position */
|
||||
but->selend = but->pos;
|
||||
|
||||
but->pos--;
|
||||
if(but->pos<0) but->pos= 0;
|
||||
|
||||
but->selsta = but->pos;
|
||||
} else if(G.qual & LR_CTRLKEY) {
|
||||
while(but->pos > 0){
|
||||
but->pos--;
|
||||
#ifdef WIN32
|
||||
if(str[but->pos]=='\\') break;
|
||||
#else
|
||||
if(str[but->pos]=='/') break;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if(but->pos>0) but->pos--;
|
||||
}
|
||||
}
|
||||
dodraw= 1;
|
||||
break;
|
||||
|
||||
case DOWNARROWKEY:
|
||||
case ENDKEY:
|
||||
but->pos= strlen(str);
|
||||
if(G.qual & LR_SHIFTKEY) {
|
||||
but->selsta = but->pos;
|
||||
but->selend = strlen(str);
|
||||
selextend = EXTEND_RIGHT;
|
||||
} else {
|
||||
but->selsta = but->selend = but->pos= strlen(str);
|
||||
}
|
||||
dodraw= 1;
|
||||
break;
|
||||
|
||||
case UPARROWKEY:
|
||||
case HOMEKEY:
|
||||
but->pos= 0;
|
||||
if(G.qual & LR_SHIFTKEY) {
|
||||
but->selend = but->pos;
|
||||
but->selsta = 0;
|
||||
selextend = EXTEND_LEFT;
|
||||
} else {
|
||||
but->selsta = but->selend = but->pos= 0;
|
||||
}
|
||||
dodraw= 1;
|
||||
break;
|
||||
|
||||
@@ -1588,7 +1768,13 @@ static int ui_do_but_TEX(uiBut *but)
|
||||
break;
|
||||
|
||||
case DELKEY:
|
||||
if(but->pos>=0 && but->pos<strlen(str)) {
|
||||
if (SELWIDTH > 0) {
|
||||
len -= ui_delete_selection_edittext(but);
|
||||
|
||||
if (len < 0) len = 0;
|
||||
dodraw=1;
|
||||
}
|
||||
else if(but->pos>=0 && but->pos<strlen(str)) {
|
||||
for(x=but->pos; x<=strlen(str); x++)
|
||||
str[x]= str[x+1];
|
||||
str[--len]='\0';
|
||||
@@ -1598,7 +1784,13 @@ static int ui_do_but_TEX(uiBut *but)
|
||||
|
||||
case BACKSPACEKEY:
|
||||
if(len!=0) {
|
||||
if(get_qual() & LR_SHIFTKEY) {
|
||||
if (SELWIDTH > 0) {
|
||||
len -= ui_delete_selection_edittext(but);
|
||||
|
||||
if (len < 0) len = 0;
|
||||
dodraw=1;
|
||||
}
|
||||
else if(get_qual() & LR_SHIFTKEY) {
|
||||
str[0]= 0;
|
||||
but->pos= 0;
|
||||
len= 0;
|
||||
@@ -1613,6 +1805,7 @@ static int ui_do_but_TEX(uiBut *but)
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TABKEY:
|
||||
if(but->autocomplete_func) {
|
||||
but->autocomplete_func(str, but->autofunc_arg);
|
||||
@@ -5448,7 +5641,7 @@ short pupmenu(char *instr)
|
||||
}
|
||||
}
|
||||
|
||||
uiBoundsBlock(block, 2);
|
||||
uiBoundsBlock(block, 1);
|
||||
|
||||
event= uiDoBlocks(&listb, 0);
|
||||
|
||||
@@ -5596,7 +5789,7 @@ short pupmenu_col(char *instr, int maxrow)
|
||||
//uiDefButI(block, BUTM, B_NOP, md->items[a].str, x1, y1, (short)(width-(rows>1)), (short)(boxh-1), &val, (float)md->items[a].retval, 0.0, 0, 0, "");
|
||||
}
|
||||
|
||||
uiBoundsBlock(block, 3);
|
||||
uiBoundsBlock(block, 1);
|
||||
|
||||
event= uiDoBlocks(&listb, 0);
|
||||
|
||||
|
||||
@@ -1452,6 +1452,8 @@ static void ui_draw_text_icon(uiBut *but)
|
||||
float x;
|
||||
int len;
|
||||
char *cpoin;
|
||||
short t, pos, ch;
|
||||
short selsta_tmp, selend_tmp, selsta_draw, selwidth_draw;
|
||||
|
||||
/* check for button text label */
|
||||
if (but->type == ICONTEXTROW) {
|
||||
@@ -1459,10 +1461,37 @@ static void ui_draw_text_icon(uiBut *but)
|
||||
}
|
||||
else {
|
||||
|
||||
// text button cursor
|
||||
/* text button selection and cursor */
|
||||
if(but->pos != -1) {
|
||||
short t, pos, ch;
|
||||
|
||||
if (SELWIDTH > 0) {
|
||||
/* text button selection */
|
||||
selsta_tmp = but->selsta + strlen(but->str);
|
||||
selend_tmp = but->selend + strlen(but->str);
|
||||
if (but->ofs >= strlen(but->str))
|
||||
selsta_tmp += (but->ofs - strlen(but->str));
|
||||
|
||||
if(but->drawstr[0]!=0) {
|
||||
ch= but->drawstr[selsta_tmp];
|
||||
but->drawstr[selsta_tmp]= 0;
|
||||
|
||||
selsta_draw = but->aspect*BIF_GetStringWidth(but->font, but->drawstr+but->ofs, (U.transopts & USER_TR_BUTTONS)) + 3;
|
||||
|
||||
but->drawstr[selsta_tmp]= ch;
|
||||
|
||||
|
||||
ch= but->drawstr[selend_tmp];
|
||||
but->drawstr[selend_tmp]= 0;
|
||||
|
||||
selwidth_draw = but->aspect*BIF_GetStringWidth(but->font, but->drawstr+but->ofs, (U.transopts & USER_TR_BUTTONS)) + 3;
|
||||
|
||||
but->drawstr[selend_tmp]= ch;
|
||||
|
||||
BIF_ThemeColor(TH_BUT_TEXTFIELD_HI);
|
||||
glRects(but->x1+selsta_draw+1, but->y1+2, but->x1+selwidth_draw+1, but->y2-2);
|
||||
}
|
||||
} else {
|
||||
/* text cursor */
|
||||
pos= but->pos+strlen(but->str);
|
||||
if(pos >= but->ofs) {
|
||||
if(but->drawstr[0]!=0) {
|
||||
@@ -1476,7 +1505,8 @@ static void ui_draw_text_icon(uiBut *but)
|
||||
else t= 3;
|
||||
|
||||
glColor3ub(255,0,0);
|
||||
glRects(but->x1+t, but->y1+2, but->x1+t+3, but->y2-2);
|
||||
glRects(but->x1+t, but->y1+2, but->x1+t+2, but->y2-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(but->drawstr[0]!=0) {
|
||||
|
||||
@@ -674,6 +674,8 @@ char *BIF_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
|
||||
cp= btheme->tui.num; break;
|
||||
case TH_BUT_TEXTFIELD:
|
||||
cp= btheme->tui.textfield; break;
|
||||
case TH_BUT_TEXTFIELD_HI:
|
||||
cp= btheme->tui.textfield_hi; break;
|
||||
case TH_BUT_POPUP:
|
||||
cp= btheme->tui.popup; break;
|
||||
case TH_BUT_TEXT:
|
||||
@@ -872,6 +874,7 @@ void BIF_InitTheme(void)
|
||||
SETCOL(btheme->tui.setting2, 0xA1,0x99,0xA7, 255);
|
||||
SETCOL(btheme->tui.num, 0x90,0x90,0x90, 255);
|
||||
SETCOL(btheme->tui.textfield, 0x90,0x90,0x90, 255);
|
||||
SETCOL(btheme->tui.textfield_hi,0xc6,0x77,0x77, 255);
|
||||
SETCOL(btheme->tui.popup, 0xA0,0xA0,0xA0, 255);
|
||||
|
||||
SETCOL(btheme->tui.text, 0,0,0, 255);
|
||||
@@ -1038,6 +1041,7 @@ char *BIF_ThemeColorsPup(int spacetype)
|
||||
sprintf(str, "Special Setting 2 %%x%d|", TH_BUT_SETTING2); strcat(cp, str);
|
||||
sprintf(str, "Number Input %%x%d|", TH_BUT_NUM); strcat(cp, str);
|
||||
sprintf(str, "Text Input %%x%d|", TH_BUT_TEXTFIELD); strcat(cp, str);
|
||||
sprintf(str, "Text Input Highlight %%x%d|", TH_BUT_TEXTFIELD_HI); strcat(cp, str);
|
||||
sprintf(str, "Popup %%x%d|", TH_BUT_POPUP); strcat(cp, str);
|
||||
sprintf(str, "Text %%x%d|", TH_BUT_TEXT); strcat(cp, str);
|
||||
sprintf(str, "Text Highlight %%x%d|", TH_BUT_TEXT_HI); strcat(cp, str);
|
||||
|
||||
@@ -259,7 +259,17 @@ static void init_userdef_file(void)
|
||||
/* TEMPORAL, remove me! (ton) */
|
||||
U.uiflag |= USER_PLAINMENUS;
|
||||
}
|
||||
|
||||
/* check for text field selection highlight, set it to text editor highlight by default */
|
||||
if(btheme->tui.textfield_hi[3]==0) {
|
||||
SETCOL(btheme->tui.textfield_hi,
|
||||
btheme->text.shade2[0],
|
||||
btheme->text.shade2[1],
|
||||
btheme->text.shade2[2],
|
||||
255);
|
||||
}
|
||||
}
|
||||
|
||||
if(U.obcenter_dia==0) U.obcenter_dia= 6;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user