efl

Форк
0
/
ecore_imf_example.c 
661 строка · 18.5 Кб
1
/**
2
 * Ecore example illustrating how to use ecore imf.
3
 *
4
 * @verbatim
5
 * gcc -o ecore_imf_example ecore_imf_example.c `pkg-config --cflags --libs ecore evas eina ecore-evas ecore-imf ecore-imf-evas`
6
 * @endverbatim
7
 */
8

9
#include <Ecore.h>
10
#include <Ecore_Evas.h>
11
#include <Ecore_IMF.h>
12
#include <Ecore_IMF_Evas.h>
13
#include <Evas.h>
14
#include <stdio.h>
15

16
#define WIDTH 480
17
#define HEIGHT 800
18

19
typedef struct _Entry Entry;
20

21
struct _Entry
22
{
23
   Evas_Object           *rect;
24
   Evas_Object           *txt_obj;
25
   Evas_Textblock_Style  *txt_style;
26
   Evas_Textblock_Cursor *cursor;
27
   Evas_Textblock_Cursor *preedit_start;
28
   Evas_Textblock_Cursor *preedit_end;
29
   Ecore_IMF_Context     *imf_context;
30
   Eina_Bool              have_preedit : 1;
31
};
32

33
static void _imf_cursor_info_set(Entry *en);
34

35
static void
36
_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
37
{
38
   Entry *en = data;
39
   Evas_Event_Mouse_Down *ev = event_info;
40
   if (!en) return;
41

42
   if (en->imf_context)
43
     {
44
        Ecore_IMF_Event_Mouse_Down ecore_ev;
45
        ecore_imf_evas_event_mouse_down_wrap(ev, &ecore_ev);
46
        if (ecore_imf_context_filter_event(en->imf_context,
47
                                           ECORE_IMF_EVENT_MOUSE_DOWN,
48
                                           (Ecore_IMF_Event *)&ecore_ev))
49
          return;
50

51
        // ecore_imf_context_reset should be called before calculating new cursor position
52
        ecore_imf_context_reset(en->imf_context);
53
     }
54

55
   // calculate new cursor position
56
}
57

58
static void
59
_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
60
{
61
   Entry *en = data;
62
   Evas_Event_Mouse_Up *ev = event_info;
63
   if (!en) return;
64

65
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
66
     {
67
        _imf_cursor_info_set(en);
68
        return;
69
     }
70

71
   if (en->imf_context)
72
     {
73
        Ecore_IMF_Event_Mouse_Up ecore_ev;
74
        ecore_imf_evas_event_mouse_up_wrap(ev, &ecore_ev);
75
        if (ecore_imf_context_filter_event(en->imf_context,
76
                                           ECORE_IMF_EVENT_MOUSE_UP,
77
                                           (Ecore_IMF_Event *)&ecore_ev))
78
          return;
79
     }
80

81
   if (en->rect)
82
     {
83
        if (evas_object_focus_get(en->rect))
84
          {
85
             // notify cursor information
86
             _imf_cursor_info_set(en);
87
          }
88
        else
89
          evas_object_focus_set(en->rect, EINA_TRUE);
90
     }
91
}
92

93
static void
94
_entry_focus_in_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED)
95
{
96
   Entry *en = data;
97
   if (!en) return;
98

99
   if (en->imf_context)
100
     ecore_imf_context_focus_in(en->imf_context);
101

102
   // notify the cursor information
103
   _imf_cursor_info_set(en);
104
}
105

106
static void
107
_entry_focus_out_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED)
108
{
109
   Entry *en = data;
110
   if (!en) return;
111

112
   if (en->imf_context)
113
     {
114
        // ecore_imf_context_reset should be called for flushing the preedit string in focus-out event handler
115
        ecore_imf_context_reset(en->imf_context);
116
        ecore_imf_context_focus_out(en->imf_context);
117
     }
118
}
119

120
static void
121
_canvas_focus_in_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED)
122
{
123
   Entry *en;
124
   Evas_Object *obj = evas_focus_get(e);
125
   if (!obj) return;
126

127
   en = evas_object_data_get(obj, "Entry");
128
   if (en)
129
     _entry_focus_in_cb(en, NULL, NULL, NULL);
130
}
131

132
static void
133
_canvas_focus_out_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED)
134
{
135
   Entry *en;
136
   Evas_Object *obj = evas_focus_get(e);
137
   if (!obj) return;
138

139
   en = evas_object_data_get(obj, "Entry");
140
   if (en)
141
     _entry_focus_out_cb(en, NULL, NULL, NULL);
142
}
143

144
static void
145
_imf_cursor_info_set(Entry *en)
146
{
147
   Evas_Coord x, y, w, h;
148
   Evas_Coord cx, cy, cw, ch; // cursor geometry
149
   int cursor_pos; // cursor position in chars (Not bytes)
150
   Evas_BiDi_Direction dir;
151

152
   if (!en) return;
153

154
   // get cursor geometry
155
   if (en->txt_obj)
156
     evas_object_geometry_get(en->txt_obj, &x, &y, &w, &h);
157

158
   if (en->cursor && en->imf_context)
159
     {
160
        evas_textblock_cursor_geometry_get(en->cursor, &cx, &cy, &cw, &ch, &dir, EVAS_TEXTBLOCK_CURSOR_BEFORE);
161

162
        // get cursor position
163
        cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
164

165
        ecore_imf_context_cursor_position_set(en->imf_context, cursor_pos);
166
        ecore_imf_context_cursor_location_set(en->imf_context, x + cx, y + cy, cw, ch);
167
        ecore_imf_context_bidi_direction_set(en->imf_context, (Ecore_IMF_BiDi_Direction)dir);
168
     }
169
}
170

171
static void
172
_preedit_del(Entry *en)
173
{
174
   if (!en || !en->have_preedit) return;
175
   if (!en->preedit_start || !en->preedit_end) return;
176
   if (!evas_textblock_cursor_compare(en->preedit_start, en->preedit_end)) return;
177

178
   // delete the preedit characters
179
   evas_textblock_cursor_range_delete(en->preedit_start, en->preedit_end);
180
}
181

182
static void
183
_preedit_clear(Entry *en)
184
{
185
   if (en->preedit_start)
186
     {
187
        evas_textblock_cursor_free(en->preedit_start);
188
        en->preedit_start = NULL;
189
     }
190

191
   if (en->preedit_end)
192
     {
193
        evas_textblock_cursor_free(en->preedit_end);
194
        en->preedit_end = NULL;
195
     }
196

197
   en->have_preedit = EINA_FALSE;
198
}
199

200
static Eina_Bool
201
_ecore_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, char **text, int *cursor_pos)
202
{
203
   // This callback will be called when the Input Method Context module requests the surrounding context.
204
   Entry *en = data;
205
   const char *str;
206

207
   if (!en) return EINA_FALSE;
208

209
   str = evas_object_textblock_text_markup_get(en->txt_obj);
210

211
   if (text)
212
     *text = str ? strdup(str) : strdup("");
213

214
   // get the current position of cursor
215
   if (cursor_pos && en->cursor)
216
     *cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
217

218
   return EINA_TRUE;
219
}
220

221
static void
222
_ecore_imf_event_delete_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
223
{
224
   // called when the input method needs to delete all or part of the context surrounding the cursor
225
   Entry *en = data;
226
   Ecore_IMF_Event_Delete_Surrounding *ev = event_info;
227
   Evas_Textblock_Cursor *del_start, *del_end;
228
   int cursor_pos;
229

230
   if ((!en) || (!ev) || (!en->cursor)) return;
231

232
   // get the current cursor position
233
   cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
234

235
   // start cursor position to be deleted
236
   del_start = evas_object_textblock_cursor_new(en->txt_obj);
237
   evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset);
238

239
   // end cursor position to be deleted
240
   del_end = evas_object_textblock_cursor_new(en->txt_obj);
241
   evas_textblock_cursor_pos_set(del_end, cursor_pos + ev->offset + ev->n_chars);
242

243
   // implement function to delete character(s) from 'cursor_pos+ev->offset' cursor position to 'cursor_pos + ev->offset + ev->n_chars'
244
   evas_textblock_cursor_range_delete(del_start, del_end);
245

246
   evas_textblock_cursor_free(del_start);
247
   evas_textblock_cursor_free(del_end);
248
}
249

250
static void
251
_ecore_imf_event_commit_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
252
{
253
   Entry *en = data;
254
   char *commit_str = (char *)event_info;
255
   if (!en) return;
256

257
   // delete preedit string
258
   _preedit_del(en);
259
   _preedit_clear(en);
260

261
   printf("commit string : %s\n", commit_str);
262

263
   // insert the commit string in the editor
264
   if (en->cursor && commit_str)
265
     evas_object_textblock_text_markup_prepend(en->cursor, commit_str);
266

267
   // notify the cursor information
268
   _imf_cursor_info_set(en);
269

270
   return;
271
}
272

273
static void
274
_ecore_imf_event_preedit_changed_cb(void *data, Ecore_IMF_Context *ctx, void *event_info EINA_UNUSED)
275
{
276
   // example how to get preedit string
277
   Entry *en = data;
278
   char *preedit_string;
279
   int cursor_pos;
280
   Eina_List *attrs = NULL;
281
   Eina_List *l;
282
   Ecore_IMF_Preedit_Attr *attr;
283
   Ecore_IMF_Context *imf_context = ctx;
284
   int preedit_start_pos, preedit_end_pos;
285
   int i;
286
   Eina_Bool preedit_end_state = EINA_FALSE;
287

288
   if (!en || !en->cursor) return;
289

290
   // get preedit string and attributes
291
   ecore_imf_context_preedit_string_with_attributes_get(imf_context, &preedit_string, &attrs, &cursor_pos);
292
   printf("preedit string : %s\n", preedit_string);
293

294
   if (!strcmp(preedit_string, ""))
295
     preedit_end_state = EINA_TRUE;
296

297
   // delete preedit
298
   _preedit_del(en);
299

300
   preedit_start_pos = evas_textblock_cursor_pos_get(en->cursor);
301

302
   // insert preedit character(s)
303
   if (strlen(preedit_string) > 0)
304
     {
305
        if (attrs)
306
          {
307
             EINA_LIST_FOREACH(attrs, l, attr)
308
               {
309
                  if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB1) // style type
310
                    {
311
                       // apply appropriate style such as underline
312
                    }
313
                  else if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB2 || attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3)
314
                    {
315
                       // apply appropriate style such as underline
316
                    }
317
               }
318

319
             // insert code to display preedit string in your editor
320
             evas_object_textblock_text_markup_prepend(en->cursor, preedit_string);
321
          }
322
     }
323

324
   if (!preedit_end_state)
325
     {
326
        // set preedit start cursor
327
        if (!en->preedit_start)
328
          en->preedit_start = evas_object_textblock_cursor_new(en->txt_obj);
329
        evas_textblock_cursor_copy(en->cursor, en->preedit_start);
330

331
        // set preedit end cursor
332
        if (!en->preedit_end)
333
          en->preedit_end = evas_object_textblock_cursor_new(en->txt_obj);
334
        evas_textblock_cursor_copy(en->cursor, en->preedit_end);
335

336
        preedit_end_pos = evas_textblock_cursor_pos_get(en->cursor);
337

338
        for (i = 0; i < (preedit_end_pos - preedit_start_pos); i++)
339
          {
340
             evas_textblock_cursor_char_prev(en->preedit_start);
341
          }
342

343
        en->have_preedit = EINA_TRUE;
344

345
        // set cursor position
346
        evas_textblock_cursor_pos_set(en->cursor, preedit_start_pos + cursor_pos);
347
     }
348

349
   // notify the cursor information
350
   _imf_cursor_info_set(en);
351

352
   EINA_LIST_FREE(attrs, attr)
353
     free(attr);
354

355
   free(preedit_string);
356
}
357

358
static void
359
_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
360
{
361
   Entry *en = data;
362
   Evas_Event_Key_Down *ev = event_info;
363
   Eina_Bool control, alt, shift;
364
   if ((!en) || (!ev->key) || (!en->cursor)) return;
365

366
   if (en->imf_context)
367
     {
368
        Ecore_IMF_Event_Key_Down ecore_ev;
369
        ecore_imf_evas_event_key_down_wrap(ev, &ecore_ev);
370
        if (ecore_imf_context_filter_event(en->imf_context,
371
                                           ECORE_IMF_EVENT_KEY_DOWN,
372
                                           (Ecore_IMF_Event *)&ecore_ev))
373
          return;
374
     }
375

376
   control = evas_key_modifier_is_set(ev->modifiers, "Control");
377
   alt = evas_key_modifier_is_set(ev->modifiers, "Alt");
378
   shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
379
   (void)alt;
380
   (void)shift;
381

382
   if (!strcmp(ev->key, "BackSpace"))
383
     {
384
        if (evas_textblock_cursor_char_prev(en->cursor))
385
          {
386
             evas_textblock_cursor_char_delete(en->cursor);
387
             // notify the cursor information
388
             _imf_cursor_info_set(en);
389
          }
390
         return;
391
     }
392
   else if (!strcmp(ev->key, "Delete") ||
393
            (!strcmp(ev->key, "KP_Delete") && !ev->string))
394
     {
395
        // FILLME
396
     }
397
   else if ((control) && (!strcmp(ev->key, "v")))
398
     {
399
        // ctrl + v
400
        // FILLME
401
     }
402
   else if ((control) && (!strcmp(ev->key, "a")))
403
     {
404
        // ctrl + a
405
        // FILLME
406
     }
407
   else if ((control) && (!strcmp(ev->key, "A")))
408
     {
409
        // ctrl + A
410
        // FILLME
411
     }
412
   else if ((control) && ((!strcmp(ev->key, "c") || (!strcmp(ev->key, "Insert")))))
413
     {
414
        // ctrl + c
415
        // FILLME
416
     }
417
   else if ((control) && ((!strcmp(ev->key, "x") || (!strcmp(ev->key, "m")))))
418
     {
419
        // ctrl + x
420
        // FILLME
421
     }
422
   else if ((control) && (!strcmp(ev->key, "z")))
423
     {
424
        // ctrl + z (undo)
425
        // FILLME
426
     }
427
   else if ((control) && (!strcmp(ev->key, "y")))
428
     {
429
        // ctrl + y (redo)
430
        // FILLME
431
     }
432
   else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
433
     {
434
        // FILLME
435
     }
436
   else
437
     {
438
        if (ev->string)
439
          {
440
             printf("key down string : %s\n", ev->string);
441
             evas_object_textblock_text_markup_prepend(en->cursor, ev->string);
442
          }
443
     }
444

445
   // notify the cursor information
446
   _imf_cursor_info_set(en);
447
}
448

449
static void
450
_key_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
451
{
452
   Entry *en = data;
453
   Evas_Event_Key_Up *ev = event_info;
454

455
   if (!en) return;
456

457
   if (en->imf_context)
458
     {
459
        Ecore_IMF_Event_Key_Up ecore_ev;
460

461
        ecore_imf_evas_event_key_up_wrap(ev, &ecore_ev);
462
        if (ecore_imf_context_filter_event(en->imf_context,
463
                                           ECORE_IMF_EVENT_KEY_UP,
464
                                           (Ecore_IMF_Event *)&ecore_ev))
465
          return;
466
     }
467
}
468

469
static void
470
create_input_field(Evas *evas, Entry *en, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
471
{
472
   if (!en) return;
473

474
   en->have_preedit = EINA_FALSE;
475
   en->preedit_start = NULL;
476
   en->preedit_end = NULL;
477

478
   // create the background for text input field
479
   en->rect = evas_object_rectangle_add(evas);
480
   evas_object_color_set(en->rect, 150, 150, 150, 255); // gray color
481
   evas_object_move(en->rect, x, y);
482
   evas_object_resize(en->rect, w, h);
483
   evas_object_show(en->rect);
484
   evas_object_data_set(en->rect, "Entry", en);
485

486
   // create text object for displaying text
487
   en->txt_obj = evas_object_textblock_add(evas);
488
   evas_object_color_set(en->txt_obj, 0, 0, 0, 255);
489
   evas_object_pass_events_set(en->txt_obj, EINA_TRUE);
490
   evas_object_move(en->txt_obj, x, y);
491
   evas_object_resize(en->txt_obj, w, h);
492
   evas_object_show(en->txt_obj);
493

494
   // set style on textblock
495
   static const char *style_buf =
496
      "DEFAULT='font=Sans font_size=30 color=#000 text_class=entry'"
497
      "newline='br'"
498
      "b='+ font=Sans:style=bold'";
499
   en->txt_style = evas_textblock_style_new();
500
   evas_textblock_style_set(en->txt_style, style_buf);
501
   evas_object_textblock_style_set(en->txt_obj, en->txt_style);
502

503
   // create cursor
504
   en->cursor = evas_object_textblock_cursor_new(en->txt_obj);
505

506
   // create input context
507
   const char *default_id = ecore_imf_context_default_id_get();
508
   if (!default_id)
509
     {
510
        fprintf(stderr, "Can't create ecore_imf_context\n");
511
        return;
512
     }
513

514
   en->imf_context = ecore_imf_context_add(default_id);
515
   ecore_imf_context_client_canvas_set(en->imf_context, evas);
516

517
   // register key event handler
518
   evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, en);
519
   evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_UP, _key_up_cb, en);
520

521
   // register mouse event handler
522
   evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, en);
523
   evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en);
524

525
   // register focus event handler
526
   evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_IN, _entry_focus_in_cb, en);
527
   evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_OUT, _entry_focus_out_cb, en);
528

529
   // register retrieve surrounding callback
530
   ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context, _ecore_imf_retrieve_surrounding_cb, en);
531

532
   // register commit event callback
533
   ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _ecore_imf_event_commit_cb, en);
534

535
   // register preedit changed event handler
536
   ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _ecore_imf_event_preedit_changed_cb, en);
537

538
   // register surrounding delete event callback
539
   ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _ecore_imf_event_delete_surrounding_cb, en);
540
}
541

542
static void
543
delete_input_field(Entry *en)
544
{
545
   if (!en) return;
546

547
   if (en->rect)
548
     {
549
        evas_object_del(en->rect);
550
        en->rect = NULL;
551
     }
552

553
   if (en->cursor)
554
     {
555
        evas_textblock_cursor_free(en->cursor);
556
        en->cursor = NULL;
557
     }
558

559
   if (en->preedit_start)
560
     {
561
        evas_textblock_cursor_free(en->preedit_start);
562
        en->preedit_start = NULL;
563
     }
564

565
   if (en->preedit_end)
566
     {
567
        evas_textblock_cursor_free(en->preedit_end);
568
        en->preedit_end = NULL;
569
     }
570

571
   if (en->txt_obj)
572
     {
573
        evas_object_del(en->txt_obj);
574
        en->txt_obj = NULL;
575
     }
576

577
   if (en->txt_style)
578
     {
579
        evas_textblock_style_free(en->txt_style);
580
        en->txt_style = NULL;
581
     }
582

583
   if (en->imf_context)
584
     {
585
        ecore_imf_context_del(en->imf_context);
586
        en->imf_context = NULL;
587
     }
588
}
589

590
int
591
main(void)
592
{
593
   Ecore_Evas *ee;
594
   Evas *evas;
595
   Entry en1, en2;
596

597
   if (!ecore_evas_init())
598
     {
599
        fprintf(stderr, "failed to call ecore_evas_init()\n");
600
        return EXIT_FAILURE;
601
     }
602

603
   ecore_imf_init();
604

605
   // create a new window, with size=WIDTHxHEIGHT and default engine
606
   ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
607

608
   if (!ee)
609
     {
610
        fprintf(stderr, "failed to call ecore_evas_new\n");
611
        return EXIT_FAILURE;
612
     }
613

614
   ecore_evas_show(ee);
615

616
   // get the canvas off just-created window
617
   evas = ecore_evas_get(ee);
618
   if (!evas)
619
     {
620
        fprintf(stderr, "failed to call ecore_evas_get\n");
621
        return EXIT_FAILURE;
622
     }
623

624
   // create input field rectangle
625
   Evas_Object *bg = evas_object_rectangle_add(evas);
626
   evas_object_move(bg, 0, 0);
627
   evas_object_resize(bg, WIDTH, HEIGHT);
628
   evas_object_color_set(bg, 255, 255, 255, 255);
629
   evas_object_show(bg);
630

631
   // register canvas focus in/out event handler
632
   evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
633
   evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
634

635
   memset(&en1, 0, sizeof(en1));
636
   memset(&en2, 0, sizeof(en2));
637

638
   // create input field 1
639
   create_input_field(evas, &en1, 40, 60, 400, 80);
640

641
   // create input field 2
642
   create_input_field(evas, &en2, 40, 180, 400, 80);
643

644
   // give focus to input field 1
645
   evas_object_focus_set(en1.rect, EINA_TRUE);
646

647
   ecore_main_loop_begin(); // begin mainloop
648

649
   delete_input_field(&en1); // delete input field 1
650
   delete_input_field(&en2); // delete input field 2
651

652
   evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
653
   evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
654

655
   ecore_evas_free(ee);
656

657
   ecore_imf_shutdown();
658
   ecore_evas_shutdown();
659

660
   return 0;
661
}
662

663

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.