LINUX.ORG.RU

[GtkEntry]Почему не устанавливается inner border?


0

1

День добрый, у своего контрола в событии expose_event пытаюсь установить inner border(контрол унаследован от GtkEntry), но при запуске, размеру контрола начинают резко увеличиваться, т.е контрол растёт в длину, пока программу не остановишь аварийно. Мне нужно чтобы бордер пересчитывался динамически, когда я например растягиваю контрол, т.к длина контрола растёт, то и бордер нужно передвинуть дальше к краю, но почему-то не получается :(


static gint 
gtk_date_picker_expose(GtkWidget * widget, GdkEventExpose *event)
{
...
//если растягиваем контрол, его длина растёт
gdk_window_get_size(window, &w, &h);
	
	picker->w = h+1;
	picker->h = h+1;
	picker->x = w-h-1;
	picker->y = -1;

...
// нужно сдвинуть бордер подальше к краю, иначе при растягивании он останется на прежнем месте, например по середине, но тут проблема
set_inner_border_offset(GTK_ENTRY(widget), picker->x-1);
...
}


// установка бордера(мне нужно устанавливать его только справа)

void 
set_inner_border_offset(GtkWidget * widget, int offset)
{
	GtkBorder * border 	= (GtkBorder *)gtk_entry_get_inner_border(GTK_ENTRY(widget)); 
	if (!border) {
		border 			= g_new(GtkBorder, 1); 
		border->top 	= 2;
		border->bottom 	= 2;
		border->left 	= 2;
		border->right 	= 2;
	}
	border->right = offset;
	border_offset = offset;	
	gtk_entry_set_inner_border(GTK_ENTRY(widget), border);
	g_free(border);
}

Any idea? Куда покопать?

★★★★★

но при запуске, размеру контрола начинают резко увеличиваться

Полагаю, gtk_entry_set_inner_border вызывает expose-event. Попробуй выставлять его в size-allocate.

baverman ★★★
()
Ответ на: комментарий от baverman

В size-allocate не получится, потому что мне нужно отслеживать изменение размера виджета, т.е например я его растянул, размер виджета изменился, соответственно бордер тоже должен измениться, т.е например мне нужно чтобы бордер был в 20 от правого края, как бы я виджет не растягивал. Если например установить бордер где-нибудь в другом месте, то при растягивании виджета, он останется на этом месте, а не сдвинется в след за правым краем виджета, вот в чём фишка :(

xterro ★★★★★
() автор топика
Ответ на: комментарий от baverman

Хмм... я почему-то думал это событие не отработает, отработало, перенёс туда функцию, но такая же ботва, прям не пруха какая-то :(

static void
gtk_date_picker_size_allocate(GtkWidget *widget, GtkAllocation * allocation)
{
	GtkDatePicker * dp = GTK_DATE_PICKER(widget);
	GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);	
	set_inner_border_offset(GTK_ENTRY(widget), dp->x-1);
	
}
xterro ★★★★★
() автор топика
Ответ на: комментарий от xterro

Насколько я понял, бордюр GtkEntry зависит от размеров другого виджета. У меня всё работает, set_inner_border выставляется на основе измененных размеров кнопки. Единственная неприятность, после первого вызова, entry резко увеличивается.

#!/usr/bin/env python2
import gtk

def button_size_allocate(button, allocation, entry):
    if not button.window:
        return

    qwidth = button.window.get_size()[0] / 4
    border = gtk.Border(qwidth, qwidth, 0, 0)
    entry.set_inner_border(border)

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect('destroy', gtk.main_quit)

box = gtk.VBox()
window.add(box)

entry = gtk.Entry()
box.pack_start(entry)

button = gtk.Button('Push')
button.connect_after('size-allocate', button_size_allocate, entry)
box.pack_start(button)

window.show_all()

gtk.main()
baverman ★★★
()
Ответ на: комментарий от baverman

Вот в этом «увеличении» как раз вся загвоздка, почему entry начинает увеличиваться, и как этого избежать, это меня и интересует, смотрю по коду, ещё больше запутываюсьь, не могу понять, чё там за quark устанавливается, хрень какая-то...

xterro ★★★★★
() автор топика
Ответ на: комментарий от ckotinko

Мне нужно было отслеживать размер виджета, в моём случае «внутренний», у GtkEntry, т.е у самого entry высота постоянная, а ширина может меняться, когда например растягиваете его мышкой, и рисовать на нём, здесь вполне подходил expose-event, ибо больше это сделать негде.

xterro ★★★★★
() автор топика
Ответ на: комментарий от xterro

смотрю по коду, ещё больше запутываюсь

По коду там как раз всё понятно.

1) В очередь ставится ресайз контрола

2) В свойства запихивается новый бордюр

3) 1) приводит к size-request

4) width = MIN_ENTRY_WIDTH + xborder * 2 + inner_border.left + inner_border.right;

Теперь смекаешь, почему размер виджета увеличивается?

baverman ★★★
()
Ответ на: комментарий от xterro

Попробуй-ка вот здесь до inherited'а бордер выставить, а то родительский метод только отработает сей важный сигнал, как ты ему палку в колесо... Авось сработает.

static void
gtk_date_picker_size_allocate(GtkWidget *widget, GtkAllocation * allocation)
{
   GtkDatePicker * dp = GTK_DATE_PICKER(widget);
   set_inner_border_offset(GTK_ENTRY(widget), dp->x-1);
   GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
}
arturpub ★★
()
Ответ на: комментарий от arturpub

И в size-request'е тоже стоит прицепиться, только уже после того, как ентри свою нужду определит. Тогда может и ширина скакать не будет при первом разе.

static void
gtk_date_picker_size_request(GtkWidget *widget, GtkRequisition *req)
{
   GTK_WIDGET_CLASS(parent_class)->size_request(widget, req);
   req->width += your_expr_here;
}

(пишу по памяти без мануала, но идея думаю понятна)

arturpub ★★
()
Ответ на: комментарий от xterro

Подводный камень в том, что некоторые методы и проперти влекут gtk_widget_queue_resize() и/или gtk_widget_queue_draw(). Тут-то ты и попадаешь на «рекурсию» через отсроченные вызовы в майнлупе. см. g_idle_add() и др.

Механика ресайза в гтк поверхностно такова: сначала самый верхний контейнер (окно) вызывает у чилдов size-request, оно все рекурсивно доходит до конечных виджетов и теперь пакуется обратно, естественно класс-зависимо (упаковка типа HBox vs Table vs ...). Теперь минимальный размер сцены стал известен. Тут окно расширяется до нужных размеров, если требуется. После этого идет фаза size-allocate, тоже рекурсивная, в которой контейнеры выделяют место своим чилдам. После этой фазы все виджеты получили как минимум то, что указали при size-request, может быть за исключением случаев, когда злобный WM окну столько не дал (может и не быть, зависит от gtkwindow.c). Также при size-allocate каждый виджет вызвал у себя gtk_widget_queue_draw(), т.е. поставился в очередь на expose. На этом кончается текущий стек вызовов, вылетаем обратно в майнлуп.

Теперь трудится обработчик expose. Тоже рекурсивно.

При доупаковке в к-л контейнер, или при изменении размера виджета (в нашем случае причиной этого служит изменение бордера), виджет вызывает у себя gtk_widget_queue_resize(), что снова запускает пред-предыдущий абзац.

Более формально,

(GtkContainer)add/remove -> queue_resize() в очередь
queue_resize() -> size-request вниз и пакуется обратно
size-request -> configure (не всегда && только для окна)
configure -> size-allocate вниз
size-allocate -> queue_draw()
queue_draw() -> expose вниз
queue_draw() собирает все запросы в единый GdkRegion, чтобы отрисовалось за 1 цикл
Естественно поведение GtkWindow в корне отличается от поведения его чилдов, т.к. оно общается не с парентом, а с WM, а там все не как в гтк.

arturpub ★★
()
Ответ на: комментарий от arturpub

Да, забыл упомянуть, что queue_resize() пропагейтится рекурсивно вверх, до окна.

arturpub ★★
()
Ответ на: комментарий от baverman

Да, понял, я изначально не сообразил про эти кварки, мол чё за фигня, зачем нужна. Вообщем я решил проблемму, выдрав кусок кода из функции «gtk_entry_set_inner_border()» и всё заработало как надо :)

Теперь моя функция выглядит так:


void 

set_inner_border_offset(GtkWidget * widget, int offset)

{

	GtkBorder * border 	= (GtkBorder *)gtk_entry_get_inner_border(GTK_ENTRY(widget)); 
	if (!border) {
		border 		= g_new(GtkBorder, 1); 
		border->top 	= 2;
		border->bottom 	= 2;
		border->left 	= 2;
		border->right 	= offset;
	}

	/* Taked from original 'gtk_entry_set_inner_border()' function */

	if(border)
		g_object_set_qdata_full(G_OBJECT(widget),quark_inner_border,						gtk_border_copy(border),
	(GDestroyNotify)gtk_border_free);

	else
		g_object_set_qdata(G_OBJECT(widget), quark_inner_border, NULL);
}

Воткнул её в expose-event и торт :)

xterro ★★★★★
() автор топика
Ответ на: комментарий от arturpub

Спасибо за «разжёвывание» темы, некоторые моменты стали понятней. Я закомитил свои наброски этого контрола в репозитарий и дал ему public доступ, кому интересно могут поглядеть: https://bitbucket.org/xterro/date_picker скачать и использовать в своих поделках. Контрол ещё не до конца доделан, например нужно ещё события добавить, но там осталось по мелочи. Может кто ещё дельным советом поделится или «патч» подкинет )))

P.S. Позже, как пару моментов доделаю могу ещё открыть репу ещё одного контрола, который я делаю - gtk_button_entry - GtkEntry только с кнопками(можно использовать например для выбора записей из 'справочника') может кому пригодится.

xterro ★★★★★
() автор топика
Ответ на: комментарий от xterro

Хех, покажи мне гтк-шника, который не делал «ентри с кнопками» :) В чем, в чем, а в бизнес-ориентированном гуе эти ребята нихрена не парят. Хотя подвижки есть, даже доперли до оффскринок и разделения минимальных и нормальных размеров, правда только к выходу третьей версии. А про грид... Ну да ладно, и на том спасибо.

...

В последнем решении ты зашел туда, куда, как некоторые говорят не стоит заходить, так что имеет смысл проверить этот виджет в разных позах. Я бы в первую очередь посмотрел, как он себя ведет на слишком малой ширине по обеим осям, например в GtkPaned с shrink=TRUE, а также в GtkSizeGroup'е. Напоследок можно подергать за size-request окна, и потыркать queue_resize() у ентри и окна. У меня непрерывное ощущение, что тут можно попасть на BadValue или g_return_if_fail() какой-нибудь.

gboolean
disturb(__unused gpointer data)
{
   gtk_widget_size_request(window);
   gtk_widget_queue_resize(window);
   gtk_widget_queue_resize(entry);
   return TRUE; // 2 сек и еще разок
}

gtk_init(&argc, &argv);
g_timeout_add(2000, disturb, NULL);
gtk_main();

arturpub ★★
()
Ответ на: комментарий от arturpub

Ок, погоняю, хотя имхо он вести себя будет точно также как и entry, так как от него наследован, size-request и size-allocate тоже «берутся родительские».

static void 
gtk_date_picker_size_request(GtkWidget *widget, GtkRequisition * requisition)
{
   GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
}

static void
gtk_date_picker_size_allocate(GtkWidget *widget, GtkAllocation * allocation)
{
   GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);   
}
Что касается последнего решения, то нужно же было как-то из ситуации выходить, если стандартная функция работала в моём случае не так как надо :)

P.S. Табличный виджет у меня в планах, пока есть только наброски объекта-хранилища данных для виджета, в него уже можно добавлять и удалять строки, устанавливать некоторые данные в ячейку. И объект-каркас для вьюшки. Как с этими контролами закончу, займусь табличным, хочу чтобы было что-то на подобии DevExpress-овского грида. Им вдохновляюсь :)

xterro ★★★★★
() автор топика
Ответ на: комментарий от xterro

TextView еще не забудь с нормальным выравниванием, обтеканием картинок/эмбеддовки и автопереносом на страницы разных размеров с колонтитулами ;)

arturpub ★★
()
Ответ на: комментарий от arturpub

Не, TextView у меня в планах нет, пока только это, думаю и этого хватит, остальное всё есть :)

xterro ★★★★★
() автор топика
Ответ на: комментарий от xterro

Течет засчет gtk_border_copy, вообще лучше так:

{
    GtkBorder border;

    border.top    = 2;
    border.bottom = 2;
    border.left   = 2;
    border.right  = offset;

    g_object_set_qdata_full(G_OBJECT(widget), quark_inner_border,
        gtk_border_copy(&border), (GDestroyNotify) gtk_border_free);
}

Любой GObject тащит с собой GHashTable на случай, если кому-нибудь в рантайме приспичит что-то к нему прицепить. Кварки, они же Атомы — это однозначное соответствие между к-л последовательностью символов и int'ом. Оно быстрее хешируется, но живет только в рантайме, вот и все.

Мануал говорит, что надо рвать связи с другими GObject'ами в dispose, а для finalize оставлять только g_free(). Задумка в том, что dispose может вызываться несколько раз для обрыва циклов, а finalize только раз — перед разрушением GObject'а. Поэтому в dispose стоит обнулять разорванные связи, а также сохранять максимально работоспособное состояние на всякий случай.

зы: Хардтабы после ts=4 выглядят естественно буээ.

arturpub ★★
()
Ответ на: комментарий от xterro

Табличный виджет у меня в планах, пока есть только наброски объекта-хранилища данных для виджета

На си ты стрельнешься его делать. Правда, я реализовывал не совсем грид, а подобие акцесовского контрола для ввода данных — так даже на питоне хотелось на стенку лезть.

baverman ★★★
()
Ответ на: комментарий от arturpub

Точно, я и забыл про выделение памяти там под бордер, спасибо, поправлю. А что за хардтабы? Я привык ставить таб в 4, если меньше, то код не так выразительно смотрится, причём в редакторе смотрится нормально, а заливаю в репу, там всё это разъезжается. :)

xterro ★★★★★
() автор топика
Ответ на: комментарий от baverman

Ну, я никуда и не спешу, а язык, так это всего-лишь инструмент, хотя смотришь код например табличного контрола от gnumeric, там несколько тысяч строк, понимаешь, работы просто ооочень много, надеюсь у меня хватит терпения и выдержки :)

xterro ★★★★★
() автор топика
Ответ на: комментарий от baverman

Настоящий гтк-шник должен сделать 3 вещи: ентри с кнопками, нормальный грид, стрельнуться. Пока никто еще не стрельнулся...

arturpub ★★
()
Ответ на: комментарий от arturpub

Настоящий гтк-ашник должен поучаствовать в разработке GTK, съездить на GUADEC ну и ещё чёнить там замутить... :) А вот интересно, можно ли в природе встретить чей-нибудь, готовый ентри с кнопочками, поглазеть, полюбопытствовать? Или тот же грид? Пока смотрю на табличные виджеты из gnumeric и gtkextra, но больше поглядываю во второй :)

xterro ★★★★★
() автор топика
Ответ на: комментарий от xterro

Хардтаб, он же '\t'. Символ, который по умолчанию при выводе сдвигает текущую позицию курсора вправо, пока она не станет кратной восьми. Детально тут, или гугл «tabs vs spaces». Если не emacs и не vi, то проще всего на будущее отключить опцию use real(hard) tabs, а в текущих сорцах заменить хардтабы на 4 пробела — тогда везде будет выглядеть красиво.

GTK сорцы тоже этим страдают.

arturpub ★★
()
Ответ на: комментарий от arturpub

Настоящий гтк-шник должен сделать нормальный грид

Жалко, что критерии нормальности у всех разные и на выходе получается узкозаточенная под конкретные задачи хреновина. Потом проект забрасывается и канет в небытие. В итоге, годы идут, а в libgtk так и нет нормального контрола.

Что-то я разнылся — я же гткашник, ******.

baverman ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.