mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-24 06:28:54 +00:00
Implement handling of soft-invalidated line frags. Soft-invalidate layout information for glyphs after an edited range in NSLayoutManager.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@15997 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
32f6790d62
commit
0383d08f23
4 changed files with 368 additions and 142 deletions
|
@ -1,3 +1,10 @@
|
|||
2003-02-18 18:03 Alexander Malmberg <alexander@malmberg.org>
|
||||
|
||||
* Headers/gnustep/gui/GSLayoutManager_internal.h,
|
||||
Source/GSLayoutManager.m, Source/NSLayoutManager.m: Implement
|
||||
handling of soft-invalidated line frags. Soft-invalidate layout
|
||||
information for glyphs after an edited range in NSLayoutManager.
|
||||
|
||||
2003-02-18 01:23 Alexander Malmberg <alexander@malmberg.org>
|
||||
|
||||
* Headers/gnustep/gui/GSLayoutManager_internal.h,
|
||||
|
|
|
@ -184,8 +184,14 @@ typedef struct GSLayoutManager_textcontainer_s
|
|||
*/
|
||||
BOOL was_invalidated;
|
||||
|
||||
/*
|
||||
The array actually has num_soft+num_linefrags entries. Only the
|
||||
num_linefrags first are significant, the rest hold soft invalidated
|
||||
layout information.
|
||||
*/
|
||||
linefrag_t *linefrags;
|
||||
int num_linefrags;
|
||||
int num_soft;
|
||||
} textcontainer_t;
|
||||
|
||||
|
||||
|
|
|
@ -1455,7 +1455,7 @@ places where we switch.
|
|||
tc->started = tc->complete = NO;
|
||||
if (tc->linefrags)
|
||||
{
|
||||
for (j = 0, lf = tc->linefrags; j < tc->num_linefrags; j++, lf++)
|
||||
for (j = 0, lf = tc->linefrags; j < tc->num_linefrags + tc->num_soft; j++, lf++)
|
||||
{
|
||||
if (lf->points)
|
||||
free(lf->points);
|
||||
|
@ -1466,7 +1466,7 @@ places where we switch.
|
|||
free(tc->linefrags);
|
||||
}
|
||||
tc->linefrags = NULL;
|
||||
tc->num_linefrags = 0;
|
||||
tc->num_linefrags = tc->num_soft = 0;
|
||||
tc->pos = tc->length = 0;
|
||||
tc->was_invalidated = YES;
|
||||
}
|
||||
|
@ -1524,6 +1524,29 @@ places where we switch.
|
|||
break;
|
||||
}
|
||||
tc->complete = YES;
|
||||
if (tc->num_soft)
|
||||
{
|
||||
/*
|
||||
If there is any soft invalidated layout information left, remove
|
||||
it.
|
||||
*/
|
||||
int k;
|
||||
linefrag_t *lf;
|
||||
for (k = tc->num_linefrags, lf = tc->linefrags + k; k < tc->num_linefrags + tc->num_soft; k++, lf++)
|
||||
{
|
||||
if (lf->points)
|
||||
{
|
||||
free(lf->points);
|
||||
lf->points = NULL;
|
||||
}
|
||||
if (lf->attachments)
|
||||
{
|
||||
free(lf->attachments);
|
||||
lf->attachments = NULL;
|
||||
}
|
||||
}
|
||||
tc->num_soft = 0;
|
||||
}
|
||||
if (delegate_responds)
|
||||
{
|
||||
[_delegate layoutManager: self
|
||||
|
@ -1592,77 +1615,6 @@ by calling this incorrectly.
|
|||
actualCharacterRange: (NSRange *)actualRange
|
||||
{
|
||||
[self _invalidateLayoutFromContainer: 0];
|
||||
#if 0
|
||||
/* TODO: need to bring this up to date and fix it */
|
||||
unsigned int from_glyph = glyphs->glyph_length;
|
||||
int i, j;
|
||||
textcontainer_t *tc;
|
||||
linefrag_t *lf;
|
||||
|
||||
if (from_glyph)
|
||||
{
|
||||
unsigned int chi = [self characterIndexForGlyphAtIndex: from_glyph - 1];
|
||||
if (chi > aRange.location)
|
||||
{
|
||||
from_glyph = [self glyphRangeForCharacterRange: NSMakeRange(aRange.location, 1)
|
||||
actualCharacterRange: actualRange].location;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (actualRange)
|
||||
actualRange->location = chi;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (actualRange)
|
||||
actualRange->location = 0;
|
||||
}
|
||||
if (actualRange)
|
||||
actualRange->length = [_textStorage length] - actualRange->location;
|
||||
|
||||
if (layout_glyph <= from_glyph)
|
||||
return;
|
||||
|
||||
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
||||
if (tc->pos + tc->length > from_glyph)
|
||||
break;
|
||||
if (i == num_textcontainers)
|
||||
{
|
||||
NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
j = i;
|
||||
|
||||
for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
|
||||
if (lf->pos + lf->length > from_glyph)
|
||||
break;
|
||||
if (i == tc->num_linefrags)
|
||||
{
|
||||
NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
int idx = i;
|
||||
for (; i < tc->num_linefrags; i++, lf++)
|
||||
{
|
||||
if (lf->points)
|
||||
free(lf->points);
|
||||
if (lf->attachments)
|
||||
free(lf->attachments);
|
||||
}
|
||||
tc->num_linefrags = idx;
|
||||
tc->length = tc->linefrags[idx - 1].pos + tc->linefrags[idx - 1].length - tc->pos;
|
||||
tc->complete = NO;
|
||||
[self _invalidateLayoutFromContainer: j + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _invalidateLayoutFromContainer: j];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -1780,6 +1732,7 @@ by calling this incorrectly.
|
|||
return;
|
||||
}
|
||||
|
||||
/* Make sure the given glyph range matches earlier layout. */
|
||||
if (!tc->num_linefrags)
|
||||
{
|
||||
if (glyphRange.location != tc->pos)
|
||||
|
@ -1789,9 +1742,6 @@ by calling this incorrectly.
|
|||
__PRETTY_FUNCTION__];
|
||||
return;
|
||||
}
|
||||
tc->linefrags = malloc(sizeof(linefrag_t));
|
||||
tc->num_linefrags = 1;
|
||||
lf = tc->linefrags;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1803,10 +1753,66 @@ by calling this incorrectly.
|
|||
__PRETTY_FUNCTION__];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(tc->num_linefrags + tc->num_soft))
|
||||
{
|
||||
tc->linefrags = malloc(sizeof(linefrag_t));
|
||||
tc->num_linefrags = 1;
|
||||
lf = tc->linefrags;
|
||||
}
|
||||
else if (!tc->num_soft)
|
||||
{
|
||||
tc->num_linefrags++;
|
||||
tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * tc->num_linefrags);
|
||||
lf = &tc->linefrags[tc->num_linefrags - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = tc->num_linefrags, lf = tc->linefrags + i; i < tc->num_linefrags + tc->num_soft; i++, lf++)
|
||||
{
|
||||
if (lf->pos >= NSMaxRange(glyphRange))
|
||||
break;
|
||||
if (lf->points)
|
||||
{
|
||||
free(lf->points);
|
||||
lf->points = NULL;
|
||||
}
|
||||
if (lf->attachments)
|
||||
{
|
||||
free(lf->attachments);
|
||||
lf->attachments = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == tc->num_linefrags)
|
||||
{
|
||||
/*
|
||||
If we should keep all soft frags, we need to enlarge the array
|
||||
to fit the new line frag.
|
||||
*/
|
||||
tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * (tc->num_linefrags + tc->num_soft + 1));
|
||||
memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[tc->num_linefrags], tc->num_soft * sizeof(linefrag_t));
|
||||
}
|
||||
else if (i > tc->num_linefrags + 1)
|
||||
{
|
||||
tc->num_soft -= i - tc->num_linefrags;
|
||||
memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[i], tc->num_soft * sizeof(linefrag_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
If i == tc->num_linefrags + 1, we're lucky and everything already
|
||||
lines up, so no moving is necessary.
|
||||
*/
|
||||
tc->num_soft--;
|
||||
}
|
||||
|
||||
tc->num_linefrags++;
|
||||
lf = &tc->linefrags[tc->num_linefrags - 1];
|
||||
}
|
||||
|
||||
memset(lf, 0, sizeof(linefrag_t));
|
||||
lf->rect = fragmentRect;
|
||||
lf->used_rect = usedRect;
|
||||
|
|
|
@ -1732,6 +1732,45 @@ TODO: not really clear what these should do
|
|||
}
|
||||
|
||||
|
||||
-(void) _dumpLayout
|
||||
{
|
||||
int i, j, k;
|
||||
textcontainer_t *tc;
|
||||
linefrag_t *lf;
|
||||
linefrag_point_t *lp;
|
||||
linefrag_attachment_t *la;
|
||||
|
||||
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
||||
{
|
||||
printf("tc %2i, %5i+%5i\n",i,tc->pos,tc->length);
|
||||
printf(" lfs: (%3i)\n", tc->num_linefrags);
|
||||
for (j = 0, lf = tc->linefrags; j < tc->num_linefrags; j++, lf++)
|
||||
{
|
||||
printf(" %3i : %5i+%5i (%g %g)+(%g %g)\n",
|
||||
j,lf->pos,lf->length,
|
||||
lf->rect.origin.x,lf->rect.origin.y,
|
||||
lf->rect.size.width,lf->rect.size.height);
|
||||
for (k = 0, lp = lf->points; k < lf->num_points; k++, lp++)
|
||||
printf(" p%3i : %5i+%5i\n",k,lp->pos,lp->length);
|
||||
for (k = 0, la = lf->attachments; k < lf->num_attachments; k++, la++)
|
||||
printf(" a%3i : %5i+%5i\n",k,la->pos,la->length);
|
||||
}
|
||||
printf(" softs: (%3i)\n", tc->num_soft);
|
||||
for (; j < tc->num_linefrags + tc->num_soft; j++, lf++)
|
||||
{
|
||||
printf(" %3i : %5i+%5i (%g %g)+(%g %g)\n",
|
||||
j,lf->pos,lf->length,
|
||||
lf->rect.origin.x,lf->rect.origin.y,
|
||||
lf->rect.size.width,lf->rect.size.height);
|
||||
for (k = 0, lp = lf->points; k < lf->num_points; k++, lp++)
|
||||
printf(" p%3i : %5i+%5i\n",k,lp->pos,lp->length);
|
||||
for (k = 0, la = lf->attachments; k < lf->num_attachments; k++, la++)
|
||||
printf(" a%3i : %5i+%5i\n",k,la->pos,la->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
We completely override this method and use the extra information we have
|
||||
about layout to do smarter invalidation. The comments at the beginning of
|
||||
|
@ -1744,6 +1783,20 @@ this file describes this.
|
|||
invalidatedRange: (NSRange)invalidatedRange
|
||||
{
|
||||
NSRange r;
|
||||
unsigned int original_last_glyph, new_last_glyph;
|
||||
int glyph_delta;
|
||||
|
||||
/* printf("\n*** invalidating\n");
|
||||
[self _dumpLayout];*/
|
||||
|
||||
/*
|
||||
Using -glyphRangeForChara... here would be safer, but we must make
|
||||
absolutely sure that we don't cause any glyph generation until the
|
||||
invalidation is done.
|
||||
|
||||
TODO: make sure last_glyph is set as expected
|
||||
*/
|
||||
original_last_glyph = layout_glyph;
|
||||
|
||||
if (!(mask & NSTextStorageEditedCharacters))
|
||||
lengthChange = 0;
|
||||
|
@ -1752,12 +1805,29 @@ this file describes this.
|
|||
changeInLength: lengthChange
|
||||
actualCharacterRange: &r];
|
||||
|
||||
if (r.location <= layout_char)
|
||||
if (layout_char > r.location)
|
||||
{
|
||||
unsigned int glyph_index;
|
||||
layout_char += lengthChange;
|
||||
if (layout_char < r.location)
|
||||
layout_char = r.location;
|
||||
}
|
||||
|
||||
if (layout_char == [_textStorage length])
|
||||
new_last_glyph = [self numberOfGlyphs];
|
||||
else
|
||||
new_last_glyph = [self glyphRangeForCharacterRange: NSMakeRange(layout_char, 1)
|
||||
actualCharacterRange: NULL].location;
|
||||
|
||||
glyph_delta = new_last_glyph - original_last_glyph;
|
||||
/* printf("original=%i, new=%i, delta %i\n",
|
||||
original_last_glyph,new_last_glyph,glyph_delta);*/
|
||||
|
||||
if (r.location < layout_char)
|
||||
{
|
||||
unsigned int glyph_index, last_glyph;
|
||||
textcontainer_t *tc;
|
||||
linefrag_t *lf;
|
||||
int i, j;
|
||||
int i, j, k;
|
||||
int new_num;
|
||||
NSRange char_range;
|
||||
|
||||
|
@ -1784,75 +1854,212 @@ this file describes this.
|
|||
actualCharacterRange: &char_range].location;
|
||||
}
|
||||
|
||||
/* glyph_index is the first index we should invalidate for. */
|
||||
for (j = 0, tc = textcontainers; j < num_textcontainers; j++, tc++)
|
||||
if (tc->pos + tc->length >= glyph_index)
|
||||
/*
|
||||
For soft invalidation, we need to know where to stop hard-invalidating.
|
||||
This will cause immediate glyph generation to fill the gaps the
|
||||
invalidation caused.
|
||||
*/
|
||||
if (NSMaxRange(r) == [_textStorage length])
|
||||
{
|
||||
last_glyph = [self numberOfGlyphs];
|
||||
}
|
||||
else
|
||||
{
|
||||
last_glyph =
|
||||
[self glyphRangeForCharacterRange: NSMakeRange(NSMaxRange(r),1)
|
||||
actualCharacterRange: NULL].location;
|
||||
}
|
||||
last_glyph -= glyph_delta;
|
||||
|
||||
/* glyph_index is the first index we should invalidate for. */
|
||||
for (j = 0, tc = textcontainers; j < num_textcontainers; j++, tc++)
|
||||
if (tc->pos + tc->length >= glyph_index)
|
||||
break;
|
||||
|
||||
LINEFRAG_FOR_GLYPH(glyph_index);
|
||||
|
||||
/*
|
||||
We invalidate the entire line containing lf, and the entire
|
||||
previous line. Thus, we scan backwards to find the first line frag
|
||||
on the previous line.
|
||||
*/
|
||||
while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y)
|
||||
lf--, i--;
|
||||
/* Now we have the first line frag on this line. */
|
||||
if (i > 0)
|
||||
{
|
||||
lf--, i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
The previous line isn't in this text container, so we move
|
||||
to the previous text container.
|
||||
*/
|
||||
if (j > 0)
|
||||
{
|
||||
j--;
|
||||
tc--;
|
||||
i = tc->num_linefrags - 1;
|
||||
lf = tc->linefrags + i;
|
||||
}
|
||||
}
|
||||
/* Last line frag on previous line. */
|
||||
while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y)
|
||||
lf--, i--;
|
||||
/* First line frag on previous line. */
|
||||
|
||||
/* Invalidate all line frags that intersect the invalidated range. */
|
||||
new_num = i;
|
||||
while (1)
|
||||
{
|
||||
for (; i < tc->num_linefrags + tc->num_soft; i++, lf++)
|
||||
{
|
||||
/*
|
||||
Since we must invalidate whole lines, we can only stop if
|
||||
the line frag is beyond the invalidated range, and the line
|
||||
frag is the first line frag in a line.
|
||||
*/
|
||||
if (lf->pos >= last_glyph &&
|
||||
(!i || lf[-1].rect.origin.y != lf->rect.origin.y))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (lf->points)
|
||||
{
|
||||
free(lf->points);
|
||||
lf->points = NULL;
|
||||
}
|
||||
if (lf->attachments)
|
||||
{
|
||||
free(lf->attachments);
|
||||
lf->attachments = NULL;
|
||||
}
|
||||
}
|
||||
if (i < tc->num_linefrags + tc->num_soft)
|
||||
break;
|
||||
tc->num_linefrags = new_num;
|
||||
tc->num_soft = 0;
|
||||
tc->was_invalidated = YES;
|
||||
tc->complete = NO;
|
||||
if (new_num)
|
||||
{
|
||||
tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
tc->pos = tc->length = 0;
|
||||
tc->started = NO;
|
||||
}
|
||||
|
||||
j++, tc++;
|
||||
if (j == num_textcontainers)
|
||||
break;
|
||||
|
||||
LINEFRAG_FOR_GLYPH(glyph_index);
|
||||
new_num = 0;
|
||||
i = 0;
|
||||
lf = tc->linefrags;
|
||||
}
|
||||
|
||||
/*
|
||||
We invalidate the entire line containing lf, and the entire
|
||||
previous line. Thus, we scan backwards to find the first line frag
|
||||
on the previous line.
|
||||
*/
|
||||
while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y)
|
||||
lf--, i--;
|
||||
/* Now we have the first line frag on this line. */
|
||||
if (i > 0)
|
||||
{
|
||||
lf--, i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
The previous line isn't in this text container, so we move
|
||||
to the previous text container.
|
||||
*/
|
||||
if (j > 0)
|
||||
{
|
||||
j--;
|
||||
tc--;
|
||||
i = tc->num_linefrags - 1;
|
||||
lf = tc->linefrags + i;
|
||||
}
|
||||
}
|
||||
/* Last line frag on previous line. */
|
||||
while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y)
|
||||
lf--, i--;
|
||||
/* First line frag on previous line. */
|
||||
if (j == num_textcontainers)
|
||||
goto no_soft_invalidation;
|
||||
|
||||
new_num = i;
|
||||
for (; i < tc->num_linefrags; i++)
|
||||
{
|
||||
if (lf->points)
|
||||
{
|
||||
free(lf->points);
|
||||
lf->points = NULL;
|
||||
}
|
||||
if (lf->attachments)
|
||||
{
|
||||
free(lf->attachments);
|
||||
lf->attachments = NULL;
|
||||
}
|
||||
}
|
||||
tc->num_linefrags = new_num;
|
||||
tc->was_invalidated = YES;
|
||||
tc->complete = NO;
|
||||
if (new_num)
|
||||
{
|
||||
tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
tc->pos = tc->length = 0;
|
||||
tc->started = NO;
|
||||
}
|
||||
if (new_num != i)
|
||||
{
|
||||
/*
|
||||
There's a gap between the last valid line frag and the first
|
||||
soft line frag. Compact the linefrags.
|
||||
*/
|
||||
memmove(tc->linefrags + new_num, lf, sizeof(linefrag_t) * (tc->num_linefrags + tc->num_soft - i));
|
||||
tc->num_linefrags -= i - new_num;
|
||||
i = new_num;
|
||||
lf = tc->linefrags + i;
|
||||
}
|
||||
tc->num_soft += tc->num_linefrags - new_num;
|
||||
tc->num_linefrags = new_num;
|
||||
tc->was_invalidated = YES;
|
||||
tc->complete = NO;
|
||||
if (new_num)
|
||||
{
|
||||
tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
tc->pos = tc->length = 0;
|
||||
tc->started = NO;
|
||||
}
|
||||
|
||||
/* This works even if j+1>=num_textcontainers, and it sets
|
||||
layout_char and layout_glyph for us. */
|
||||
[self _invalidateLayoutFromContainer: j + 1];
|
||||
/*
|
||||
Soft invalidate all remaining layout. Update their glyph positions
|
||||
and set the soft-invalidate markers in the text containers.
|
||||
*/
|
||||
while (1)
|
||||
{
|
||||
for (; i < tc->num_linefrags + tc->num_soft; i++, lf++)
|
||||
{
|
||||
lf->pos += glyph_delta;
|
||||
for (k = 0; k < lf->num_points; k++)
|
||||
lf->points[k].pos += glyph_delta;
|
||||
for (k = 0; k < lf->num_attachments; k++)
|
||||
lf->attachments[k].pos += glyph_delta;
|
||||
}
|
||||
|
||||
j++, tc++;
|
||||
if (j == num_textcontainers)
|
||||
break;
|
||||
i = 0;
|
||||
lf = tc->linefrags;
|
||||
}
|
||||
|
||||
no_soft_invalidation:
|
||||
/* Set layout_glyph and layout_char. */
|
||||
for (i = num_textcontainers - 1, tc = textcontainers + i; i >= 0; i--, tc--)
|
||||
{
|
||||
if (tc->started)
|
||||
{
|
||||
layout_glyph = tc->pos + tc->length;
|
||||
if (layout_glyph == glyphs->glyph_length)
|
||||
layout_char = glyphs->char_length;
|
||||
else
|
||||
layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0)
|
||||
layout_glyph = layout_char = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i, j;
|
||||
linefrag_t *lf;
|
||||
textcontainer_t *tc;
|
||||
/*
|
||||
TODO: could handle this better, but it should be a rare case,
|
||||
handling it efficiently is tricky.
|
||||
|
||||
For now, we simply clear out all soft invalidation information.
|
||||
*/
|
||||
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
||||
{
|
||||
for (j = 0, lf = tc->linefrags + j; j < tc->num_soft; j++, lf++)
|
||||
{
|
||||
if (lf->points)
|
||||
{
|
||||
free(lf->points);
|
||||
lf->points = NULL;
|
||||
}
|
||||
if (lf->attachments)
|
||||
{
|
||||
free(lf->attachments);
|
||||
lf->attachments = NULL;
|
||||
}
|
||||
}
|
||||
tc->num_soft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* [self _dumpLayout];
|
||||
printf("*** done\n");*/
|
||||
|
||||
[self _didInvalidateLayout];
|
||||
|
||||
|
|
Loading…
Reference in a new issue